diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2024-09-23 19:49:52 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-23 19:49:52 +0900 |
| commit | 3f0aaaa41efe42776d70490ea213e3c8b194c152 (patch) | |
| tree | aaa2e340be67f32e933851cd6461aa944c06c3db /packages/frontend-embed | |
| parent | enhance(frontend): tweak control panel (diff) | |
| download | sharkey-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.ts | 10 | ||||
| -rw-r--r-- | packages/frontend-embed/src/components/EmNoteDetailed.vue | 4 | ||||
| -rw-r--r-- | packages/frontend-embed/src/components/EmNotes.vue | 6 | ||||
| -rw-r--r-- | packages/frontend-embed/src/di.ts | 2 | ||||
| -rw-r--r-- | packages/frontend-embed/src/pages/clip.vue | 44 | ||||
| -rw-r--r-- | packages/frontend-embed/src/pages/note.vue | 33 | ||||
| -rw-r--r-- | packages/frontend-embed/src/pages/tag.vue | 7 | ||||
| -rw-r--r-- | packages/frontend-embed/src/pages/user-timeline.vue | 49 | ||||
| -rw-r--r-- | packages/frontend-embed/src/server-context.ts | 21 | ||||
| -rw-r--r-- | packages/frontend-embed/src/ui.vue | 16 |
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]; |