From f0b0be12b04e4ecc2e2be6ced1b148bc498e9783 Mon Sep 17 00:00:00 2001
From: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sat, 22 Jun 2024 14:52:16 +0900
Subject: [PATCH] =?UTF-8?q?enhance(frontend):=20Websocket=E3=82=92?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=AA=E3=81=84=E3=83=A2=E3=83=BC?=
 =?UTF-8?q?=E3=83=89=E3=82=92=E5=AE=9F=E8=A3=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/scripts/stream-mock.ts | 75 ++++++++++++++++++++
 packages/frontend/src/store.ts               |  1 +
 packages/frontend/src/stream.ts              | 14 +++-
 packages/misskey-js/src/index.ts             |  2 +
 packages/misskey-js/src/streaming.ts         |  2 +-
 5 files changed, 90 insertions(+), 4 deletions(-)
 create mode 100644 packages/frontend/src/scripts/stream-mock.ts

diff --git a/packages/frontend/src/scripts/stream-mock.ts b/packages/frontend/src/scripts/stream-mock.ts
new file mode 100644
index 0000000000..451b9e45ee
--- /dev/null
+++ b/packages/frontend/src/scripts/stream-mock.ts
@@ -0,0 +1,75 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { EventEmitter } from 'eventemitter3';
+import * as Misskey from 'misskey-js';
+import type { Channels, StreamEvents } from 'misskey-js';
+
+type AnyOf<T extends Record<any, any>> = T[keyof T];
+type OmitFirst<T extends any[]> = T extends [any, ...infer R] ? R : never;
+
+/**
+ * Websocket無効化時に使うStreamのモック(なにもしない)
+ */
+export class StreamMock extends EventEmitter<StreamEvents> {
+	public readonly state = 'initializing';
+
+	constructor(...args: ConstructorParameters<typeof Misskey.Stream>) {
+		super();
+		// do nothing
+	}
+
+	public useChannel<C extends keyof Channels>(channel: C, params?: Channels[C]['params'], name?: string): ChannelConnectionMock<Channels[C]> {
+		return new ChannelConnectionMock(this, channel, name);
+	}
+
+	public removeSharedConnection(connection: any): void {
+		// do nothing
+	}
+
+	public removeSharedConnectionPool(pool: any): void {
+		// do nothing
+	}
+
+	public disconnectToChannel(): void {
+		// do nothing
+	}
+
+	public send(typeOrPayload: string): void
+	public send(typeOrPayload: string, payload: any): void
+	public send(typeOrPayload: Record<string, any> | any[]): void
+	public send(typeOrPayload: string | Record<string, any> | any[], payload?: any): void {
+		// do nothing
+	}
+
+	public ping(): void {
+		// do nothing
+	}
+
+	public heartbeat(): void {
+		// do nothing
+	}
+
+	public close(): void {
+		// do nothing
+	}
+}
+
+class ChannelConnectionMock<Channel extends AnyOf<Channels> = any> extends EventEmitter<Channel['events']>{
+	public id = '';
+
+	constructor(stream: StreamMock, ...args: OmitFirst<ConstructorParameters<typeof Misskey.ChannelConnection<Channel>>>) {
+		super();
+		// do nothing
+	}
+
+	public send<T extends keyof Channel['receives']>(type: T, body: Channel['receives'][T]): void {
+		// do nothing
+	}
+
+	public dispose(): void {
+		// do nothing
+	}
+}
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e8eb5a1ed7..e5114b058b 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -528,6 +528,7 @@ export class ColdDeviceStorage {
 		lightTheme,
 		darkTheme,
 		syncDeviceDarkMode: true,
+		disableWebsocket: false,
 		plugins: [] as Plugin[],
 	};
 
diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts
index 0d5bd78b09..d23ad8711e 100644
--- a/packages/frontend/src/stream.ts
+++ b/packages/frontend/src/stream.ts
@@ -7,6 +7,9 @@ import * as Misskey from 'misskey-js';
 import { markRaw } from 'vue';
 import { $i } from '@/account.js';
 import { wsOrigin } from '@/config.js';
+import { StreamMock } from '@/scripts/stream-mock.js';
+import { isEmbedPage } from '@/scripts/embed-page.js';
+import { ColdDeviceStorage } from '@/store.js';
 
 // heart beat interval in ms
 const HEART_BEAT_INTERVAL = 1000 * 60;
@@ -18,9 +21,14 @@ let lastHeartbeatCall = 0;
 export function useStream(): Misskey.Stream {
 	if (stream) return stream;
 
-	stream = markRaw(new Misskey.Stream(wsOrigin, $i ? {
-		token: $i.token,
-	} : null));
+	if (isEmbedPage() || ColdDeviceStorage.get('disableWebsocket') === true) {
+		stream = markRaw(new StreamMock(wsOrigin, null) as unknown as Misskey.Stream);
+		return stream;
+	} else {
+		stream = markRaw(new Misskey.Stream(wsOrigin, $i ? {
+			token: $i.token,
+		} : null));
+	}
 
 	if (timeoutHeartBeat) window.clearTimeout(timeoutHeartBeat);
 	timeoutHeartBeat = window.setTimeout(heartbeat, HEART_BEAT_INTERVAL);
diff --git a/packages/misskey-js/src/index.ts b/packages/misskey-js/src/index.ts
index 28007a8ade..a34d86bf95 100644
--- a/packages/misskey-js/src/index.ts
+++ b/packages/misskey-js/src/index.ts
@@ -1,5 +1,6 @@
 import { type Endpoints } from './api.types.js';
 import Stream, { Connection } from './streaming.js';
+import { type StreamEvents } from './streaming.js';
 import { type Channels } from './streaming.types.js';
 import { type Acct } from './acct.js';
 import * as consts from './consts.js';
@@ -8,6 +9,7 @@ export type {
 	Endpoints,
 	Channels,
 	Acct,
+	StreamEvents,
 };
 
 export {
diff --git a/packages/misskey-js/src/streaming.ts b/packages/misskey-js/src/streaming.ts
index 0f26857782..f5528e333c 100644
--- a/packages/misskey-js/src/streaming.ts
+++ b/packages/misskey-js/src/streaming.ts
@@ -17,7 +17,7 @@ export function urlQuery(obj: Record<string, string | number | boolean | undefin
 
 type AnyOf<T extends Record<any, any>> = T[keyof T];
 
-type StreamEvents = {
+export type StreamEvents = {
 	_connected_: void;
 	_disconnected_: void;
 } & BroadcastEvents;