diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 5ebec4ffd0..1b8873214b 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -559,7 +559,7 @@ export class ClientServerService { } }); - //#region SSR (for crawlers) + //#region SSR // User fastify.get<{ Params: { user: string; sub?: string; } }>('/@:user/:sub?', async (request, reply) => { const { username, host } = Acct.parse(request.params.user); @@ -584,11 +584,17 @@ export class ClientServerService { reply.header('X-Robots-Tag', 'noimageai'); reply.header('X-Robots-Tag', 'noai'); } + + const _user = await this.userEntityService.pack(user); + return await reply.view('user', { user, profile, me, avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user), sub: request.params.sub, ...await this.generateCommonPugData(this.meta), + clientCtx: htmlSafeJsonStringify({ + user: _user, + }), }); } else { // リモートユーザーなので @@ -641,6 +647,9 @@ export class ClientServerService { // TODO: Let locale changeable by instance setting summary: getNoteSummary(_note), ...await this.generateCommonPugData(this.meta), + clientCtx: htmlSafeJsonStringify({ + note: _note, + }), }); } else { return await renderBase(reply); @@ -729,6 +738,9 @@ export class ClientServerService { profile, avatarUrl: _clip.user.avatarUrl, ...await this.generateCommonPugData(this.meta), + clientCtx: htmlSafeJsonStringify({ + clip: _clip, + }), }); } else { return await renderBase(reply); diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 280a5923c2..3883b5e5ab 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -74,6 +74,9 @@ html script(type='application/json' id='misskey_meta' data-generated-at=now) != metaJson + script(type='application/json' id='misskey_clientCtx' data-generated-at=now) + != clientCtx + script include ../boot.js diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue index 7b1737fece..4df093d423 100644 --- a/packages/frontend/src/pages/clip.vue +++ b/packages/frontend/src/pages/clip.vue @@ -46,12 +46,13 @@ import { isSupportShare } from '@/scripts/navigator.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js'; import type { MenuItem } from '@/types/menu.js'; +import { getServerContext } from '@/server-context.js'; const props = defineProps<{ clipId: string, }>(); -const clip = ref(null); +const clip = ref(getServerContext('clip')); const favorited = ref(false); const pagination = { endpoint: 'clips/notes' as const, @@ -64,6 +65,11 @@ const pagination = { const isOwned = computed(() => $i && clip.value && ($i.id === clip.value.userId)); watch(() => props.clipId, async () => { + if (getServerContext('clip') && getServerContext('clip').id === props.clipId) { + clip.value = getServerContext('clip'); + return; + } + clip.value = await misskeyApi('clips/show', { clipId: props.clipId, }); diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue index 454ee3c6bc..920a6cee21 100644 --- a/packages/frontend/src/pages/note.vue +++ b/packages/frontend/src/pages/note.vue @@ -62,13 +62,14 @@ import { dateString } from '@/filters/date.js'; import MkClipPreview from '@/components/MkClipPreview.vue'; import { defaultStore } from '@/store.js'; import { pleaseLogin } from '@/scripts/please-login.js'; +import { getServerContext } from '@/server-context.js'; const props = defineProps<{ noteId: string; initialTab?: string; }>(); -const note = ref(); +const note = ref(getServerContext('note')); const clips = ref(); const showPrev = ref<'user' | 'channel' | false>(false); const showNext = ref<'user' | 'channel' | false>(false); @@ -116,6 +117,12 @@ function fetchNote() { showPrev.value = false; showNext.value = false; note.value = null; + + if (getServerContext('note') && getServerContext('note').id === props.noteId) { + note.value = getServerContext('note'); + return; + } + misskeyApi('notes/show', { noteId: props.noteId, }).then(res => { diff --git a/packages/frontend/src/server-context.ts b/packages/frontend/src/server-context.ts new file mode 100644 index 0000000000..aa44a10290 --- /dev/null +++ b/packages/frontend/src/server-context.ts @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import * as Misskey from 'misskey-js'; +import { $i } from '@/account.js'; + +const providedContextEl = document.getElementById('misskey_clientCtx'); + +export type ServerContext = { + clip?: Misskey.entities.Clip; + note?: Misskey.entities.Note; + user?: Misskey.entities.UserLite; +} | null; + +export const serverContext: ServerContext = (providedContextEl && providedContextEl.textContent) ? JSON.parse(providedContextEl.textContent) : null; + +export function getServerContext>(entity: K): Required, K>> | null { + // contextは非ログイン状態の情報しかないためログイン時は利用できない + if ($i) return null; + + return serverContext ? (serverContext[entity] ?? null) : null; +}