From cf3ec7fc396125ed452c553b34ae2737329fc61d Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Fri, 24 Dec 2021 01:36:20 +0100 Subject: feat: Service to main process communication --- packages/main/package.json | 1 + packages/main/src/index.ts | 51 ++++++++++++++++------ packages/main/tsconfig.json | 3 ++ packages/preload/src/SophieRendererImpl.ts | 5 +++ packages/renderer/src/devTools.ts | 13 ++++++ packages/renderer/src/index.tsx | 6 ++- packages/service-preload/package.json | 22 ++++++++++ packages/service-preload/src/index.ts | 29 ++++++++++++ packages/service-preload/tsconfig.json | 17 ++++++++ packages/service-preload/vite.config.js | 31 +++++++++++++ packages/service-shared/package.json | 21 +++++++++ packages/service-shared/src/index.ts | 28 ++++++++++++ packages/service-shared/src/ipc.ts | 23 ++++++++++ packages/service-shared/src/schemas.ts | 28 ++++++++++++ packages/service-shared/tsconfig.json | 12 +++++ packages/service-shared/vite.config.js | 34 +++++++++++++++ packages/shared/package.json | 11 ++--- .../shared/src/contextBridge/SophieRenderer.ts | 2 + packages/shared/src/index.ts | 6 ++- packages/shared/src/ipc.ts | 31 +++++++++++++ .../shared/src/ipc/MainToRendererIpcMessage.ts | 24 ---------- .../shared/src/ipc/RendererToMainIpcMessage.ts | 25 ----------- 22 files changed, 352 insertions(+), 71 deletions(-) create mode 100644 packages/service-preload/package.json create mode 100644 packages/service-preload/src/index.ts create mode 100644 packages/service-preload/tsconfig.json create mode 100644 packages/service-preload/vite.config.js create mode 100644 packages/service-shared/package.json create mode 100644 packages/service-shared/src/index.ts create mode 100644 packages/service-shared/src/ipc.ts create mode 100644 packages/service-shared/src/schemas.ts create mode 100644 packages/service-shared/tsconfig.json create mode 100644 packages/service-shared/vite.config.js create mode 100644 packages/shared/src/ipc.ts delete mode 100644 packages/shared/src/ipc/MainToRendererIpcMessage.ts delete mode 100644 packages/shared/src/ipc/RendererToMainIpcMessage.ts (limited to 'packages') diff --git a/packages/main/package.json b/packages/main/package.json index 014c511..a23d791 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -10,6 +10,7 @@ "typecheck": "tsc" }, "dependencies": { + "@sophie/service-shared": "workspace:*", "@sophie/shared": "workspace:*", "electron": "16.0.5", "mobx": "^6.3.10", diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index f56d629..857c15d 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -22,6 +22,10 @@ import { app, BrowserView, BrowserWindow } from 'electron'; import { autorun } from 'mobx'; import { getSnapshot, onPatch } from 'mobx-state-tree'; import { join } from 'path'; +import { + ServiceToMainIpcMessage, + unreadCount, +} from '@sophie/service-shared'; import { browserViewBounds, MainToRendererIpcMessage, @@ -54,9 +58,9 @@ app.enableSandbox(); // Remove sophie and electron from the user-agent string to avoid detection. const originalUserAgent = app.userAgentFallback; +const userAgent = originalUserAgent.replaceAll(/ ([Ss]ophie|Electron)\/[0-9.]+/g, ''); // Removing the electron version breaks redux devtools, so we only do this in production. if (!isDevelopment) { - const userAgent = originalUserAgent.replaceAll(/ ([Ss]ophie|Electron)\/[0-9.]+/g, ''); app.userAgentFallback = userAgent; } @@ -97,6 +101,17 @@ async function createWindow(): Promise { webContents.userAgent = originalUserAgent; + const browserView = new BrowserView({ + webPreferences: { + sandbox: true, + partition: 'persist:service', + preload: join(__dirname, '../../service-preload/dist/index.cjs'), + }, + }); + + browserView.webContents.userAgent = userAgent; + browserView.setBackgroundColor('#fff'); + webContents.on('ipc-message', (_event, channel, ...args) => { try { switch (channel) { @@ -109,8 +124,11 @@ async function createWindow(): Promise { case RendererToMainIpcMessage.SetPaletteMode: store.setPaletteMode(paletteMode.parse(args[0])) break; + case RendererToMainIpcMessage.ReloadAllServices: + browserView.webContents.reload(); + break; default: - console.warn('Unknown IPC message:', channel, args); + console.error('Unknown IPC message:', channel, args); break; } } catch (err) { @@ -122,26 +140,31 @@ async function createWindow(): Promise { webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); }); - const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) - ? import.meta.env.VITE_DEV_SERVER_URL - : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); - - const browserView = new BrowserView({ - webPreferences: { - sandbox: true, - partition: 'persist:service', - }, + browserView.webContents.on('ipc-message', (_event, channel, ...args) => { + try { + switch (channel) { + case ServiceToMainIpcMessage.SetUnreadCount: + console.log('Unread count:', unreadCount.parse(args[0])); + break; + default: + console.error('Unknown IPC message:', channel, args); + break; + } + } catch (err) { + console.error('Error while processing IPC message:', channel, args, err); + } }); - browserView.setBackgroundColor('#fff'); - autorun(() => { browserView.setBounds(store.shared.browserViewBounds); }); + const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) + ? import.meta.env.VITE_DEV_SERVER_URL + : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); await mainWindow.loadURL(pageUrl); - mainWindow.addBrowserView(browserView); + mainWindow.setBrowserView(browserView); return browserView.webContents.loadURL('https://git.marussy.com/sophie/about'); } diff --git a/packages/main/tsconfig.json b/packages/main/tsconfig.json index 5afee21..2529709 100644 --- a/packages/main/tsconfig.json +++ b/packages/main/tsconfig.json @@ -11,6 +11,9 @@ ] }, "references": [ + { + "path": "../service-shared" + }, { "path": "../shared" } diff --git a/packages/preload/src/SophieRendererImpl.ts b/packages/preload/src/SophieRendererImpl.ts index 6bffeae..bd0f6cb 100644 --- a/packages/preload/src/SophieRendererImpl.ts +++ b/packages/preload/src/SophieRendererImpl.ts @@ -86,6 +86,10 @@ export class SophieRendererImpl implements SophieRenderer { this.#send(RendererToMainIpcMessage.SetPaletteMode, mode); } } + + reloadAllServices(): void { + this.#send(RendererToMainIpcMessage.ReloadAllServices); + } } export function createSophieRenderer(): SophieRenderer { @@ -103,5 +107,6 @@ export function createSophieRenderer(): SophieRenderer { setSharedStoreListener: impl.setSharedStoreListener.bind(impl), setBrowserViewBounds: impl.setBrowserViewBounds.bind(impl), setPaletteMode: impl.setPaletteMode.bind(impl), + reloadAllServices: impl.reloadAllServices.bind(impl), }; } diff --git a/packages/renderer/src/devTools.ts b/packages/renderer/src/devTools.ts index ffd99e6..44c87ae 100644 --- a/packages/renderer/src/devTools.ts +++ b/packages/renderer/src/devTools.ts @@ -55,3 +55,16 @@ export function exposeToReduxDevtools(model: IAnyStateTreeNode): void { console.error('Could not connect to Redux devtools', err); }); } + +/** + * Sends a message to the main process to reload all services when + * `build/watch.js` sends a reload event on bundle write. + */ +export function hotReloadServices(): void { + import.meta.hot?.on( + 'sophie:reload-services', + () => { + window.sophieRenderer.reloadAllServices(); + }, + ); +} diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index 90cba2c..0919b93 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx @@ -29,11 +29,15 @@ import { render } from 'react-dom'; import { App } from './components/App'; import { StoreProvider } from './components/StoreProvider'; import { ThemeProvider } from './components/ThemeProvider'; -import { exposeToReduxDevtools } from './devTools'; +import { exposeToReduxDevtools, hotReloadServices } from './devTools'; import { createAndConnectRootStore } from './stores/RootStore'; const isDevelopment = import.meta.env.MODE === 'development'; +if (isDevelopment) { + hotReloadServices(); +} + const store = createAndConnectRootStore(window.sophieRenderer); if (isDevelopment) { diff --git a/packages/service-preload/package.json b/packages/service-preload/package.json new file mode 100644 index 0000000..1296cda --- /dev/null +++ b/packages/service-preload/package.json @@ -0,0 +1,22 @@ +{ + "name": "@sophie/service-preload", + "version": "0.1.0", + "private": true, + "sideEffects": false, + "main": "dist/index.cjs", + "types": "dist-types/index.d.ts", + "scripts": { + "clean": "rimraf dist dist-types tsconfig.tsbuildinfo", + "build": "vite build", + "typecheck": "tsc" + }, + "dependencies": { + "@sophie/service-shared": "workspace:*", + "electron": "16.0.5" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "^4.5.4", + "vite": "^2.7.6" + } +} diff --git a/packages/service-preload/src/index.ts b/packages/service-preload/src/index.ts new file mode 100644 index 0000000..4e93a07 --- /dev/null +++ b/packages/service-preload/src/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ipcRenderer } from 'electron'; +import { ServiceToMainIpcMessage } from '@sophie/service-shared'; + +let i = 1; + +setInterval(() => { + ipcRenderer.send(ServiceToMainIpcMessage.SetUnreadCount, { direct: i }); + i += 1; +}, 1000); diff --git a/packages/service-preload/tsconfig.json b/packages/service-preload/tsconfig.json new file mode 100644 index 0000000..e5b8ccb --- /dev/null +++ b/packages/service-preload/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "declarationDir": "dist-types", + "emitDeclarationOnly": true, + "rootDir": "src" + }, + "references": [ + { + "path": "../service-shared" + } + ], + "include": [ + "src/**/*.ts" + ] +} diff --git a/packages/service-preload/vite.config.js b/packages/service-preload/vite.config.js new file mode 100644 index 0000000..5af78d9 --- /dev/null +++ b/packages/service-preload/vite.config.js @@ -0,0 +1,31 @@ +// @ts-check + +import { builtinModules } from 'module'; + +import { chrome, makeConfig } from '../../config/vite-common'; + +/** @type string */ +const PACKAGE_ROOT = __dirname; + +/** + * @type {import('vite').UserConfig} + * @see https://vitejs.dev/config/ + */ +const config = makeConfig({ + root: PACKAGE_ROOT, + build: { + target: chrome, + lib: { + entry: 'src/index.ts', + formats: ['cjs'], + }, + rollupOptions: { + external: [ + 'electron', + ...builtinModules, + ], + }, + }, +}); + +export default config; diff --git a/packages/service-shared/package.json b/packages/service-shared/package.json new file mode 100644 index 0000000..88f113e --- /dev/null +++ b/packages/service-shared/package.json @@ -0,0 +1,21 @@ +{ + "name": "@sophie/service-shared", + "version": "0.1.0", + "private": true, + "sideEffects": false, + "main": "dist/index.cjs", + "types": "dist-types/index.d.ts", + "scripts": { + "clean": "rimraf dist dist-types tsconfig.tsbuildinfo", + "build": "vite build", + "typecheck": "tsc" + }, + "dependencies": { + "zod": "^3.11.6" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "^4.5.4", + "vite": "^2.7.6" + } +} diff --git a/packages/service-shared/src/index.ts b/packages/service-shared/src/index.ts new file mode 100644 index 0000000..c517959 --- /dev/null +++ b/packages/service-shared/src/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export { ServiceToMainIpcMessage } from './ipc'; + +export type { + UnreadCount, +} from './schemas'; +export { + unreadCount, +} from './schemas'; diff --git a/packages/service-shared/src/ipc.ts b/packages/service-shared/src/ipc.ts new file mode 100644 index 0000000..ad2ceca --- /dev/null +++ b/packages/service-shared/src/ipc.ts @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export enum ServiceToMainIpcMessage { + SetUnreadCount = 'sophie-service-to-main:set-unread-count', +} diff --git a/packages/service-shared/src/schemas.ts b/packages/service-shared/src/schemas.ts new file mode 100644 index 0000000..1513e43 --- /dev/null +++ b/packages/service-shared/src/schemas.ts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { z } from 'zod'; + +export const unreadCount = z.object({ + direct: z.number().nonnegative().optional(), + indirect: z.number().nonnegative().optional(), +}); + +export type UnreadCount = z.infer; diff --git a/packages/service-shared/tsconfig.json b/packages/service-shared/tsconfig.json new file mode 100644 index 0000000..de75833 --- /dev/null +++ b/packages/service-shared/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "composite": true, + "declarationDir": "dist-types", + "emitDeclarationOnly": true, + "rootDir": "src" + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/packages/service-shared/vite.config.js b/packages/service-shared/vite.config.js new file mode 100644 index 0000000..6f4959f --- /dev/null +++ b/packages/service-shared/vite.config.js @@ -0,0 +1,34 @@ +// @ts-check + +import { builtinModules } from 'module'; + +import { chrome, makeConfig, node } from '../../config/vite-common'; + +/** @type string */ +const PACKAGE_ROOT = __dirname; + +/** + * @type {import('vite').UserConfig} + * @see https://vitejs.dev/config/ + */ +const config = makeConfig({ + root: PACKAGE_ROOT, + build: { + target: [ + chrome, + node, + ], + lib: { + entry: 'src/index.ts', + formats: ['cjs'], + }, + rollupOptions: { + external: [ + 'zod', + ...builtinModules, + ], + }, + }, +}); + +export default config; diff --git a/packages/shared/package.json b/packages/shared/package.json index f5d4f90..96e9b68 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -2,6 +2,7 @@ "name": "@sophie/shared", "version": "0.1.0", "private": true, + "sideEffects": false, "main": "dist/index.cjs", "module": "dist/index.es.js", "types": "dist-types/index.d.ts", @@ -10,14 +11,14 @@ "build": "vite build", "typecheck": "tsc" }, - "devDependencies": { - "rimraf": "^3.0.2", - "typescript": "^4.5.4", - "vite": "^2.7.6" - }, "dependencies": { "mobx": "^6.3.10", "mobx-state-tree": "^5.1.0", "zod": "^3.11.6" + }, + "devDependencies": { + "rimraf": "^3.0.2", + "typescript": "^4.5.4", + "vite": "^2.7.6" } } diff --git a/packages/shared/src/contextBridge/SophieRenderer.ts b/packages/shared/src/contextBridge/SophieRenderer.ts index f2e2180..e310829 100644 --- a/packages/shared/src/contextBridge/SophieRenderer.ts +++ b/packages/shared/src/contextBridge/SophieRenderer.ts @@ -28,4 +28,6 @@ export interface SophieRenderer { setBrowserViewBounds(bounds: BrowserViewBounds): void; setPaletteMode(mode: PaletteMode): void; + + reloadAllServices(): void; } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 0a1ec40..f054571 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -20,8 +20,10 @@ export type { SophieRenderer } from './contextBridge/SophieRenderer'; -export { MainToRendererIpcMessage } from './ipc/MainToRendererIpcMessage'; -export { RendererToMainIpcMessage } from './ipc/RendererToMainIpcMessage'; +export { + MainToRendererIpcMessage, + RendererToMainIpcMessage, +} from './ipc'; export type { BrowserViewBounds, diff --git a/packages/shared/src/ipc.ts b/packages/shared/src/ipc.ts new file mode 100644 index 0000000..2e35d7a --- /dev/null +++ b/packages/shared/src/ipc.ts @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021-2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export enum MainToRendererIpcMessage { + SharedStoreSnapshot = 'sophie-main-to-renderer:shared-store-snapshot', + SharedStorePatch = 'sophie-main-to-renderer:shared-store-patch', +} + +export enum RendererToMainIpcMessage { + SharedStoreSnapshotRequest = 'sophie-renderer-to-main:shared-store-snapshot-request', + SetBrowserViewBounds = 'sophie-renderer-to-main:set-browser-view-bounds', + SetPaletteMode = 'sophie-renderer-to-main:set-palette-mode', + ReloadAllServices = 'sophie-renderer-to-main:reload-all-services', +} diff --git a/packages/shared/src/ipc/MainToRendererIpcMessage.ts b/packages/shared/src/ipc/MainToRendererIpcMessage.ts deleted file mode 100644 index 92da489..0000000 --- a/packages/shared/src/ipc/MainToRendererIpcMessage.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2021-2022 Kristóf Marussy - * - * This file is part of Sophie. - * - * Sophie is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export enum MainToRendererIpcMessage { - SharedStoreSnapshot = 'sophie-shared-store-snapshot', - SharedStorePatch = 'sophie-shared-store-patch', -} diff --git a/packages/shared/src/ipc/RendererToMainIpcMessage.ts b/packages/shared/src/ipc/RendererToMainIpcMessage.ts deleted file mode 100644 index ba354d1..0000000 --- a/packages/shared/src/ipc/RendererToMainIpcMessage.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2021-2022 Kristóf Marussy - * - * This file is part of Sophie. - * - * Sophie is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export enum RendererToMainIpcMessage { - SharedStoreSnapshotRequest = 'sophie-shared-store-snapshot-request', - SetBrowserViewBounds = 'sophie-set-browser-view-bounds', - SetPaletteMode = 'sophie-set-palette-mode', -} -- cgit v1.2.3-54-g00ecf