aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-23 13:40:47 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-23 15:48:30 +0100
commit950fb9be8061e2a26e0536b98c6a3ee230618f54 (patch)
treeb136dcc9add0d268a2e7a6288ec934a86d03b652 /packages
parentfeat: Add shared package for electron ipc (diff)
downloadsophie-950fb9be8061e2a26e0536b98c6a3ee230618f54.tar.gz
sophie-950fb9be8061e2a26e0536b98c6a3ee230618f54.tar.zst
sophie-950fb9be8061e2a26e0536b98c6a3ee230618f54.zip
feat: Main to renderer store synchronization
Patches are send in one direction only, from the main to the renderer, so all actions have to go through the context bridge and the renderer IPC to modify the store in the renderer. This makes the store in the main process a single source of truth, which simplifies debugging and state persistence. The store in the renderer is connected to redux devtools for inspection, but playing back the state in the devtools won't change the sotre in main process.
Diffstat (limited to 'packages')
-rw-r--r--packages/main/package.json6
-rw-r--r--packages/main/src/devTools.ts49
-rw-r--r--packages/main/src/index.ts66
-rw-r--r--packages/main/src/stores/RootStore.ts12
-rw-r--r--packages/preload/package.json4
-rw-r--r--packages/preload/src/SophieRendererImpl.ts74
-rw-r--r--packages/preload/src/index.ts11
-rw-r--r--packages/renderer/package.json6
-rw-r--r--packages/renderer/src/devTools.ts37
-rw-r--r--packages/renderer/src/index.tsx23
-rw-r--r--packages/renderer/tsconfig.json3
-rw-r--r--packages/renderer/vite.config.js3
-rw-r--r--packages/shared/package.json5
-rw-r--r--packages/shared/src/contextBridge/SophieRenderer.ts6
-rw-r--r--packages/shared/src/index.ts11
-rw-r--r--packages/shared/src/ipc/MainToRendererIpcMessage.ts4
-rw-r--r--packages/shared/src/ipc/RendererIpcMessage.ts3
-rw-r--r--packages/shared/src/ipc/RendererToMainIpcMessage.ts4
-rw-r--r--packages/shared/src/stores/SharedStore.ts23
-rw-r--r--packages/shared/vite.config.js8
20 files changed, 302 insertions, 56 deletions
diff --git a/packages/main/package.json b/packages/main/package.json
index 63da432..22afafa 100644
--- a/packages/main/package.json
+++ b/packages/main/package.json
@@ -9,11 +9,13 @@
9 }, 9 },
10 "dependencies": { 10 "dependencies": {
11 "@sophie/shared": "workspace:*", 11 "@sophie/shared": "workspace:*",
12 "electron": "^16.0.5" 12 "electron": "^16.0.5",
13 "mobx": "^6.3.10",
14 "mobx-state-tree": "^5.1.0"
13 }, 15 },
14 "devDependencies": { 16 "devDependencies": {
15 "@types/electron-devtools-installer": "^2.2.0", 17 "@types/electron-devtools-installer": "^2.2.0",
16 "@types/node": "^16.11.15", 18 "@types/node": "^16.11.17",
17 "electron-devtools-installer": "^3.2.0", 19 "electron-devtools-installer": "^3.2.0",
18 "typescript": "^4.5.4", 20 "typescript": "^4.5.4",
19 "vite": "^2.7.6" 21 "vite": "^2.7.6"
diff --git a/packages/main/src/devTools.ts b/packages/main/src/devTools.ts
new file mode 100644
index 0000000..d02bfbf
--- /dev/null
+++ b/packages/main/src/devTools.ts
@@ -0,0 +1,49 @@
1import type { App, BrowserWindow } from 'electron';
2
3/**
4 * Installs the react and redux developer tools extensions.
5 *
6 * We use the redux devtools and connect the mobx store to it with `mst-middlewares`,
7 * because the mobx-state-tree devtools are currently unmaintained.
8 *
9 * @param app The electron application instance.
10 */
11export function installDevToolsExtensions(app: App): void {
12 app.whenReady().then(async () => {
13 const {
14 default: installExtension,
15 REACT_DEVELOPER_TOOLS,
16 REDUX_DEVTOOLS,
17 } = await import('electron-devtools-installer');
18 installExtension(
19 [
20 REACT_DEVELOPER_TOOLS,
21 REDUX_DEVTOOLS,
22 ],
23 {
24 forceDownload: false,
25 loadExtensionOptions: {
26 allowFileAccess: true,
27 },
28 },
29 );
30 }).catch((err) => {
31 console.error('Failed to install devtools extension', err);
32 });
33}
34
35/**
36 * Opens the developer tools while applying a workaround to enable the redux devtools.
37 *
38 * @param browserWindow The browser window to open the devtools in.
39 * @see https://github.com/MarshallOfSound/electron-devtools-installer/issues/195#issuecomment-998872878
40 */
41export function openDevToolsWhenReady(browserWindow: BrowserWindow): void {
42 const { webContents } = browserWindow;
43 webContents.once('dom-ready', () => {
44 webContents.once('devtools-opened', () => {
45 browserWindow?.focus();
46 });
47 webContents.openDevTools();
48 });
49}
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index cd04276..dbe6890 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -1,8 +1,18 @@
1import { app, BrowserWindow } from 'electron'; 1import { app, BrowserWindow } from 'electron';
2import { getSnapshot, onPatch } from 'mobx-state-tree';
2import { join } from 'path'; 3import { join } from 'path';
3import { RendererIpcMessage } from '@sophie/shared'; 4import {
5 MainToRendererIpcMessage,
6 RendererToMainIpcMessage,
7} from '@sophie/shared';
4import { URL } from 'url'; 8import { URL } from 'url';
5 9
10import {
11 installDevToolsExtensions,
12 openDevToolsWhenReady,
13} from './devTools';
14import { rootStore } from './stores/RootStore';
15
6const isSingleInstance = app.requestSingleInstanceLock(); 16const isSingleInstance = app.requestSingleInstanceLock();
7const isDevelopment = import.meta.env.MODE === 'development'; 17const isDevelopment = import.meta.env.MODE === 'development';
8 18
@@ -14,62 +24,46 @@ if (!isSingleInstance) {
14app.enableSandbox(); 24app.enableSandbox();
15 25
16if (isDevelopment) { 26if (isDevelopment) {
17 app.whenReady().then(async () => { 27 installDevToolsExtensions(app);
18 const {
19 default: installExtension,
20 MOBX_DEVTOOLS,
21 REACT_DEVELOPER_TOOLS,
22 } = await import('electron-devtools-installer');
23 installExtension(
24 [
25 MOBX_DEVTOOLS,
26 REACT_DEVELOPER_TOOLS,
27 ],
28 {
29 forceDownload: false,
30 loadExtensionOptions: {
31 allowFileAccess: true,
32 },
33 },
34 );
35 }).catch((err) => {
36 console.error('Failed to install devtools extension', err);
37 });
38} 28}
39 29
40let mainWindow: BrowserWindow | null = null; 30let mainWindow: BrowserWindow | null = null;
41 31
32const store = rootStore.create({
33 shared: {
34 clickCount: 1,
35 },
36});
37
42function createWindow(): Promise<void> { 38function createWindow(): Promise<void> {
43 mainWindow = new BrowserWindow({ 39 mainWindow = new BrowserWindow({
44 show: false, 40 show: false,
41 autoHideMenuBar: true,
45 webPreferences: { 42 webPreferences: {
46 nativeWindowOpen: true, 43 nativeWindowOpen: true,
47 webviewTag: false, 44 webviewTag: false,
48 sandbox: true, 45 sandbox: true,
49 preload: join(__dirname, '../../preload/dist/index.cjs'), 46 preload: join(__dirname, '../../preload/dist/index.cjs'),
50 } 47 },
51 }); 48 });
52 49
53 const { webContents } = mainWindow;
54
55 // See https://github.com/MarshallOfSound/electron-devtools-installer/issues/195#issuecomment-998872878
56 if (isDevelopment) { 50 if (isDevelopment) {
57 webContents.once('dom-ready', () => { 51 openDevToolsWhenReady(mainWindow);
58 webContents.once('devtools-opened', () => {
59 mainWindow?.focus();
60 });
61 webContents.openDevTools();
62 });
63 } 52 }
64 53
65 mainWindow.on('ready-to-show', () => { 54 mainWindow.on('ready-to-show', () => {
66 mainWindow?.show(); 55 mainWindow?.show();
67 }); 56 });
68 57
58 const { webContents } = mainWindow;
59
69 webContents.on('ipc-message', (_event, channel, ...args) => { 60 webContents.on('ipc-message', (_event, channel, ...args) => {
70 switch (channel) { 61 switch (channel) {
71 case RendererIpcMessage.ButtonClicked: 62 case RendererToMainIpcMessage.SharedStoreSnapshotRequest:
72 console.log('Button clicked'); 63 webContents.send(MainToRendererIpcMessage.SharedStoreSnapshot, getSnapshot(store.shared));
64 break;
65 case RendererToMainIpcMessage.ButtonClick:
66 store.buttonClick();
73 break; 67 break;
74 default: 68 default:
75 console.warn('Unknown IPC message:', channel, args); 69 console.warn('Unknown IPC message:', channel, args);
@@ -77,6 +71,10 @@ function createWindow(): Promise<void> {
77 } 71 }
78 }); 72 });
79 73
74 onPatch(store.shared, (patch) => {
75 webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch);
76 });
77
80 const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined) 78 const pageUrl = (isDevelopment && import.meta.env.VITE_DEV_SERVER_URL !== undefined)
81 ? import.meta.env.VITE_DEV_SERVER_URL 79 ? import.meta.env.VITE_DEV_SERVER_URL
82 : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString(); 80 : new URL('../renderer/dist/index.html', `file://${__dirname}`).toString();
diff --git a/packages/main/src/stores/RootStore.ts b/packages/main/src/stores/RootStore.ts
new file mode 100644
index 0000000..a9c1290
--- /dev/null
+++ b/packages/main/src/stores/RootStore.ts
@@ -0,0 +1,12 @@
1import { Instance, types } from 'mobx-state-tree';
2import { sharedStore } from '@sophie/shared';
3
4export const rootStore = types.model('RootStore', {
5 shared: sharedStore,
6}).actions((self) => ({
7 buttonClick() {
8 self.shared.clickCount += 1;
9 },
10}));
11
12export interface RootStore extends Instance<typeof rootStore> {}
diff --git a/packages/preload/package.json b/packages/preload/package.json
index 5556c10..2ad215c 100644
--- a/packages/preload/package.json
+++ b/packages/preload/package.json
@@ -10,7 +10,9 @@
10 }, 10 },
11 "dependencies": { 11 "dependencies": {
12 "@sophie/shared": "workspace:*", 12 "@sophie/shared": "workspace:*",
13 "electron": "^16.0.5" 13 "electron": "^16.0.5",
14 "mobx": "^6.3.10",
15 "mobx-state-tree": "^5.1.0"
14 }, 16 },
15 "devDependencies": { 17 "devDependencies": {
16 "typescript": "^4.5.4", 18 "typescript": "^4.5.4",
diff --git a/packages/preload/src/SophieRendererImpl.ts b/packages/preload/src/SophieRendererImpl.ts
new file mode 100644
index 0000000..e03c89e
--- /dev/null
+++ b/packages/preload/src/SophieRendererImpl.ts
@@ -0,0 +1,74 @@
1import { ipcRenderer } from 'electron';
2import type { IJsonPatch } from 'mobx-state-tree';
3import {
4 MainToRendererIpcMessage,
5 RendererToMainIpcMessage,
6 sharedStore,
7 SharedStoreListener,
8 SharedStoreSnapshotIn,
9 SophieRenderer
10} from '@sophie/shared';
11
12export type MessageSender = (channel: RendererToMainIpcMessage, ...args: unknown[]) => void;
13
14export class SophieRendererImpl implements SophieRenderer {
15 readonly #send: MessageSender;
16
17 #listener: SharedStoreListener | null = null;
18
19 #snapshot: SharedStoreSnapshotIn | null = null;
20
21 constructor(send: MessageSender) {
22 this.#send = send;
23 }
24
25 sharedStoreSnapshotReceived(snapshot: unknown): void {
26 if (sharedStore.is(snapshot)) {
27 if (this.#listener === null) {
28 this.#snapshot = snapshot;
29 } else {
30 this.#listener.onSnapshot(snapshot);
31 }
32 } else {
33 console.error('Received invalid snapshot', snapshot);
34 this.#snapshot = null;
35 }
36 }
37
38 sharedStorePatchReceived(patch: unknown): void {
39 if (this.#listener !== null) {
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 setSharedStoreListener(listener: SharedStoreListener): void {
46 this.#listener = listener;
47 if (this.#snapshot !== null) {
48 listener.onSnapshot(this.#snapshot);
49 this.#snapshot = null;
50 }
51 this.#send(RendererToMainIpcMessage.SharedStoreSnapshotRequest);
52 }
53
54 buttonClick(): void {
55 this.#send(RendererToMainIpcMessage.ButtonClick);
56 }
57}
58
59export function createSophieRenderer(): SophieRenderer {
60 const impl = new SophieRendererImpl(ipcRenderer.send);
61
62 ipcRenderer.on(MainToRendererIpcMessage.SharedStoreSnapshot, (_event, snapshot) => {
63 impl.sharedStoreSnapshotReceived(snapshot);
64 });
65
66 ipcRenderer.on(MainToRendererIpcMessage.SharedStorePatch, (_event, patch) => {
67 impl.sharedStorePatchReceived(patch);
68 });
69
70 return {
71 setSharedStoreListener: impl.setSharedStoreListener.bind(impl),
72 buttonClick: impl.buttonClick.bind(impl),
73 };
74}
diff --git a/packages/preload/src/index.ts b/packages/preload/src/index.ts
index 830901a..19d1285 100644
--- a/packages/preload/src/index.ts
+++ b/packages/preload/src/index.ts
@@ -1,10 +1,7 @@
1import { contextBridge, ipcRenderer } from 'electron'; 1import { contextBridge } from 'electron';
2import { RendererIpcMessage, SophieRenderer } from '@sophie/shared';
3 2
4const sophieRenderer: SophieRenderer = { 3import { createSophieRenderer } from './SophieRendererImpl';
5 buttonClicked() { 4
6 ipcRenderer.send(RendererIpcMessage.ButtonClicked); 5const sophieRenderer = createSophieRenderer();
7 }
8};
9 6
10contextBridge.exposeInMainWorld('sophieRenderer', sophieRenderer); 7contextBridge.exposeInMainWorld('sophieRenderer', sophieRenderer);
diff --git a/packages/renderer/package.json b/packages/renderer/package.json
index 559e668..ee627b4 100644
--- a/packages/renderer/package.json
+++ b/packages/renderer/package.json
@@ -14,13 +14,17 @@
14 "@mui/icons-material": "^5.2.5", 14 "@mui/icons-material": "^5.2.5",
15 "@mui/material": "^5.2.5", 15 "@mui/material": "^5.2.5",
16 "@sophie/shared": "workspace:*", 16 "@sophie/shared": "workspace:*",
17 "mobx": "^6.3.10",
18 "mobx-state-tree": "^5.1.0",
17 "react": "^17.0.2", 19 "react": "^17.0.2",
18 "react-dom": "^17.0.2" 20 "react-dom": "^17.0.2"
19 }, 21 },
20 "devDependencies": { 22 "devDependencies": {
21 "@types/react": "^17.0.37", 23 "@types/react": "^17.0.38",
22 "@types/react-dom": "^17.0.11", 24 "@types/react-dom": "^17.0.11",
23 "@vitejs/plugin-react": "^1.1.3", 25 "@vitejs/plugin-react": "^1.1.3",
26 "mst-middlewares": "^5.1.0",
27 "remotedev": "^0.2.9",
24 "typescript": "^4.5.4", 28 "typescript": "^4.5.4",
25 "vite": "^2.7.6" 29 "vite": "^2.7.6"
26 } 30 }
diff --git a/packages/renderer/src/devTools.ts b/packages/renderer/src/devTools.ts
new file mode 100644
index 0000000..5930c48
--- /dev/null
+++ b/packages/renderer/src/devTools.ts
@@ -0,0 +1,37 @@
1import type { IAnyStateTreeNode } from 'mobx-state-tree';
2
3/**
4 * Connects the `model` to the redux devtools extension after loading the required
5 * dependencies asynchronously.
6 *
7 * We have to apply a workaround to load `remotedev` by shimming the `global` object,
8 * because `remotedev` uses an old version of `socketcluster-client` that refers to
9 * `global` instead of `globalThis`.
10 *
11 * Due to the old dependencies, this function is not safe to call in production.
12 * However, we don't bundle `remotedev` in production, so the call would fail anyways.
13 *
14 * @param model The store to connect to the redux devtools.
15 * @see https://github.com/SocketCluster/socketcluster-client/issues/118#issuecomment-469064682
16 */
17async function exposeToReduxDevtoolsAsync(model: IAnyStateTreeNode): Promise<void> {
18 (window as { global?: unknown }).global = window;
19
20 const [remotedev, { connectReduxDevtools }] = await Promise.all([
21 // @ts-ignore
22 import('remotedev'),
23 import('mst-middlewares'),
24 ]);
25 connectReduxDevtools(remotedev, model);
26}
27
28/**
29 * Connects the `model` to the redux devtools extension.
30 *
31 * @param model The store to connect to the redux devtools.
32 */
33export function exposeToReduxDevtools(model: IAnyStateTreeNode): void {
34 exposeToReduxDevtoolsAsync(model).catch((err) => {
35 console.error('Could not connect to Redux devtools', err);
36 });
37}
diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx
index 452448c..37daaa6 100644
--- a/packages/renderer/src/index.tsx
+++ b/packages/renderer/src/index.tsx
@@ -2,6 +2,7 @@ import '@fontsource/roboto/300.css';
2import '@fontsource/roboto/400.css'; 2import '@fontsource/roboto/400.css';
3import '@fontsource/roboto/500.css'; 3import '@fontsource/roboto/500.css';
4import '@fontsource/roboto/700.css'; 4import '@fontsource/roboto/700.css';
5import { applyPatch, applySnapshot } from 'mobx-state-tree';
5import Button from "@mui/material/Button"; 6import Button from "@mui/material/Button";
6import CssBaseline from "@mui/material/CssBaseline"; 7import CssBaseline from "@mui/material/CssBaseline";
7import { 8import {
@@ -10,6 +11,26 @@ import {
10} from '@mui/material/styles'; 11} from '@mui/material/styles';
11import React from 'react'; 12import React from 'react';
12import { render } from 'react-dom'; 13import { render } from 'react-dom';
14import { sharedStore } from '@sophie/shared';
15
16import { exposeToReduxDevtools } from './devTools';
17
18const isDevelopment = import.meta.env.MODE === 'development';
19
20const store = sharedStore.create();
21
22if (isDevelopment) {
23 exposeToReduxDevtools(store);
24}
25
26window.sophieRenderer.setSharedStoreListener({
27 onSnapshot(snapshot) {
28 applySnapshot(store, snapshot);
29 },
30 onPatch(patch) {
31 applyPatch(store, patch);
32 },
33});
13 34
14const theme = createTheme({ 35const theme = createTheme({
15 palette: { 36 palette: {
@@ -24,7 +45,7 @@ function App() {
24 <CssBaseline enableColorScheme /> 45 <CssBaseline enableColorScheme />
25 <Button 46 <Button
26 variant="contained" 47 variant="contained"
27 onClick={window.sophieRenderer.buttonClicked} 48 onClick={window.sophieRenderer.buttonClick}
28 > 49 >
29 Hello Sophie! 50 Hello Sophie!
30 </Button> 51 </Button>
diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json
index 668356c..8746462 100644
--- a/packages/renderer/tsconfig.json
+++ b/packages/renderer/tsconfig.json
@@ -7,6 +7,9 @@
7 "dom", 7 "dom",
8 "dom.iterable", 8 "dom.iterable",
9 "esnext" 9 "esnext"
10 ],
11 "types": [
12 "vite/client"
10 ] 13 ]
11 }, 14 },
12 "references": [ 15 "references": [
diff --git a/packages/renderer/vite.config.js b/packages/renderer/vite.config.js
index ff34b6d..87c2d0c 100644
--- a/packages/renderer/vite.config.js
+++ b/packages/renderer/vite.config.js
@@ -3,7 +3,6 @@
3/* eslint-env node */ 3/* eslint-env node */
4 4
5import { builtinModules } from 'module'; 5import { builtinModules } from 'module';
6import { join } from 'path';
7import react from '@vitejs/plugin-react'; 6import react from '@vitejs/plugin-react';
8 7
9// `resolveJsonModule` is disabled for this package, but vite will load the json nevertheless. 8// `resolveJsonModule` is disabled for this package, but vite will load the json nevertheless.
@@ -36,6 +35,8 @@ const config = {
36 assetsDir: '.', 35 assetsDir: '.',
37 rollupOptions: { 36 rollupOptions: {
38 external: [ 37 external: [
38 'mst-middlewares',
39 'remotedev',
39 ...builtinModules, 40 ...builtinModules,
40 ], 41 ],
41 }, 42 },
diff --git a/packages/shared/package.json b/packages/shared/package.json
index ccde705..f49be47 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -3,6 +3,7 @@
3 "version": "0.1.0", 3 "version": "0.1.0",
4 "private": true, 4 "private": true,
5 "main": "dist/index.cjs", 5 "main": "dist/index.cjs",
6 "module": "dist/index.es.js",
6 "types": "dist/index.d.ts", 7 "types": "dist/index.d.ts",
7 "scripts": { 8 "scripts": {
8 "build": "vite build", 9 "build": "vite build",
@@ -11,5 +12,9 @@
11 "devDependencies": { 12 "devDependencies": {
12 "typescript": "^4.5.4", 13 "typescript": "^4.5.4",
13 "vite": "^2.7.6" 14 "vite": "^2.7.6"
15 },
16 "dependencies": {
17 "mobx": "^6.3.10",
18 "mobx-state-tree": "^5.1.0"
14 } 19 }
15} 20}
diff --git a/packages/shared/src/contextBridge/SophieRenderer.ts b/packages/shared/src/contextBridge/SophieRenderer.ts
index 91a0a3c..c878595 100644
--- a/packages/shared/src/contextBridge/SophieRenderer.ts
+++ b/packages/shared/src/contextBridge/SophieRenderer.ts
@@ -1,3 +1,7 @@
1import { SharedStoreListener } from '../stores/SharedStore';
2
1export interface SophieRenderer { 3export interface SophieRenderer {
2 buttonClicked(): void; 4 setSharedStoreListener(listener: SharedStoreListener): void;
5
6 buttonClick(): void;
3} 7}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index f95675d..7f192bd 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,3 +1,12 @@
1export type { SophieRenderer } from './contextBridge/SophieRenderer'; 1export type { SophieRenderer } from './contextBridge/SophieRenderer';
2 2
3export { RendererIpcMessage } from './ipc/RendererIpcMessage'; 3export { MainToRendererIpcMessage } from './ipc/MainToRendererIpcMessage';
4export { RendererToMainIpcMessage } from './ipc/RendererToMainIpcMessage';
5
6export type {
7 SharedStore,
8 SharedStoreListener,
9 SharedStoreSnapshotIn,
10 SharedStoreSnapshotOut,
11} from './stores/SharedStore';
12export { sharedStore } from './stores/SharedStore';
diff --git a/packages/shared/src/ipc/MainToRendererIpcMessage.ts b/packages/shared/src/ipc/MainToRendererIpcMessage.ts
new file mode 100644
index 0000000..2ddca80
--- /dev/null
+++ b/packages/shared/src/ipc/MainToRendererIpcMessage.ts
@@ -0,0 +1,4 @@
1export enum MainToRendererIpcMessage {
2 SharedStoreSnapshot = "sophie-shared-store-snapshot",
3 SharedStorePatch = "sophie-shared-store-patch",
4}
diff --git a/packages/shared/src/ipc/RendererIpcMessage.ts b/packages/shared/src/ipc/RendererIpcMessage.ts
deleted file mode 100644
index cfb87ae..0000000
--- a/packages/shared/src/ipc/RendererIpcMessage.ts
+++ /dev/null
@@ -1,3 +0,0 @@
1export enum RendererIpcMessage {
2 ButtonClicked = "button-clicked"
3}
diff --git a/packages/shared/src/ipc/RendererToMainIpcMessage.ts b/packages/shared/src/ipc/RendererToMainIpcMessage.ts
new file mode 100644
index 0000000..c4c682d
--- /dev/null
+++ b/packages/shared/src/ipc/RendererToMainIpcMessage.ts
@@ -0,0 +1,4 @@
1export enum RendererToMainIpcMessage {
2 SharedStoreSnapshotRequest = "sophie-shared-store-snapshot-request",
3 ButtonClick = "sophie-button-click"
4}
diff --git a/packages/shared/src/stores/SharedStore.ts b/packages/shared/src/stores/SharedStore.ts
new file mode 100644
index 0000000..8933bca
--- /dev/null
+++ b/packages/shared/src/stores/SharedStore.ts
@@ -0,0 +1,23 @@
1import {
2 IJsonPatch,
3 Instance,
4 types,
5 SnapshotIn,
6 SnapshotOut,
7} from 'mobx-state-tree';
8
9export const sharedStore = types.model("SharedStore", {
10 clickCount: 0
11});
12
13export interface SharedStore extends Instance<typeof sharedStore> {}
14
15export interface SharedStoreSnapshotIn extends SnapshotIn<typeof sharedStore> {}
16
17export interface SharedStoreSnapshotOut extends SnapshotOut<typeof sharedStore> {}
18
19export interface SharedStoreListener {
20 onSnapshot(snapshot: SharedStoreSnapshotIn): void;
21
22 onPatch(patch: IJsonPatch): void;
23}
diff --git a/packages/shared/vite.config.js b/packages/shared/vite.config.js
index cbebb8a..05ec118 100644
--- a/packages/shared/vite.config.js
+++ b/packages/shared/vite.config.js
@@ -26,15 +26,15 @@ const config = {
26 outDir: 'dist', 26 outDir: 'dist',
27 lib: { 27 lib: {
28 entry: 'src/index.ts', 28 entry: 'src/index.ts',
29 formats: ['cjs'], 29 formats: ['cjs', 'es'],
30 fileName: (format) => format === 'cjs' ? 'index.cjs' : `index.${format}.js`,
30 }, 31 },
31 rollupOptions: { 32 rollupOptions: {
32 external: [ 33 external: [
34 'mobx',
35 'mobx-state-tree',
33 ...builtinModules, 36 ...builtinModules,
34 ], 37 ],
35 output: {
36 entryFileNames: '[name].cjs',
37 },
38 }, 38 },
39 emptyOutDir: false, // Do not remove `.d.ts` files. 39 emptyOutDir: false, // Do not remove `.d.ts` files.
40 brotliSize: false, 40 brotliSize: false,