enhance: embedページではstoreの保存先を完全に分離するように
This commit is contained in:
parent
ecf7945fe8
commit
e9b3b5ffcd
|
@ -751,7 +751,7 @@ export class ClientServerService {
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//region noindex pages
|
//#region noindex pages
|
||||||
// Tags
|
// Tags
|
||||||
fastify.get<{ Params: { clip: string; } }>('/tags/:tag', async (request, reply) => {
|
fastify.get<{ Params: { clip: string; } }>('/tags/:tag', async (request, reply) => {
|
||||||
return await renderBase(reply, { noindex: true });
|
return await renderBase(reply, { noindex: true });
|
||||||
|
@ -761,7 +761,13 @@ export class ClientServerService {
|
||||||
fastify.get<{ Params: { clip: string; } }>('/user-tags/:tag', async (request, reply) => {
|
fastify.get<{ Params: { clip: string; } }>('/user-tags/:tag', async (request, reply) => {
|
||||||
return await renderBase(reply, { noindex: true });
|
return await renderBase(reply, { noindex: true });
|
||||||
});
|
});
|
||||||
//endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region embed pages
|
||||||
|
fastify.get('/embed/:path(.*)', async (request, reply) => {
|
||||||
|
reply.removeHeader('X-Frame-Options');
|
||||||
|
return await renderBase(reply, { noindex: true });
|
||||||
|
});
|
||||||
|
|
||||||
fastify.get('/_info_card_', async (request, reply) => {
|
fastify.get('/_info_card_', async (request, reply) => {
|
||||||
const meta = await this.metaService.fetch(true);
|
const meta = await this.metaService.fetch(true);
|
||||||
|
@ -776,6 +782,7 @@ export class ClientServerService {
|
||||||
originalNotesCount: await this.notesRepository.countBy({ userHost: IsNull() }),
|
originalNotesCount: await this.notesRepository.countBy({ userHost: IsNull() }),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
//#endregion
|
||||||
|
|
||||||
fastify.get('/bios', async (request, reply) => {
|
fastify.get('/bios', async (request, reply) => {
|
||||||
return await reply.view('bios', {
|
return await reply.view('bios', {
|
||||||
|
|
|
@ -9,10 +9,19 @@ import 'vite/modulepreload-polyfill';
|
||||||
import '@/style.scss';
|
import '@/style.scss';
|
||||||
import { mainBoot } from '@/boot/main-boot.js';
|
import { mainBoot } from '@/boot/main-boot.js';
|
||||||
import { subBoot } from '@/boot/sub-boot.js';
|
import { subBoot } from '@/boot/sub-boot.js';
|
||||||
|
import { isEmbedPage } from '@/scripts/embed-page.js';
|
||||||
|
|
||||||
const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete'];
|
const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete', '/embed'];
|
||||||
|
|
||||||
if (subBootPaths.some(i => location.pathname === i || location.pathname.startsWith(i + '/'))) {
|
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' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subBoot();
|
subBoot();
|
||||||
} else {
|
} else {
|
||||||
mainBoot();
|
mainBoot();
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { apiUrl } from '@/config.js';
|
||||||
import { waiting, popup, popupMenu, success, alert } from '@/os.js';
|
import { waiting, popup, popupMenu, success, alert } from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
|
import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
|
||||||
|
import { isEmbedPage } from '@/scripts/embed-page.js';
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
||||||
|
@ -21,8 +22,14 @@ type Account = Misskey.entities.MeDetailed & { token: string };
|
||||||
|
|
||||||
const accountData = miLocalStorage.getItem('account');
|
const accountData = miLocalStorage.getItem('account');
|
||||||
|
|
||||||
|
function initAccount() {
|
||||||
|
if (isEmbedPage()) return null;
|
||||||
|
if (accountData) return reactive(JSON.parse(accountData) as Account);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: 外部からはreadonlyに
|
// TODO: 外部からはreadonlyに
|
||||||
export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
|
export const $i = initAccount();
|
||||||
|
|
||||||
export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
|
export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
|
||||||
export const iAmAdmin = $i != null && $i.isAdmin;
|
export const iAmAdmin = $i != null && $i.isAdmin;
|
||||||
|
@ -78,10 +85,14 @@ export async function signout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> {
|
export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> {
|
||||||
|
if (isEmbedPage()) return [];
|
||||||
|
|
||||||
return (await get('accounts')) || [];
|
return (await get('accounts')) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addAccount(id: Account['id'], token: Account['token']) {
|
export async function addAccount(id: Account['id'], token: Account['token']) {
|
||||||
|
if (isEmbedPage()) return;
|
||||||
|
|
||||||
const accounts = await getAccounts();
|
const accounts = await getAccounts();
|
||||||
if (!accounts.some(x => x.id === id)) {
|
if (!accounts.some(x => x.id === id)) {
|
||||||
await set('accounts', accounts.concat([{ id, token }]));
|
await set('accounts', accounts.concat([{ id, token }]));
|
||||||
|
@ -183,6 +194,8 @@ export async function refreshAccount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(token: Account['token'], redirect?: string) {
|
export async function login(token: Account['token'], redirect?: string) {
|
||||||
|
if (isEmbedPage()) return;
|
||||||
|
|
||||||
const showing = ref(true);
|
const showing = ref(true);
|
||||||
popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
|
popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
|
||||||
success: false,
|
success: false,
|
||||||
|
|
|
@ -24,7 +24,17 @@ import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { setupRouter } from '@/router/definition.js';
|
import { setupRouter } from '@/router/definition.js';
|
||||||
|
|
||||||
export async function common(createVue: () => App<Element>) {
|
export type CommonBootOptions = {
|
||||||
|
forceColorMode?: 'dark' | 'light' | 'auto';
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultCommonBootOptions: CommonBootOptions = {
|
||||||
|
forceColorMode: 'auto',
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function common(createVue: () => App<Element>, partialOptions?: Partial<CommonBootOptions>) {
|
||||||
|
const bootOptions = Object.assign(defaultCommonBootOptions, partialOptions);
|
||||||
|
|
||||||
console.info(`Misskey v${version}`);
|
console.info(`Misskey v${version}`);
|
||||||
|
|
||||||
if (_DEV_) {
|
if (_DEV_) {
|
||||||
|
@ -166,15 +176,19 @@ export async function common(createVue: () => App<Element>) {
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region Sync dark mode
|
//#region Sync dark mode
|
||||||
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
|
if (ColdDeviceStorage.get('syncDeviceDarkMode') && bootOptions.forceColorMode === 'auto') {
|
||||||
defaultStore.set('darkMode', isDeviceDarkmode());
|
defaultStore.set('darkMode', isDeviceDarkmode());
|
||||||
}
|
}
|
||||||
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (mql) => {
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (mql) => {
|
||||||
if (ColdDeviceStorage.get('syncDeviceDarkMode')) {
|
if (ColdDeviceStorage.get('syncDeviceDarkMode') && bootOptions.forceColorMode === 'auto') {
|
||||||
defaultStore.set('darkMode', mql.matches);
|
defaultStore.set('darkMode', mql.matches);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (bootOptions.forceColorMode !== 'auto') {
|
||||||
|
defaultStore.set('darkMode', bootOptions.forceColorMode === 'dark');
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
fetchInstanceMetaPromise.then(() => {
|
fetchInstanceMetaPromise.then(() => {
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
|
|
||||||
import { createApp, defineAsyncComponent } from 'vue';
|
import { createApp, defineAsyncComponent } from 'vue';
|
||||||
import { common } from './common.js';
|
import { common } from './common.js';
|
||||||
|
import type { CommonBootOptions } from './common.js';
|
||||||
|
|
||||||
export async function subBoot() {
|
export async function subBoot(options?: CommonBootOptions) {
|
||||||
const { isClientUpdated } = await common(() => createApp(
|
const { isClientUpdated } = await common(() => createApp(
|
||||||
defineAsyncComponent(() => import('@/ui/minimum.vue')),
|
defineAsyncComponent(() => import('@/ui/minimum.vue')),
|
||||||
));
|
), options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
import { isEmbedPage, initEmbedPageLocalStorage } from "@/scripts/embed-page.js";
|
||||||
|
|
||||||
type Keys =
|
export type Keys =
|
||||||
'v' |
|
'v' |
|
||||||
'lastVersion' |
|
'lastVersion' |
|
||||||
'instance' |
|
'instance' |
|
||||||
|
@ -38,12 +39,33 @@ type Keys =
|
||||||
`aiscript:${string}` |
|
`aiscript:${string}` |
|
||||||
'lastEmojisFetchedAt' | // DEPRECATED, stored in indexeddb (13.9.0~)
|
'lastEmojisFetchedAt' | // DEPRECATED, stored in indexeddb (13.9.0~)
|
||||||
'emojis' | // DEPRECATED, stored in indexeddb (13.9.0~);
|
'emojis' | // DEPRECATED, stored in indexeddb (13.9.0~);
|
||||||
`channelLastReadedAt:${string}`
|
`channelLastReadedAt:${string}` |
|
||||||
|
`idbfallback::${string}`
|
||||||
|
|
||||||
|
// セッション毎に廃棄されるLocalStorage代替(embedなどで使用)
|
||||||
|
const safeSessionStorage = new Map<Keys, string>();
|
||||||
|
|
||||||
export const miLocalStorage = {
|
export const miLocalStorage = {
|
||||||
getItem: (key: Keys): string | null => window.localStorage.getItem(key),
|
getItem: (key: Keys): string | null => {
|
||||||
setItem: (key: Keys, value: string): void => window.localStorage.setItem(key, value),
|
if (isEmbedPage()) {
|
||||||
removeItem: (key: Keys): void => window.localStorage.removeItem(key),
|
return safeSessionStorage.get(key) ?? null;
|
||||||
|
}
|
||||||
|
return window.localStorage.getItem(key);
|
||||||
|
},
|
||||||
|
setItem: (key: Keys, value: string): void => {
|
||||||
|
if (isEmbedPage()) {
|
||||||
|
safeSessionStorage.set(key, value);
|
||||||
|
} else {
|
||||||
|
window.localStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItem: (key: Keys): void => {
|
||||||
|
if (isEmbedPage()) {
|
||||||
|
safeSessionStorage.delete(key);
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
},
|
||||||
getItemAsJson: (key: Keys): any | undefined => {
|
getItemAsJson: (key: Keys): any | undefined => {
|
||||||
const item = miLocalStorage.getItem(key);
|
const item = miLocalStorage.getItem(key);
|
||||||
if (item === null) {
|
if (item === null) {
|
||||||
|
@ -51,5 +73,12 @@ export const miLocalStorage = {
|
||||||
}
|
}
|
||||||
return JSON.parse(item);
|
return JSON.parse(item);
|
||||||
},
|
},
|
||||||
setItemAsJson: (key: Keys, value: any): void => window.localStorage.setItem(key, JSON.stringify(value)),
|
setItemAsJson: (key: Keys, value: any): void => {
|
||||||
|
miLocalStorage.setItem(key, JSON.stringify(value));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isEmbedPage()) {
|
||||||
|
initEmbedPageLocalStorage();
|
||||||
|
if (_DEV_) console.warn('Using safeSessionStorage as localStorage alternative');
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import MkContextMenu from '@/components/MkContextMenu.vue';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
import { MenuItem } from '@/types/menu.js';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
|
import { isEmbedPage } from '@/scripts/embed-page.js';
|
||||||
|
|
||||||
export const openingWindowsCount = ref(0);
|
export const openingWindowsCount = ref(0);
|
||||||
|
|
||||||
|
@ -172,6 +173,8 @@ export async function popup<T extends Component>(
|
||||||
events: ComponentEmit<T> = {} as ComponentEmit<T>,
|
events: ComponentEmit<T> = {} as ComponentEmit<T>,
|
||||||
disposeEvent?: keyof ComponentEmit<T>,
|
disposeEvent?: keyof ComponentEmit<T>,
|
||||||
): Promise<{ dispose: () => void }> {
|
): Promise<{ dispose: () => void }> {
|
||||||
|
if (isEmbedPage()) return { dispose: () => {} };
|
||||||
|
|
||||||
markRaw(component);
|
markRaw(component);
|
||||||
|
|
||||||
const id = ++popupIdCount;
|
const id = ++popupIdCount;
|
||||||
|
|
13
packages/frontend/src/pages/embed/index.vue
Normal file
13
packages/frontend/src/pages/embed/index.vue
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
$i: {{ JSON.stringify($i) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { $i } from '@/account.js';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -555,6 +555,10 @@ const routes: RouteDef[] = [{
|
||||||
path: '/reversi/g/:gameId',
|
path: '/reversi/g/:gameId',
|
||||||
component: page(() => import('@/pages/reversi/game.vue')),
|
component: page(() => import('@/pages/reversi/game.vue')),
|
||||||
loginRequired: false,
|
loginRequired: false,
|
||||||
|
}, {
|
||||||
|
path: '/embed',
|
||||||
|
component: page(() => import('@/pages/embed/index.vue')),
|
||||||
|
// children: [],
|
||||||
}, {
|
}, {
|
||||||
path: '/timeline',
|
path: '/timeline',
|
||||||
component: page(() => import('@/pages/timeline.vue')),
|
component: page(() => import('@/pages/timeline.vue')),
|
||||||
|
|
37
packages/frontend/src/scripts/embed-page.ts
Normal file
37
packages/frontend/src/scripts/embed-page.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { miLocalStorage } from "@/local-storage.js";
|
||||||
|
import type { Keys } from "@/local-storage.js";
|
||||||
|
|
||||||
|
export function isEmbedPage() {
|
||||||
|
return location.pathname.startsWith('/embed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EmbedページではlocalStorageを使用できないようにしているが、
|
||||||
|
* 動作に必要な値はsafeSessionStorage(miLocalStorage内のやつ)に移動する
|
||||||
|
*/
|
||||||
|
export function initEmbedPageLocalStorage() {
|
||||||
|
if (!isEmbedPage()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keysToDuplicate: Keys[] = [
|
||||||
|
'v',
|
||||||
|
'lastVersion',
|
||||||
|
'instance',
|
||||||
|
'instanceCachedAt',
|
||||||
|
'lang',
|
||||||
|
'locale',
|
||||||
|
'localeVersion',
|
||||||
|
];
|
||||||
|
|
||||||
|
keysToDuplicate.forEach(key => {
|
||||||
|
const value = window.localStorage.getItem(key);
|
||||||
|
if (value && !miLocalStorage.getItem(key)) {
|
||||||
|
miLocalStorage.setItem(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -10,10 +10,12 @@ import {
|
||||||
set as iset,
|
set as iset,
|
||||||
del as idel,
|
del as idel,
|
||||||
} from 'idb-keyval';
|
} from 'idb-keyval';
|
||||||
|
import { isEmbedPage } from './embed-page.js';
|
||||||
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
||||||
const fallbackName = (key: string) => `idbfallback::${key}`;
|
const PREFIX = 'idbfallback::';
|
||||||
|
|
||||||
let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && window.indexedDB.open) : true;
|
let idbAvailable = typeof window !== 'undefined' ? !!(window.indexedDB && typeof window.indexedDB.open === 'function' && !isEmbedPage()) : true;
|
||||||
|
|
||||||
// iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。
|
// iframe.contentWindow.indexedDB.deleteDatabase() がchromeのバグで使用できないため、indexedDBを無効化している。
|
||||||
// バグが治って再度有効化するのであれば、cypressのコマンド内のコメントアウトを外すこと
|
// バグが治って再度有効化するのであれば、cypressのコマンド内のコメントアウトを外すこと
|
||||||
|
@ -38,15 +40,15 @@ if (idbAvailable) {
|
||||||
|
|
||||||
export async function get(key: string) {
|
export async function get(key: string) {
|
||||||
if (idbAvailable) return iget(key);
|
if (idbAvailable) return iget(key);
|
||||||
return JSON.parse(window.localStorage.getItem(fallbackName(key)));
|
return miLocalStorage.getItemAsJson(`${PREFIX}${key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function set(key: string, val: any) {
|
export async function set(key: string, val: any) {
|
||||||
if (idbAvailable) return iset(key, val);
|
if (idbAvailable) return iset(key, val);
|
||||||
return window.localStorage.setItem(fallbackName(key), JSON.stringify(val));
|
return miLocalStorage.setItemAsJson(`${PREFIX}${key}`, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function del(key: string) {
|
export async function del(key: string) {
|
||||||
if (idbAvailable) return idel(key);
|
if (idbAvailable) return idel(key);
|
||||||
return window.localStorage.removeItem(fallbackName(key));
|
return miLocalStorage.removeItem(`${PREFIX}${key}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
export const postMessageEventTypes = [
|
export const postMessageEventTypes = [
|
||||||
'misskey:shareForm:shareCompleted',
|
'misskey:shareForm:shareCompleted',
|
||||||
|
'misskey:embed:changeHeight',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type PostMessageEventType = typeof postMessageEventTypes[number];
|
export type PostMessageEventType = typeof postMessageEventTypes[number];
|
||||||
|
@ -18,7 +19,7 @@ export type MiPostMessageEvent = {
|
||||||
* 親フレームにイベントを送信
|
* 親フレームにイベントを送信
|
||||||
*/
|
*/
|
||||||
export function postMessageToParentWindow(type: PostMessageEventType, payload?: any): void {
|
export function postMessageToParentWindow(type: PostMessageEventType, payload?: any): void {
|
||||||
window.postMessage({
|
window.parent.postMessage({
|
||||||
type,
|
type,
|
||||||
payload,
|
payload,
|
||||||
}, '*');
|
}, '*');
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div :class="isEmbed ? $style.rootForEmbedPage : $style.root">
|
||||||
<div style="container-type: inline-size;">
|
<div style="container-type: inline-size;">
|
||||||
<RouterView/>
|
<RouterView/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,6 +20,12 @@ import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '
|
||||||
import { instanceName } from '@/config.js';
|
import { instanceName } from '@/config.js';
|
||||||
import { mainRouter } from '@/router/main.js';
|
import { mainRouter } from '@/router/main.js';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
isEmbed?: boolean;
|
||||||
|
}>(), {
|
||||||
|
isEmbed: false,
|
||||||
|
});
|
||||||
|
|
||||||
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
|
||||||
|
|
||||||
const pageMetadata = ref<null | PageMetadata>(null);
|
const pageMetadata = ref<null | PageMetadata>(null);
|
||||||
|
@ -38,7 +44,9 @@ provideMetadataReceiver((metadataGetter) => {
|
||||||
});
|
});
|
||||||
provideReactiveMetadata(pageMetadata);
|
provideReactiveMetadata(pageMetadata);
|
||||||
|
|
||||||
|
if (!props.isEmbed) {
|
||||||
document.documentElement.style.overflowY = 'scroll';
|
document.documentElement.style.overflowY = 'scroll';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
@ -46,4 +54,8 @@ document.documentElement.style.overflowY = 'scroll';
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rootForEmbedPage {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue