summaryrefslogtreecommitdiff
path: root/packages/frontend-embed
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2024-09-23 19:49:52 +0900
committerGitHub <noreply@github.com>2024-09-23 19:49:52 +0900
commit3f0aaaa41efe42776d70490ea213e3c8b194c152 (patch)
treeaaa2e340be67f32e933851cd6461aa944c06c3db /packages/frontend-embed
parentenhance(frontend): tweak control panel (diff)
downloadsharkey-3f0aaaa41efe42776d70490ea213e3c8b194c152.tar.gz
sharkey-3f0aaaa41efe42776d70490ea213e3c8b194c152.tar.bz2
sharkey-3f0aaaa41efe42776d70490ea213e3c8b194c152.zip
perf(embed): improve embed performance (#14613)
* wip * wip * wip * refactor * refactor --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Diffstat (limited to 'packages/frontend-embed')
-rw-r--r--packages/frontend-embed/src/boot.ts10
-rw-r--r--packages/frontend-embed/src/components/EmNoteDetailed.vue4
-rw-r--r--packages/frontend-embed/src/components/EmNotes.vue6
-rw-r--r--packages/frontend-embed/src/di.ts2
-rw-r--r--packages/frontend-embed/src/pages/clip.vue44
-rw-r--r--packages/frontend-embed/src/pages/note.vue33
-rw-r--r--packages/frontend-embed/src/pages/tag.vue7
-rw-r--r--packages/frontend-embed/src/pages/user-timeline.vue49
-rw-r--r--packages/frontend-embed/src/server-context.ts21
-rw-r--r--packages/frontend-embed/src/ui.vue16
10 files changed, 120 insertions, 72 deletions
diff --git a/packages/frontend-embed/src/boot.ts b/packages/frontend-embed/src/boot.ts
index fcea7d32ea..00c7944eb3 100644
--- a/packages/frontend-embed/src/boot.ts
+++ b/packages/frontend-embed/src/boot.ts
@@ -20,16 +20,19 @@ import { serverMetadata } from '@/server-metadata.js';
import { url } from '@@/js/config.js';
import { parseEmbedParams } from '@@/js/embed-page.js';
import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
+import { serverContext } from '@/server-context.js';
import type { Theme } from '@/theme.js';
console.log('Misskey Embed');
+//#region Embedパラメータの取得・パース
const params = new URLSearchParams(location.search);
const embedParams = parseEmbedParams(params);
-
if (_DEV_) console.log(embedParams);
+//#endregion
+//#region テーマ
function parseThemeOrNull(theme: string | null): Theme | null {
if (theme == null) return null;
try {
@@ -65,6 +68,7 @@ if (embedParams.colorMode === 'dark') {
}
});
}
+//#endregion
// サイズの制限
document.documentElement.style.maxWidth = '500px';
@@ -89,6 +93,10 @@ const app = createApp(
app.provide(DI.mediaProxy, new MediaProxy(serverMetadata, url));
+app.provide(DI.serverMetadata, serverMetadata);
+
+app.provide(DI.serverContext, serverContext);
+
app.provide(DI.embedParams, embedParams);
// https://github.com/misskey-dev/misskey/pull/8575#issuecomment-1114239210
diff --git a/packages/frontend-embed/src/components/EmNoteDetailed.vue b/packages/frontend-embed/src/components/EmNoteDetailed.vue
index 8169f500a9..a233011af7 100644
--- a/packages/frontend-embed/src/components/EmNoteDetailed.vue
+++ b/packages/frontend-embed/src/components/EmNoteDetailed.vue
@@ -142,8 +142,8 @@ import EmAcct from '@/components/EmAcct.vue';
import { userPage } from '@/utils.js';
import { notePage } from '@/utils.js';
import { i18n } from '@/i18n.js';
+import { DI } from '@/di.js';
import { shouldCollapsed } from '@@/js/collapsed.js';
-import { serverMetadata } from '@/server-metadata.js';
import { url } from '@@/js/config.js';
import EmMfm from '@/components/EmMfm.js';
@@ -151,6 +151,8 @@ const props = defineProps<{
note: Misskey.entities.Note;
}>();
+const serverMetadata = inject(DI.serverMetadata)!;
+
const inChannel = inject('inChannel', null);
const note = ref(props.note);
diff --git a/packages/frontend-embed/src/components/EmNotes.vue b/packages/frontend-embed/src/components/EmNotes.vue
index 6370f4aeae..3418d97f77 100644
--- a/packages/frontend-embed/src/components/EmNotes.vue
+++ b/packages/frontend-embed/src/components/EmNotes.vue
@@ -20,12 +20,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { shallowRef } from 'vue';
+import { useTemplateRef } from 'vue';
import EmNote from '@/components/EmNote.vue';
import EmPagination, { Paging } from '@/components/EmPagination.vue';
import { i18n } from '@/i18n.js';
-const props = withDefaults(defineProps<{
+withDefaults(defineProps<{
pagination: Paging;
noGap?: boolean;
disableAutoLoad?: boolean;
@@ -34,7 +34,7 @@ const props = withDefaults(defineProps<{
ad: true,
});
-const pagingComponent = shallowRef<InstanceType<typeof EmPagination>>();
+const pagingComponent = useTemplateRef('pagingComponent');
defineExpose({
pagingComponent,
diff --git a/packages/frontend-embed/src/di.ts b/packages/frontend-embed/src/di.ts
index 799bbed598..22f6276630 100644
--- a/packages/frontend-embed/src/di.ts
+++ b/packages/frontend-embed/src/di.ts
@@ -7,9 +7,11 @@ import type { InjectionKey } from 'vue';
import * as Misskey from 'misskey-js';
import { MediaProxy } from '@@/js/media-proxy.js';
import type { ParsedEmbedParams } from '@@/js/embed-page.js';
+import type { ServerContext } from '@/server-context.js';
export const DI = {
serverMetadata: Symbol() as InjectionKey<Misskey.entities.MetaDetailed>,
embedParams: Symbol() as InjectionKey<ParsedEmbedParams>,
+ serverContext: Symbol() as InjectionKey<ServerContext>,
mediaProxy: Symbol() as InjectionKey<MediaProxy>,
};
diff --git a/packages/frontend-embed/src/pages/clip.vue b/packages/frontend-embed/src/pages/clip.vue
index 957d425d93..2528dc4b80 100644
--- a/packages/frontend-embed/src/pages/clip.vue
+++ b/packages/frontend-embed/src/pages/clip.vue
@@ -5,8 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
- <EmLoading v-if="loading"/>
- <EmTimelineContainer v-else-if="clip" :showHeader="embedParams.header">
+ <EmTimelineContainer v-if="clip" :showHeader="embedParams.header">
<template #header>
<div :class="$style.clipHeader">
<div :class="$style.headerClipIconRoot">
@@ -39,20 +38,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script setup lang="ts">
-import { ref, computed, shallowRef, inject } from 'vue';
+import { ref, computed, inject, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { scrollToTop } from '@@/js/scroll.js';
+import { url, instanceName } from '@@/js/config.js';
+import { isLink } from '@@/js/is-link.js';
+import { defaultEmbedParams } from '@@/js/embed-page.js';
import type { Paging } from '@/components/EmPagination.vue';
-import EmLoading from '@/components/EmLoading.vue';
import EmNotes from '@/components/EmNotes.vue';
import XNotFound from '@/pages/not-found.vue';
import EmTimelineContainer from '@/components/EmTimelineContainer.vue';
import { misskeyApi } from '@/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { serverMetadata } from '@/server-metadata.js';
-import { url, instanceName } from '@@/js/config.js';
-import { isLink } from '@@/js/is-link.js';
-import { defaultEmbedParams } from '@@/js/embed-page.js';
+import { assertServerContext } from '@/server-context.js';
import { DI } from '@/di.js';
const props = defineProps<{
@@ -61,16 +59,30 @@ const props = defineProps<{
const embedParams = inject(DI.embedParams, defaultEmbedParams);
-const clip = ref<Misskey.entities.Clip | null>(null);
+const serverMetadata = inject(DI.serverMetadata)!;
+
+const serverContext = inject(DI.serverContext)!;
+
+const clip = ref<Misskey.entities.Clip | null>();
+
+if (assertServerContext(serverContext, 'clip')) {
+ clip.value = serverContext.clip;
+} else {
+ clip.value = await misskeyApi('clips/show', {
+ clipId: props.clipId,
+ }).catch(() => {
+ return null;
+ });
+}
+
const pagination = computed(() => ({
endpoint: 'clips/notes',
params: {
clipId: props.clipId,
},
} as Paging));
-const loading = ref(true);
-const notesEl = shallowRef<InstanceType<typeof EmNotes> | null>(null);
+const notesEl = useTemplateRef('notesEl');
function top(ev: MouseEvent) {
const target = ev.target as HTMLElement | null;
@@ -80,16 +92,6 @@ function top(ev: MouseEvent) {
scrollToTop(notesEl.value.$el as HTMLElement, { behavior: 'smooth' });
}
}
-
-misskeyApi('clips/show', {
- clipId: props.clipId,
-}).then(res => {
- clip.value = res;
- loading.value = false;
-}).catch(err => {
- console.error(err);
- loading.value = false;
-});
</script>
<style lang="scss" module>
diff --git a/packages/frontend-embed/src/pages/note.vue b/packages/frontend-embed/src/pages/note.vue
index 86aebe072a..918583ecc7 100644
--- a/packages/frontend-embed/src/pages/note.vue
+++ b/packages/frontend-embed/src/pages/note.vue
@@ -5,40 +5,37 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.noteEmbedRoot">
- <EmLoading v-if="loading"/>
- <EmNoteDetailed v-else-if="note" :note="note"/>
+ <EmNoteDetailed v-if="note" :note="note"/>
<XNotFound v-else/>
</div>
</template>
<script setup lang="ts">
-import { ref } from 'vue';
+import { inject, ref } from 'vue';
import * as Misskey from 'misskey-js';
import EmNoteDetailed from '@/components/EmNoteDetailed.vue';
-import EmLoading from '@/components/EmLoading.vue';
import XNotFound from '@/pages/not-found.vue';
+import { DI } from '@/di.js';
import { misskeyApi } from '@/misskey-api.js';
+import { assertServerContext } from '@/server-context';
const props = defineProps<{
noteId: string;
}>();
+const serverContext = inject(DI.serverContext)!;
+
const note = ref<Misskey.entities.Note | null>(null);
-const loading = ref(true);
-// TODO: クライアント側でAPIを叩くのは二度手間なので予めHTMLに埋め込んでおく
-misskeyApi('notes/show', {
- noteId: props.noteId,
-}).then(res => {
- // リモートのノートは埋め込ませない
- if (res.url == null && res.uri == null) {
- note.value = res;
- }
- loading.value = false;
-}).catch(err => {
- console.error(err);
- loading.value = false;
-});
+if (assertServerContext(serverContext, 'note')) {
+ note.value = serverContext.note;
+} else {
+ note.value = await misskeyApi('notes/show', {
+ noteId: props.noteId,
+ }).catch(() => {
+ return null;
+ });
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend-embed/src/pages/tag.vue b/packages/frontend-embed/src/pages/tag.vue
index d9759a47e7..b481b3ebe5 100644
--- a/packages/frontend-embed/src/pages/tag.vue
+++ b/packages/frontend-embed/src/pages/tag.vue
@@ -38,14 +38,13 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script setup lang="ts">
-import { computed, shallowRef, inject } from 'vue';
+import { computed, inject, useTemplateRef } from 'vue';
import { scrollToTop } from '@@/js/scroll.js';
import type { Paging } from '@/components/EmPagination.vue';
import EmNotes from '@/components/EmNotes.vue';
import XNotFound from '@/pages/not-found.vue';
import EmTimelineContainer from '@/components/EmTimelineContainer.vue';
import { i18n } from '@/i18n.js';
-import { serverMetadata } from '@/server-metadata.js';
import { url, instanceName } from '@@/js/config.js';
import { isLink } from '@@/js/is-link.js';
import { DI } from '@/di.js';
@@ -55,6 +54,8 @@ const props = defineProps<{
tag: string;
}>();
+const serverMetadata = inject(DI.serverMetadata)!;
+
const embedParams = inject(DI.embedParams, defaultEmbedParams);
const pagination = computed(() => ({
@@ -64,7 +65,7 @@ const pagination = computed(() => ({
},
} as Paging));
-const notesEl = shallowRef<InstanceType<typeof EmNotes> | null>(null);
+const notesEl = useTemplateRef('notesEl');
function top(ev: MouseEvent) {
const target = ev.target as HTMLElement | null;
diff --git a/packages/frontend-embed/src/pages/user-timeline.vue b/packages/frontend-embed/src/pages/user-timeline.vue
index 8f587d2604..2d5dbb687b 100644
--- a/packages/frontend-embed/src/pages/user-timeline.vue
+++ b/packages/frontend-embed/src/pages/user-timeline.vue
@@ -5,8 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div>
- <EmLoading v-if="loading"/>
- <EmTimelineContainer v-else-if="user" :showHeader="embedParams.header">
+ <EmTimelineContainer v-if="user && !prohibited" :showHeader="embedParams.header">
<template #header>
<div :class="$style.userHeader">
<a :href="`/@${user.username}`" target="_blank" rel="noopener noreferrer" :class="$style.avatarLink">
@@ -46,21 +45,20 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script setup lang="ts">
-import { ref, computed, shallowRef, inject } from 'vue';
+import { ref, computed, inject, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
+import { url, instanceName } from '@@/js/config.js';
+import { defaultEmbedParams } from '@@/js/embed-page.js';
import type { Paging } from '@/components/EmPagination.vue';
import EmNotes from '@/components/EmNotes.vue';
import EmAvatar from '@/components/EmAvatar.vue';
-import EmLoading from '@/components/EmLoading.vue';
import EmUserName from '@/components/EmUserName.vue';
import I18n from '@/components/I18n.vue';
import XNotFound from '@/pages/not-found.vue';
import EmTimelineContainer from '@/components/EmTimelineContainer.vue';
import { misskeyApi } from '@/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { serverMetadata } from '@/server-metadata.js';
-import { url, instanceName } from '@@/js/config.js';
-import { defaultEmbedParams } from '@@/js/embed-page.js';
+import { assertServerContext } from '@/server-context.js';
import { DI } from '@/di.js';
const props = defineProps<{
@@ -69,26 +67,37 @@ const props = defineProps<{
const embedParams = inject(DI.embedParams, defaultEmbedParams);
-const user = ref<Misskey.entities.UserLite | null>(null);
+const serverMetadata = inject(DI.serverMetadata)!;
+
+const serverContext = inject(DI.serverContext)!;
+
+const user = ref<Misskey.entities.UserLite | null>();
+
+const prohibited = ref(false);
+
+if (assertServerContext(serverContext, 'user')) {
+ user.value = serverContext.user;
+} else {
+ user.value = await misskeyApi('users/show', {
+ userId: props.userId,
+ }).catch(() => {
+ return null;
+ });
+}
+
+if (user.value?.host != null) {
+ // リモートサーバーのユーザーは弾く
+ prohibited.value = true;
+}
+
const pagination = computed(() => ({
endpoint: 'users/notes',
params: {
userId: user.value?.id,
},
} as Paging));
-const loading = ref(true);
-
-const notesEl = shallowRef<InstanceType<typeof EmNotes> | null>(null);
-misskeyApi('users/show', {
- userId: props.userId,
-}).then(res => {
- user.value = res;
- loading.value = false;
-}).catch(err => {
- console.error(err);
- loading.value = false;
-});
+const notesEl = useTemplateRef('notesEl');
</script>
<style lang="scss" module>
diff --git a/packages/frontend-embed/src/server-context.ts b/packages/frontend-embed/src/server-context.ts
new file mode 100644
index 0000000000..a84a1a726a
--- /dev/null
+++ b/packages/frontend-embed/src/server-context.ts
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+import * as Misskey from 'misskey-js';
+
+const providedContextEl = document.getElementById('misskey_embedCtx');
+
+export type ServerContext = {
+ clip?: Misskey.entities.Clip;
+ note?: Misskey.entities.Note;
+ user?: Misskey.entities.UserLite;
+} | null;
+
+// NOTE: devモードのときしか embedCtx が null になることは無い
+export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null;
+
+export function assertServerContext<K extends keyof NonNullable<ServerContext>>(ctx: ServerContext, entity: K): ctx is Required<Pick<NonNullable<ServerContext>, K>> {
+ if (ctx == null) return false;
+ return entity in ctx;
+}
diff --git a/packages/frontend-embed/src/ui.vue b/packages/frontend-embed/src/ui.vue
index 35d9946b12..f426778898 100644
--- a/packages/frontend-embed/src/ui.vue
+++ b/packages/frontend-embed/src/ui.vue
@@ -18,11 +18,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<div
:class="$style.routerViewContainer"
>
- <EmNotePage v-if="page === 'notes'" :noteId="contentId"/>
- <EmUserTimelinePage v-else-if="page === 'user-timeline'" :userId="contentId"/>
- <EmClipPage v-else-if="page === 'clips'" :clipId="contentId"/>
- <EmTagPage v-else-if="page === 'tags'" :tag="contentId"/>
- <XNotFound v-else/>
+ <Suspense :timeout="0">
+ <EmNotePage v-if="page === 'notes'" :noteId="contentId"/>
+ <EmUserTimelinePage v-else-if="page === 'user-timeline'" :userId="contentId"/>
+ <EmClipPage v-else-if="page === 'clips'" :clipId="contentId"/>
+ <EmTagPage v-else-if="page === 'tags'" :tag="contentId"/>
+ <XNotFound v-else/>
+ <template #fallback>
+ <EmLoading/>
+ </template>
+ </Suspense>
</div>
</div>
</template>
@@ -37,6 +42,7 @@ import EmUserTimelinePage from '@/pages/user-timeline.vue';
import EmClipPage from '@/pages/clip.vue';
import EmTagPage from '@/pages/tag.vue';
import XNotFound from '@/pages/not-found.vue';
+import EmLoading from '@/components/EmLoading.vue';
const page = location.pathname.split('/')[2];
const contentId = location.pathname.split('/')[3];