From e1a541d60bee47b3893db1b191386d263ebef464 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Sun, 2 Jun 2024 00:03:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=8E=E3=83=BC=E3=83=88=E3=83=BB?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6TL=E5=9F=8B=E3=82=81=E8=BE=BC?= =?UTF-8?q?=E3=81=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/web/ClientServerService.ts | 4 +- packages/backend/src/server/web/style.css | 13 ++ .../backend/src/server/web/views/base.pug | 2 +- packages/frontend/src/_boot_.ts | 30 +++-- packages/frontend/src/boot/common.ts | 2 +- packages/frontend/src/boot/sub-boot.ts | 4 +- packages/frontend/src/pages/embed/index.vue | 9 -- packages/frontend/src/pages/embed/note.vue | 33 +++++ .../src/pages/embed/user-timeline.vue | 57 +++++++++ packages/frontend/src/router/definition.ts | 11 +- packages/frontend/src/scripts/post-message.ts | 18 ++- packages/frontend/src/style.scss | 7 ++ packages/frontend/src/ui/embed.vue | 113 ++++++++++++++++++ packages/frontend/src/ui/minimum.vue | 58 +-------- 14 files changed, 277 insertions(+), 84 deletions(-) delete mode 100644 packages/frontend/src/pages/embed/index.vue create mode 100644 packages/frontend/src/pages/embed/note.vue create mode 100644 packages/frontend/src/pages/embed/user-timeline.vue create mode 100644 packages/frontend/src/ui/embed.vue diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 8554e98aec..f42cefe986 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -764,9 +764,9 @@ export class ClientServerService { //#endregion //#region embed pages - fastify.get('/embed/:path(.*)', async (request, reply) => { + fastify.get('/embed/*', async (request, reply) => { reply.removeHeader('X-Frame-Options'); - return await renderBase(reply, { noindex: true }); + return await renderBase(reply, { noindex: true, embed: true }); }); fastify.get('/_info_card_', async (request, reply) => { diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index e4723c24fd..70a6da694e 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -9,6 +9,12 @@ html { color: var(--fg); } +html.embed { + box-sizing: border-box; + background-color: transparent; + max-width: 500px; +} + #splash { position: fixed; z-index: 10000; @@ -22,6 +28,13 @@ html { transition: opacity 0.5s ease; } +html.embed #splash { + box-sizing: border-box; + min-height: 300px; + border-radius: var(--radius, 12px); + border: 1px solid var(--divider); +} + #splashIcon { position: absolute; top: 0; diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index ec1325e4e9..897481d369 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -77,7 +77,7 @@ html script include ../boot.js - body + body(class=embed && 'embed') noscript: p | JavaScriptを有効にしてください br diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts index d44b0553f3..3d2647289d 100644 --- a/packages/frontend/src/_boot_.ts +++ b/packages/frontend/src/_boot_.ts @@ -7,21 +7,33 @@ import 'vite/modulepreload-polyfill'; import '@/style.scss'; +import type { CommonBootOptions } from '@/boot/common.js'; import { mainBoot } from '@/boot/main-boot.js'; import { subBoot } from '@/boot/sub-boot.js'; import { isEmbedPage } from '@/scripts/embed-page.js'; +import { setIframeId, postMessageToParentWindow } from '@/scripts/post-message.js'; -const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete', '/embed']; +const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete']; -if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) { - if (isEmbedPage()) { - const params = new URLSearchParams(location.search); - const color = params.get('color'); - if (color && ['light', 'dark'].includes(color)) { - subBoot({ forceColorMode: color as 'light' | 'dark' }); - } +if (isEmbedPage()) { + const bootOptions: Partial = {}; + + const params = new URLSearchParams(location.search); + const color = params.get('color'); + if (color && ['light', 'dark'].includes(color)) { + bootOptions.forceColorMode = color as 'light' | 'dark'; } - + + window.addEventListener('message', event => { + if (event.data?.type === 'misskey:embedParent:registerIframeId' && event.data.payload?.iframeId != null) { + setIframeId(event.data.payload.iframeId); + } + }); + + subBoot(bootOptions, true).then(() => { + postMessageToParentWindow('misskey:embed:ready'); + }); +} else if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) { subBoot(); } else { mainBoot(); diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index adf3125143..619d451192 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -25,7 +25,7 @@ import { fetchCustomEmojis } from '@/custom-emojis.js'; import { setupRouter } from '@/router/definition.js'; export type CommonBootOptions = { - forceColorMode?: 'dark' | 'light' | 'auto'; + forceColorMode: 'dark' | 'light' | 'auto'; }; const defaultCommonBootOptions: CommonBootOptions = { diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts index 656b1bf196..ea6dce2d4d 100644 --- a/packages/frontend/src/boot/sub-boot.ts +++ b/packages/frontend/src/boot/sub-boot.ts @@ -7,8 +7,8 @@ import { createApp, defineAsyncComponent } from 'vue'; import { common } from './common.js'; import type { CommonBootOptions } from './common.js'; -export async function subBoot(options?: CommonBootOptions) { +export async function subBoot(options?: Partial, isEmbedPage?: boolean) { const { isClientUpdated } = await common(() => createApp( - defineAsyncComponent(() => import('@/ui/minimum.vue')), + defineAsyncComponent(() => isEmbedPage ? import('@/ui/embed.vue') : import('@/ui/minimum.vue')), ), options); } diff --git a/packages/frontend/src/pages/embed/index.vue b/packages/frontend/src/pages/embed/index.vue deleted file mode 100644 index 71b4bfaea0..0000000000 --- a/packages/frontend/src/pages/embed/index.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/packages/frontend/src/pages/embed/note.vue b/packages/frontend/src/pages/embed/note.vue new file mode 100644 index 0000000000..6de8fda977 --- /dev/null +++ b/packages/frontend/src/pages/embed/note.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/packages/frontend/src/pages/embed/user-timeline.vue b/packages/frontend/src/pages/embed/user-timeline.vue new file mode 100644 index 0000000000..e2d7db447e --- /dev/null +++ b/packages/frontend/src/pages/embed/user-timeline.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index 66a38b45ee..09d80fcf40 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -556,9 +556,14 @@ const routes: RouteDef[] = [{ component: page(() => import('@/pages/reversi/game.vue')), loginRequired: false, }, { - path: '/embed', - component: page(() => import('@/pages/embed/index.vue')), -// children: [], + path: '/embed/notes/:noteId', + component: page(() => import('@/pages/embed/note.vue')), +}, { + path: '/embed/user-timeline/@:username', + component: page(() => import('@/pages/embed/user-timeline.vue')), + query: { + header: 'showHeader', + } }, { path: '/timeline', component: page(() => import('@/pages/timeline.vue')), diff --git a/packages/frontend/src/scripts/post-message.ts b/packages/frontend/src/scripts/post-message.ts index 6084ac3e5f..6f63d3c7da 100644 --- a/packages/frontend/src/scripts/post-message.ts +++ b/packages/frontend/src/scripts/post-message.ts @@ -5,6 +5,7 @@ export const postMessageEventTypes = [ 'misskey:shareForm:shareCompleted', + 'misskey:embed:ready', 'misskey:embed:changeHeight', ] as const; @@ -12,16 +13,29 @@ export type PostMessageEventType = typeof postMessageEventTypes[number]; export type MiPostMessageEvent = { type: PostMessageEventType; + iframeId?: string; payload?: any; }; +let defaultIframeId: string | null = null; + +export function setIframeId(id: string): void { + if (_DEV_) console.log('setIframeId', id); + defaultIframeId = id; +} + /** * 親フレームにイベントを送信 */ -export function postMessageToParentWindow(type: PostMessageEventType, payload?: any): void { - if (_DEV_) console.log('postMessageToParentWindow', type, payload); +export function postMessageToParentWindow(type: PostMessageEventType, payload?: any, iframeId: string | null = null): void { + let _iframeId = iframeId; + if (_iframeId == null) { + _iframeId = defaultIframeId; + } + if (_DEV_) console.log('postMessageToParentWindow', type, _iframeId, payload); window.parent.postMessage({ type, + iframeId: _iframeId, payload, }, '*'); } diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index 82671dab83..27aa020e0a 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -93,9 +93,16 @@ html { &.embed { background-color: transparent; + overflow: hidden; } } +html.embed, +html.embed body, +html.embed #misskey_app { + height: 100%; +} + html._themeChanging_ { &, * { transition: background 1s ease, border 1s ease !important; diff --git a/packages/frontend/src/ui/embed.vue b/packages/frontend/src/ui/embed.vue new file mode 100644 index 0000000000..c053781445 --- /dev/null +++ b/packages/frontend/src/ui/embed.vue @@ -0,0 +1,113 @@ + + + + + + + diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue index 1f94b55a8b..db5eb19c20 100644 --- a/packages/frontend/src/ui/minimum.vue +++ b/packages/frontend/src/ui/minimum.vue @@ -4,15 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only -->