From 8f1efb256f2cc95571088a1e19e83d49c1cbbfac Mon Sep 17 00:00:00 2001 From: Innei Date: Tue, 24 Sep 2024 20:16:42 +0800 Subject: [PATCH] refactor(electron): global caller Signed-off-by: Innei --- apps/main/src/lib/cleaner.ts | 8 ++++--- apps/main/src/tipc/app.ts | 10 +++++--- apps/main/src/tipc/reader.ts | 20 ++++++---------- apps/main/src/window.ts | 14 ++++++++---- packages/shared/src/bridge.ts | 43 ++++++++++++++++++++++------------- 5 files changed, 55 insertions(+), 40 deletions(-) diff --git a/apps/main/src/lib/cleaner.ts b/apps/main/src/lib/cleaner.ts index 190aecaf9..18832311c 100644 --- a/apps/main/src/lib/cleaner.ts +++ b/apps/main/src/lib/cleaner.ts @@ -1,4 +1,4 @@ -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { dialog } from "electron" import { getMainWindow } from "~/window" @@ -17,6 +17,7 @@ export const clearAllData = async () => { message: t("dialog.clearAllData"), buttons: [t("dialog.yes"), t("dialog.no")], }) + const caller = callWindowExpose(win) if (result.response === 1) { return @@ -36,11 +37,12 @@ export const clearAllData = async () => { "cookies", ], }) - callGlobalContextMethod(win, "toast.success", ["App data reset successfully"]) + + caller.toast.success("App data reset successfully") // reload the app win.reload() } catch (error: any) { - callGlobalContextMethod(win, "toast.error", [`Error resetting app data: ${error.message}`]) + caller.toast.error(`Error resetting app data: ${error.message}`) } } diff --git a/apps/main/src/tipc/app.ts b/apps/main/src/tipc/app.ts index ef6c4afaa..58761890f 100644 --- a/apps/main/src/tipc/app.ts +++ b/apps/main/src/tipc/app.ts @@ -1,7 +1,7 @@ import path from "node:path" import { getRendererHandlers } from "@egoist/tipc/main" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import type { BrowserWindow } from "electron" import { app, clipboard, dialog, screen } from "electron" @@ -199,13 +199,17 @@ export const appRoute = { await downloadFile(input, result.filePath).catch((err) => { const senderWindow = (sender as Sender).getOwnerBrowserWindow() if (!senderWindow) return - callGlobalContextMethod(senderWindow, "toast.error", ["Download failed!"]) + callWindowExpose(senderWindow).toast.error("Download failed!", { + duration: 1000, + }) throw err }) const senderWindow = (sender as Sender).getOwnerBrowserWindow() if (!senderWindow) return - callGlobalContextMethod(senderWindow, "toast.success", ["Download success!"]) + callWindowExpose(senderWindow).toast.success("Download success!", { + duration: 1000, + }) }), getAppPath: t.procedure.action(async () => app.getAppPath()), diff --git a/apps/main/src/tipc/reader.ts b/apps/main/src/tipc/reader.ts index c403a8bf2..d21aa085c 100644 --- a/apps/main/src/tipc/reader.ts +++ b/apps/main/src/tipc/reader.ts @@ -2,7 +2,7 @@ import fs from "node:fs" import { createRequire } from "node:module" import path from "node:path" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { app, BrowserWindow } from "electron" import { MsEdgeTTS, OUTPUT_FORMAT } from "msedge-tts" @@ -54,12 +54,9 @@ export const readerRoute = { if (!window) { return } - callGlobalContextMethod(window, "toast.error", [ - error.message, - { - duration: 1000, - }, - ]) + callWindowExpose(window).toast.error(error.message, { + duration: 1000, + }) } }), @@ -70,12 +67,9 @@ export const readerRoute = { } await tts.setMetadata(input, OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS).catch((error) => { - callGlobalContextMethod(window, "toast.error", [ - error.message, - { - duration: 1000, - }, - ]) + callWindowExpose(window).toast.error(error.message, { + duration: 1000, + }) }) }), diff --git a/apps/main/src/window.ts b/apps/main/src/window.ts index ce4145c75..3a4ffacf7 100644 --- a/apps/main/src/window.ts +++ b/apps/main/src/window.ts @@ -2,7 +2,7 @@ import path from "node:path" import { fileURLToPath } from "node:url" import { is } from "@electron-toolkit/utils" -import { callGlobalContextMethod } from "@follow/shared/bridge" +import { callWindowExpose } from "@follow/shared/bridge" import { imageRefererMatches } from "@follow/shared/image" import type { BrowserWindowConstructorOptions } from "electron" import { BrowserWindow, screen, shell } from "electron" @@ -219,7 +219,8 @@ export const createMainWindow = () => { window.hide() } - callGlobalContextMethod(window, "electronClose") + const caller = callWindowExpose(window) + caller.electronClose() } else { windows.mainWindow = null } @@ -228,11 +229,13 @@ export const createMainWindow = () => { window.on("show", () => { cancelPollingUpdateUnreadCount() - callGlobalContextMethod(window, "electronShow") + const caller = callWindowExpose(window) + caller.electronShow() }) window.on("hide", async () => { - const settings = await callGlobalContextMethod(window, "getUISettings") + const caller = callWindowExpose(window) + const settings = await caller.getUISettings() if (settings.showDockBadge) { pollingUpdateUnreadCount() @@ -247,7 +250,8 @@ export const createSettingWindow = (path?: string) => { // if we open a new window then the state between the two windows will be out of sync. if (windows.mainWindow && windows.mainWindow.isVisible()) { windows.mainWindow.show() - callGlobalContextMethod(windows.mainWindow, "showSetting", [path]) + + callWindowExpose(windows.mainWindow).showSetting(path) return } if (windows.settingWindow) { diff --git a/packages/shared/src/bridge.ts b/packages/shared/src/bridge.ts index 31a6a3237..d7192b3e2 100644 --- a/packages/shared/src/bridge.ts +++ b/packages/shared/src/bridge.ts @@ -4,6 +4,7 @@ import type { toast } from "sonner" import type { GeneralSettings, UISettings } from "./interface/settings" const PREFIX = "__follow" + interface RenderGlobalContext { showSetting: (path?: string) => void getGeneralSettings: () => GeneralSettings @@ -19,22 +20,32 @@ export const registerGlobalContext = (context: RenderGlobalContext) => { globalThis[PREFIX] = context } -export function callGlobalContextMethod( - window: BrowserWindow, - method: T, +function createProxy(window: BrowserWindow, path: string[] = []): T { + return new Proxy((() => {}) as any, { + get(_, prop: string) { + const newPath = [...path, prop] - // @ts-expect-error - args: Parameters = [] as any, -): Promise> -export function callGlobalContextMethod(window: BrowserWindow, method: string, args?: any[]): void + return createProxy(window, newPath) + }, + apply(_, __, args: any[]) { + const methodPath = path.join(".") -export function callGlobalContextMethod( - window: BrowserWindow, - method: T, - - args: Parameters = [] as any, -) { - return window.webContents.executeJavaScript( - `globalThis.${PREFIX}.${method}(${args.map((arg) => JSON.stringify(arg)).join(",")})`, - ) + return window.webContents.executeJavaScript( + `globalThis.${PREFIX}.${methodPath}(${args.map((arg) => JSON.stringify(arg)).join(",")})`, + ) + }, + }) +} +type AddPromise = T extends (...args: infer A) => Promise + ? (...args: A) => Promise + : T extends (...args: infer A) => infer R + ? (...args: A) => Promise> + : any + +type Fn = { + [K in keyof T]: AddPromise & + (T[K] extends object ? { [P in keyof T[K]]: AddPromise } : never) +} +export function callWindowExpose(window: BrowserWindow) { + return createProxy(window) as Fn }