aboutsummaryrefslogtreecommitdiffstats
path: root/packages/preload
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-26 21:12:08 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-26 21:15:53 +0100
commita2651dff66faf98dc1a6f924227af454eaa2311d (patch)
tree7f15c402d449b934d5a2b7858aa16523e9fc121e /packages/preload
parentrefactor: Rename RootStore to RendererStore (diff)
downloadsophie-a2651dff66faf98dc1a6f924227af454eaa2311d.tar.gz
sophie-a2651dff66faf98dc1a6f924227af454eaa2311d.tar.zst
sophie-a2651dff66faf98dc1a6f924227af454eaa2311d.zip
refactor: Less boilerplate around SophieRenderer
Diffstat (limited to 'packages/preload')
-rw-r--r--packages/preload/src/SophieRendererImpl.ts112
-rw-r--r--packages/preload/src/contextBridge/SophieRendererImpl.ts74
-rw-r--r--packages/preload/src/index.ts5
-rw-r--r--packages/preload/src/services/RendererToMainIpcService.ts42
4 files changed, 119 insertions, 114 deletions
diff --git a/packages/preload/src/SophieRendererImpl.ts b/packages/preload/src/SophieRendererImpl.ts
deleted file mode 100644
index a06433f..0000000
--- a/packages/preload/src/SophieRendererImpl.ts
+++ /dev/null
@@ -1,112 +0,0 @@
1/*
2 * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { ipcRenderer } from 'electron';
22import type { IJsonPatch } from 'mobx-state-tree';
23import {
24 BrowserViewBounds,
25 browserViewBounds,
26 MainToRendererIpcMessage,
27 RendererToMainIpcMessage,
28 sharedStore,
29 SharedStoreListener,
30 SharedStoreSnapshotIn,
31 SophieRenderer,
32 themeSource,
33 ThemeSource,
34} from '@sophie/shared';
35
36export type MessageSender = (channel: RendererToMainIpcMessage, ...args: unknown[]) => void;
37
38export class SophieRendererImpl implements SophieRenderer {
39 readonly #send: MessageSender;
40
41 #listener: SharedStoreListener | null = null;
42
43 #snapshot: SharedStoreSnapshotIn | null = null;
44
45 constructor(send: MessageSender) {
46 this.#send = send;
47 }
48
49 sharedStoreSnapshotReceived(snapshot: unknown): void {
50 if (sharedStore.is(snapshot)) {
51 if (this.#listener === null) {
52 this.#snapshot = snapshot;
53 } else {
54 this.#listener.onSnapshot(snapshot);
55 }
56 } else {
57 console.error('Received invalid snapshot', snapshot);
58 this.#snapshot = null;
59 }
60 }
61
62 sharedStorePatchReceived(patch: unknown): void {
63 if (this.#listener !== null) {
64 // `mobx-state-tree` will validate the patch, so we can safely cast here.
65 this.#listener.onPatch(patch as IJsonPatch);
66 }
67 }
68
69 setSharedStoreListener(listener: SharedStoreListener): void {
70 this.#listener = listener;
71 if (this.#snapshot !== null) {
72 listener.onSnapshot(this.#snapshot);
73 this.#snapshot = null;
74 }
75 this.#send(RendererToMainIpcMessage.SharedStoreSnapshotRequest);
76 }
77
78 setBrowserViewBounds(bounds: BrowserViewBounds): void {
79 if (browserViewBounds.safeParse(bounds).success) {
80 this.#send(RendererToMainIpcMessage.SetBrowserViewBounds, bounds);
81 }
82 }
83
84 setThemeSource(mode: ThemeSource): void {
85 if (themeSource.safeParse(mode).success) {
86 this.#send(RendererToMainIpcMessage.SetThemeSource, mode);
87 }
88 }
89
90 reloadAllServices(): void {
91 this.#send(RendererToMainIpcMessage.ReloadAllServices);
92 }
93}
94
95export function createSophieRenderer(): SophieRenderer {
96 const impl = new SophieRendererImpl(ipcRenderer.send);
97
98 ipcRenderer.on(MainToRendererIpcMessage.SharedStoreSnapshot, (_event, snapshot) => {
99 impl.sharedStoreSnapshotReceived(snapshot);
100 });
101
102 ipcRenderer.on(MainToRendererIpcMessage.SharedStorePatch, (_event, patch) => {
103 impl.sharedStorePatchReceived(patch);
104 });
105
106 return {
107 setSharedStoreListener: impl.setSharedStoreListener.bind(impl),
108 setBrowserViewBounds: impl.setBrowserViewBounds.bind(impl),
109 setThemeSource: impl.setThemeSource.bind(impl),
110 reloadAllServices: impl.reloadAllServices.bind(impl),
111 };
112}
diff --git a/packages/preload/src/contextBridge/SophieRendererImpl.ts b/packages/preload/src/contextBridge/SophieRendererImpl.ts
new file mode 100644
index 0000000..5d29071
--- /dev/null
+++ b/packages/preload/src/contextBridge/SophieRendererImpl.ts
@@ -0,0 +1,74 @@
1/*
2 * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import type { IJsonPatch } from 'mobx-state-tree';
22import {
23 Action,
24 action,
25 sharedStore,
26 SharedStoreListener,
27 SophieRenderer,
28} from '@sophie/shared';
29
30import { RendererToMainIpcService } from '../services/RendererToMainIpcService';
31
32class SophieRendererImpl implements SophieRenderer {
33 readonly #ipcService: RendererToMainIpcService;
34
35 #listener: SharedStoreListener | null = null;
36
37 constructor(ipcService: RendererToMainIpcService) {
38 this.#ipcService = ipcService;
39 ipcService.onSharedStorePatch((patch) => {
40 // `mobx-state-tree` will validate the patch, so we can safely cast here.
41 this.#listener?.onPatch(patch as IJsonPatch);
42 });
43 }
44
45 onSharedStoreChange(listener: SharedStoreListener): void {
46 this.#ipcService.getSharedStoreSnapshot().then((snapshot) => {
47 if (sharedStore.is(snapshot)) {
48 listener.onSnapshot(snapshot);
49 this.#listener = listener;
50 } else {
51 console.error('Got invalid initial shared store snapshot', snapshot);
52 }
53 }).catch((err) => {
54 console.error('Failed set initial shared store snapshot', err);
55 });
56 }
57
58 dispatchAction(actionToDispatch: Action): void {
59 const parsedAction = action.safeParse(actionToDispatch);
60 if (parsedAction.success) {
61 this.#ipcService.dispatchAction(parsedAction.data);
62 } else {
63 console.error('Trying to dispatch invalid action', actionToDispatch, parsedAction.error);
64 }
65 }
66}
67
68export function createSophieRenderer(ipcService: RendererToMainIpcService): SophieRenderer {
69 const impl = new SophieRendererImpl(ipcService);
70 return {
71 onSharedStoreChange: impl.onSharedStoreChange.bind(impl),
72 dispatchAction: impl.dispatchAction.bind(impl),
73 };
74}
diff --git a/packages/preload/src/index.ts b/packages/preload/src/index.ts
index ef85f70..9336433 100644
--- a/packages/preload/src/index.ts
+++ b/packages/preload/src/index.ts
@@ -20,8 +20,9 @@
20 20
21import { contextBridge } from 'electron'; 21import { contextBridge } from 'electron';
22 22
23import { createSophieRenderer } from './SophieRendererImpl'; 23import { createSophieRenderer } from './contextBridge/SophieRendererImpl';
24import { RendererToMainIpcService } from './services/RendererToMainIpcService';
24 25
25const sophieRenderer = createSophieRenderer(); 26const sophieRenderer = createSophieRenderer(new RendererToMainIpcService());
26 27
27contextBridge.exposeInMainWorld('sophieRenderer', sophieRenderer); 28contextBridge.exposeInMainWorld('sophieRenderer', sophieRenderer);
diff --git a/packages/preload/src/services/RendererToMainIpcService.ts b/packages/preload/src/services/RendererToMainIpcService.ts
new file mode 100644
index 0000000..40f1339
--- /dev/null
+++ b/packages/preload/src/services/RendererToMainIpcService.ts
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { ipcRenderer } from 'electron';
22import {
23 Action,
24 MainToRendererIpcMessage,
25 RendererToMainIpcMessage,
26} from '@sophie/shared';
27
28export class RendererToMainIpcService {
29 getSharedStoreSnapshot(): Promise<unknown> {
30 return ipcRenderer.invoke(RendererToMainIpcMessage.GetSharedStoreSnapshot);
31 }
32
33 dispatchAction(actionToDispatch: Action): void {
34 ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, actionToDispatch);
35 }
36
37 onSharedStorePatch(callback: (patch: unknown) => void): void {
38 ipcRenderer.on(MainToRendererIpcMessage.SharedStorePatch, (_event, patch) => {
39 callback(patch);
40 })
41 }
42}