From 6a7ed7e0d17c16762d8a39bacd3fed1a6c4c41e3 Mon Sep 17 00:00:00 2001
From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 7 Jun 2024 19:43:09 +0900
Subject: [PATCH] =?UTF-8?q?embed=E3=81=AEstore=E3=81=A7=E3=81=AE=E5=A4=89?=
 =?UTF-8?q?=E6=9B=B4=E3=81=8C=E5=A4=96=E9=83=A8=E3=82=BF=E3=83=96=E3=81=AB?=
 =?UTF-8?q?=E5=BD=B1=E9=9F=BF=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=E3=83=BB=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/server/web/boot.js | 72 +++++++++++++------------
 packages/frontend/src/_embed_boot_.ts   |  6 +++
 packages/frontend/src/pizzax.ts         | 43 ++++++++++-----
 3 files changed, 74 insertions(+), 47 deletions(-)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index 5e59a51ed9..1a71244119 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -32,6 +32,8 @@
 		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.')
 	}
 
+	const isEmbedPage = document.documentElement.classList.contains('embed');
+
 	//#region Detect language & fetch translations
 	if (!localStorage.hasOwnProperty('locale')) {
 		const supportedLangs = LANGS;
@@ -104,49 +106,51 @@
 	}
 	//#endregion
 
