From d07e7b834831230b53860d0919a68edc2d36193d Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 8 Jan 2022 21:36:43 +0100 Subject: build: Eslint fixes for multi-module project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristóf Marussy --- .../src/contextBridge/createSophieRenderer.ts | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 packages/preload/src/contextBridge/createSophieRenderer.ts (limited to 'packages/preload/src/contextBridge/createSophieRenderer.ts') diff --git a/packages/preload/src/contextBridge/createSophieRenderer.ts b/packages/preload/src/contextBridge/createSophieRenderer.ts new file mode 100644 index 0000000..2055080 --- /dev/null +++ b/packages/preload/src/contextBridge/createSophieRenderer.ts @@ -0,0 +1,96 @@ +/* + * 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 { + Action, + action, + MainToRendererIpcMessage, + RendererToMainIpcMessage, + sharedStore, + SharedStoreListener, + SophieRenderer, +} from '@sophie/shared'; +import { ipcRenderer } from 'electron'; +import log from 'loglevel'; +import type { IJsonPatch } from 'mobx-state-tree'; + +class SharedStoreConnector { + private onSharedStoreChangeCalled = false; + + private listener: SharedStoreListener | null = null; + + constructor(private readonly allowReplaceListener: boolean) { + ipcRenderer.on(MainToRendererIpcMessage.SharedStorePatch, (_event, patch) => { + try { + // `mobx-state-tree` will validate the patch, so we can safely cast here. + this.listener?.onPatch(patch as IJsonPatch); + } catch (err) { + log.error('Shared store listener onPatch failed', err); + this.listener = null; + } + }); + } + + async onSharedStoreChange(listener: SharedStoreListener): Promise { + if (this.onSharedStoreChangeCalled && !this.allowReplaceListener) { + throw new Error('Shared store change listener was already set'); + } + this.onSharedStoreChangeCalled = true; + let success = false; + let snapshot: unknown | null = null; + try { + snapshot = await ipcRenderer.invoke(RendererToMainIpcMessage.GetSharedStoreSnapshot); + success = true; + } catch (err) { + log.error('Failed to get initial shared store snapshot', err); + } + if (success) { + if (sharedStore.is(snapshot)) { + listener.onSnapshot(snapshot); + this.listener = listener; + return; + } + log.error('Got invalid initial shared store snapshot', snapshot); + } + throw new Error('Failed to connect to shared store'); + } +} + +function dispatchAction(actionToDispatch: Action): void { + // Let the full zod parse error bubble up to the main world, + // since all data it may contain was provided from the main world. + const parsedAction = action.parse(actionToDispatch); + try { + ipcRenderer.send(RendererToMainIpcMessage.DispatchAction, parsedAction); + } catch (err) { + // Do not leak IPC failure details into the main world. + const message = 'Failed to dispatch action'; + log.error(message, actionToDispatch, err); + throw new Error(message); + } +} + +export default function createSophieRenderer(allowReplaceListener: boolean): SophieRenderer { + const connector = new SharedStoreConnector(allowReplaceListener); + return { + onSharedStoreChange: connector.onSharedStoreChange.bind(connector), + dispatchAction, + }; +} -- cgit v1.2.3-54-g00ecf