Skip to content

Commit

Permalink
perf(embed): improve embed performance (#14613)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* refactor

* refactor

---------

Co-authored-by: かっこかり <[email protected]>
  • Loading branch information
syuilo and kakkokari-gtyih authored Sep 23, 2024
1 parent 2aebdb8 commit 3f0aaaa
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 73 deletions.
66 changes: 66 additions & 0 deletions packages/backend/src/server/web/ClientServerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,72 @@ export class ClientServerService {
//#endregion

//#region embed pages
fastify.get<{ Params: { user: string; } }>('/embed/user-timeline/:user', async (request, reply) => {
reply.removeHeader('X-Frame-Options');

const user = await this.usersRepository.findOneBy({
id: request.params.user,
});

if (user == null) return;
if (user.host != null) return;

const _user = await this.userEntityService.pack(user);

reply.header('Cache-Control', 'public, max-age=3600');
return await reply.view('base-embed', {
title: this.meta.name ?? 'Misskey',
...await this.generateCommonPugData(this.meta),
embedCtx: htmlSafeJsonStringify({
user: _user,
}),
});
});

fastify.get<{ Params: { note: string; } }>('/embed/notes/:note', async (request, reply) => {
reply.removeHeader('X-Frame-Options');

const note = await this.notesRepository.findOneBy({
id: request.params.note,
});

if (note == null) return;
if (note.visibility !== 'public') return;
if (note.userHost != null) return;

const _note = await this.noteEntityService.pack(note, null, { detail: true });

reply.header('Cache-Control', 'public, max-age=3600');
return await reply.view('base-embed', {
title: this.meta.name ?? 'Misskey',
...await this.generateCommonPugData(this.meta),
embedCtx: htmlSafeJsonStringify({
note: _note,
}),
});
});

fastify.get<{ Params: { clip: string; } }>('/embed/clips/:clip', async (request, reply) => {
reply.removeHeader('X-Frame-Options');

const clip = await this.clipsRepository.findOneBy({
id: request.params.clip,
});

if (clip == null) return;

const _clip = await this.clipEntityService.pack(clip);

reply.header('Cache-Control', 'public, max-age=3600');
return await reply.view('base-embed', {
title: this.meta.name ?? 'Misskey',
...await this.generateCommonPugData(this.meta),
embedCtx: htmlSafeJsonStringify({
clip: _clip,
}),
});
});

fastify.get('/embed/*', async (request, reply) => {
reply.removeHeader('X-Frame-Options');

Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/server/web/views/base-embed.pug
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ html(class='embed')
script(type='application/json' id='misskey_meta' data-generated-at=now)
!= metaJson

script(type='application/json' id='misskey_embedCtx' data-generated-at=now)
!= embedCtx

script
include ../boot.embed.js

Expand Down
10 changes: 9 additions & 1 deletion packages/frontend-embed/src/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -65,6 +68,7 @@ if (embedParams.colorMode === 'dark') {
}
});
}
//#endregion

// サイズの制限
document.documentElement.style.maxWidth = '500px';
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion packages/frontend-embed/src/components/EmNoteDetailed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,17 @@ 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';
const props = defineProps<{
note: Misskey.entities.Note;
}>();
const serverMetadata = inject(DI.serverMetadata)!;
const inChannel = inject('inChannel', null);
const note = ref(props.note);
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend-embed/src/components/EmNotes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,7 +34,7 @@ const props = withDefaults(defineProps<{
ad: true,
});
const pagingComponent = shallowRef<InstanceType<typeof EmPagination>>();
const pagingComponent = useTemplateRef('pagingComponent');
defineExpose({
pagingComponent,
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend-embed/src/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
};
44 changes: 23 additions & 21 deletions packages/frontend-embed/src/pages/clip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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<{
Expand All @@ -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;
Expand All @@ -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>
Expand Down
33 changes: 15 additions & 18 deletions packages/frontend-embed/src/pages/note.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
7 changes: 4 additions & 3 deletions packages/frontend-embed/src/pages/tag.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -55,6 +54,8 @@ const props = defineProps<{
tag: string;
}>();
const serverMetadata = inject(DI.serverMetadata)!;
const embedParams = inject(DI.embedParams, defaultEmbedParams);
const pagination = computed(() => ({
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 3f0aaaa

Please sign in to comment.