-	//#region Theme
-	const theme = localStorage.getItem('theme');
-	if (theme) {
-		for (const [k, v] of Object.entries(JSON.parse(theme))) {
-			document.documentElement.style.setProperty(`--${k}`, v.toString());
+	if (!isEmbedPage) {
+		//#region Theme
+		const theme = localStorage.getItem('theme');
+		if (theme) {
+			for (const [k, v] of Object.entries(JSON.parse(theme))) {
+				document.documentElement.style.setProperty(`--${k}`, v.toString());
 
-			// HTMLの theme-color 適用
-			if (k === 'htmlThemeColor') {
-				for (const tag of document.head.children) {
-					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
-						tag.setAttribute('content', v);
-						break;
+				// HTMLの theme-color 適用
+				if (k === 'htmlThemeColor') {
+					for (const tag of document.head.children) {
+						if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+							tag.setAttribute('content', v);
+							break;
+						}
 					}
 				}
 			}
 		}
-	}
-	const colorScheme = localStorage.getItem('colorScheme');
-	if (colorScheme) {
-		document.documentElement.style.setProperty('color-scheme', colorScheme);
-	}
-	//#endregion
+		const colorScheme = localStorage.getItem('colorScheme');
+		if (colorScheme) {
+			document.documentElement.style.setProperty('color-scheme', colorScheme);
+		}
+		//#endregion
 
-	const fontSize = localStorage.getItem('fontSize');
-	if (fontSize) {
-		document.documentElement.classList.add('f-' + fontSize);
-	}
+		const fontSize = localStorage.getItem('fontSize');
+		if (fontSize) {
+			document.documentElement.classList.add('f-' + fontSize);
+		}
 
-	const useSystemFont = localStorage.getItem('useSystemFont');
-	if (useSystemFont) {
-		document.documentElement.classList.add('useSystemFont');
-	}
+		const useSystemFont = localStorage.getItem('useSystemFont');
+		if (useSystemFont) {
+			document.documentElement.classList.add('useSystemFont');
+		}
 
-	const wallpaper = localStorage.getItem('wallpaper');
-	if (wallpaper) {
-		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
-	}
+		const wallpaper = localStorage.getItem('wallpaper');
+		if (wallpaper) {
+			document.documentElement.style.backgroundImage = `url(${wallpaper})`;
+		}
 
-	const customCss = localStorage.getItem('customCss');
-	if (customCss && customCss.length > 0) {
-		const style = document.createElement('style');
-		style.innerHTML = customCss;
-		document.head.appendChild(style);
+		const customCss = localStorage.getItem('customCss');
+		if (customCss && customCss.length > 0) {
+			const style = document.createElement('style');
+			style.innerHTML = customCss;
+			document.head.appendChild(style);
+		}
 	}
 
 	async function addStyle(styleText) {
diff --git a/packages/frontend/src/_embed_boot_.ts b/packages/frontend/src/_embed_boot_.ts
index e83d7efb2e..bbd42905e3 100644
--- a/packages/frontend/src/_embed_boot_.ts
+++ b/packages/frontend/src/_embed_boot_.ts
@@ -11,6 +11,7 @@ import '@/style.embed.scss';
 import type { CommonBootOptions } from '@/boot/common.js';
 import { subBoot } from '@/boot/sub-boot.js';
 import { setIframeId, postMessageToParentWindow } from '@/scripts/post-message.js';
+import { defaultStore } from '@/store.js';
 
 const bootOptions: Partial<CommonBootOptions> = {};
 
@@ -21,6 +22,11 @@ if (color && ['light', 'dark'].includes(color)) {
 	bootOptions.forceColorMode = color as 'light' | 'dark';
 }
 
+// 外部タブでのstoreの変更の影響を受けないように
+defaultStore.setConfig({
+	disableMessageChannel: true,
+});
+
 // iframeIdの設定
 window.addEventListener('message', event => {
 	if (event.data?.type === 'misskey:embedParent:registerIframeId' && event.data.payload?.iframeId != null) {
diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts
index ac325e923f..f7d23b202a 100644
--- a/packages/frontend/src/pizzax.ts
+++ b/packages/frontend/src/pizzax.ts
@@ -32,6 +32,10 @@ type PizzaxChannelMessage<T extends StateDef> = {
 	userId?: string;
 };
 
+export type PizzaxConfig = {
+	disableMessageChannel: boolean;
+};
+
 export class Storage<T extends StateDef> {
 	public readonly ready: Promise<void>;
 	public readonly loaded: Promise<void>;
@@ -47,6 +51,10 @@ export class Storage<T extends StateDef> {
 	public readonly state: State<T>;
 	public readonly reactiveState: ReactiveState<T>;
 
+	private options: PizzaxConfig = {
+		disableMessageChannel: false,
+	};
+
 	private pizzaxChannel: BroadcastChannel<PizzaxChannelMessage<T>>;
 
 	// 簡易的にキューイングして占有ロックとする
@@ -60,12 +68,13 @@ export class Storage<T extends StateDef> {
 		return promise;
 	}
 
-	constructor(key: string, def: T) {
+	constructor(key: string, def: T, options?: Partial<PizzaxConfig>) {
 		this.key = key;
 		this.deviceStateKeyName = `pizzax::${key}`;
 		this.deviceAccountStateKeyName = $i ? `pizzax::${key}::${$i.id}` : '';
 		this.registryCacheKeyName = $i ? `pizzax::${key}::cache::${$i.id}` : '';
 		this.def = def;
+		this.options = Object.assign(this.options, options);
 
 		this.pizzaxChannel = new BroadcastChannel(`pizzax::${key}`);
 
@@ -119,7 +128,7 @@ export class Storage<T extends StateDef> {
 		this.pizzaxChannel.addEventListener('message', ({ where, key, value, userId }) => {
 			// アカウント変更すればunisonReloadが効くため、このreturnが発火することは
 			// まずないと思うけど一応弾いておく
-			if (where === 'deviceAccount' && !($i && userId !== $i.id)) return;
+			if ((where === 'deviceAccount' && !($i && userId !== $i.id) || this.options.disableMessageChannel)) return;
 			this.reactiveState[key].value = this.state[key] = value;
 		});
 
@@ -174,6 +183,10 @@ export class Storage<T extends StateDef> {
 		});
 	}
 
+	public setConfig(config: Partial<PizzaxConfig>) {
+		this.options = Object.assign(this.options, config);
+	}
+
 	public set<K extends keyof T>(key: K, value: T[K]['default']): Promise<void> {
 		// IndexedDBやBroadcastChannelで扱うために単純なオブジェクトにする
 		// (JSON.parse(JSON.stringify(value))の代わり)
@@ -187,11 +200,13 @@ export class Storage<T extends StateDef> {
 			if (_DEV_) console.log(`set ${String(key)} start`);
 			switch (this.def[key].where) {
 				case 'device': {
-					this.pizzaxChannel.postMessage({
-						where: 'device',
-						key,
-						value: rawValue,
-					});
+					if (!this.options.disableMessageChannel) {
+						this.pizzaxChannel.postMessage({
+							where: 'device',
+							key,
+							value: rawValue,
+						});
+					}
 					const deviceState = await get(this.deviceStateKeyName) || {};
 					deviceState[key] = rawValue;
 					await set(this.deviceStateKeyName, deviceState);
@@ -199,12 +214,14 @@ export class Storage<T extends StateDef> {
 				}
 				case 'deviceAccount': {
 					if ($i == null) break;
-					this.pizzaxChannel.postMessage({
-						where: 'deviceAccount',
-						key,
-						value: rawValue,
-						userId: $i.id,
-					});
+					if (!this.options.disableMessageChannel) {
+						this.pizzaxChannel.postMessage({
+							where: 'deviceAccount',
+							key,
+							value: rawValue,
+							userId: $i.id,
+						});
+					}
 					const deviceAccountState = await get(this.deviceAccountStateKeyName) || {};
 					deviceAccountState[key] = rawValue;
 					await set(this.deviceAccountStateKeyName, deviceAccountState);