aboutsummaryrefslogtreecommitdiffstats
path: root/packages/renderer
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-20 18:06:23 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-08 21:43:15 +0100
commitd24734ac4cd1c6ddda5ba39f033ce9eaa4dcda01 (patch)
treeb696243e5bf35ee19237c149da3d779239e2a7d7 /packages/renderer
parentfix: Do not access localStorage in service-preload (diff)
downloadsophie-d24734ac4cd1c6ddda5ba39f033ce9eaa4dcda01.tar.gz
sophie-d24734ac4cd1c6ddda5ba39f033ce9eaa4dcda01.tar.zst
sophie-d24734ac4cd1c6ddda5ba39f033ce9eaa4dcda01.zip
feat: Add RuntimeService store
Stores transient state for services shared between the main and renderer processes. Signed-off-by: Kristóf Marussy <kristof@marussy.com>
Diffstat (limited to 'packages/renderer')
-rw-r--r--packages/renderer/jest.config.js7
-rw-r--r--packages/renderer/package.json5
-rw-r--r--packages/renderer/src/env/RendererEnv.ts27
-rw-r--r--packages/renderer/src/env/getEnv.ts (renamed from packages/renderer/src/stores/RendererEnv.ts)9
-rw-r--r--packages/renderer/src/env/impl/RendererEnvImpl.ts55
-rw-r--r--packages/renderer/src/env/impl/__tests__/RendererEnvImpl.spec.ts87
-rw-r--r--packages/renderer/src/stores/Config.ts32
-rw-r--r--packages/renderer/src/stores/RendererStore.ts21
-rw-r--r--packages/renderer/src/stores/Service.ts38
-rw-r--r--packages/renderer/src/stores/SharedStore.ts35
-rw-r--r--packages/renderer/src/stores/__tests__/Service.spec.ts63
-rw-r--r--packages/renderer/tsconfig.json3
12 files changed, 362 insertions, 20 deletions
diff --git a/packages/renderer/jest.config.js b/packages/renderer/jest.config.js
new file mode 100644
index 0000000..27af475
--- /dev/null
+++ b/packages/renderer/jest.config.js
@@ -0,0 +1,7 @@
1import rootConfig from '../../config/jest.config.base.js';
2
3/** @type {import('@jest/types').Config.InitialOptions} */
4export default {
5 ...rootConfig,
6 testEnvironment: 'jsdom',
7};
diff --git a/packages/renderer/package.json b/packages/renderer/package.json
index 9e84159..cfd6e4b 100644
--- a/packages/renderer/package.json
+++ b/packages/renderer/package.json
@@ -24,10 +24,15 @@
24 "react-dom": "^17.0.2" 24 "react-dom": "^17.0.2"
25 }, 25 },
26 "devDependencies": { 26 "devDependencies": {
27 "@jest/globals": "^27.4.6",
28 "@types/jest": "^27.4.0",
27 "@types/lodash-es": "^4.17.5", 29 "@types/lodash-es": "^4.17.5",
28 "@types/react": "^17.0.38", 30 "@types/react": "^17.0.38",
29 "@types/react-dom": "^17.0.11", 31 "@types/react-dom": "^17.0.11",
30 "@vitejs/plugin-react": "^1.1.4", 32 "@vitejs/plugin-react": "^1.1.4",
33 "jest": "^27.4.7",
34 "jest-mock": "^27.4.6",
35 "jsdom": "^19.0.0",
31 "mst-middlewares": "^5.1.0", 36 "mst-middlewares": "^5.1.0",
32 "remotedev": "^0.2.9", 37 "remotedev": "^0.2.9",
33 "vite": "^2.7.13" 38 "vite": "^2.7.13"
diff --git a/packages/renderer/src/env/RendererEnv.ts b/packages/renderer/src/env/RendererEnv.ts
new file mode 100644
index 0000000..5ca2978
--- /dev/null
+++ b/packages/renderer/src/env/RendererEnv.ts
@@ -0,0 +1,27 @@
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 { Action, RuntimeService } from '@sophie/shared';
22
23export default interface RendererEnv {
24 dispatchMainAction(action: Action): void;
25
26 getRuntimeService(serviceId: string): RuntimeService | undefined;
27}
diff --git a/packages/renderer/src/stores/RendererEnv.ts b/packages/renderer/src/env/getEnv.ts
index f0a5a51..4f7357a 100644
--- a/packages/renderer/src/stores/RendererEnv.ts
+++ b/packages/renderer/src/env/getEnv.ts
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com> 2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 * 3 *
4 * This file is part of Sophie. 4 * This file is part of Sophie.
5 * 5 *
@@ -18,12 +18,9 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import type { Action } from '@sophie/shared';
22import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree'; 21import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree';
23 22
24export default interface RendererEnv { 23import type RendererEnv from './RendererEnv';
25 dispatchMainAction(action: Action): void;
26}
27 24
28/** 25/**
29 * Gets a well-typed environment from `model`. 26 * Gets a well-typed environment from `model`.
@@ -32,6 +29,6 @@ export default interface RendererEnv {
32 * 29 *
33 * @param model The state tree node. 30 * @param model The state tree node.
34 */ 31 */
35export function getEnv(model: IAnyStateTreeNode): RendererEnv { 32export default function getEnv(model: IAnyStateTreeNode): RendererEnv {
36 return getAnyEnv<RendererEnv>(model); 33 return getAnyEnv<RendererEnv>(model);
37} 34}
diff --git a/packages/renderer/src/env/impl/RendererEnvImpl.ts b/packages/renderer/src/env/impl/RendererEnvImpl.ts
new file mode 100644
index 0000000..184d31b
--- /dev/null
+++ b/packages/renderer/src/env/impl/RendererEnvImpl.ts
@@ -0,0 +1,55 @@
1/*
2 * Copyright (C) 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 {
22 Action,
23 runtimeService,
24 RuntimeService,
25 SophieRenderer,
26} from '@sophie/shared';
27import type { IMSTMap } from 'mobx-state-tree';
28
29import type { RendererStore } from '../../stores/RendererStore';
30import type RendererEnv from '../RendererEnv';
31
32export default class RendererEnvImpl implements RendererEnv {
33 readonly #ipc: SophieRenderer;
34
35 #runtimeServices: IMSTMap<typeof runtimeService> | undefined;
36
37 constructor(ipc: SophieRenderer) {
38 this.#ipc = ipc;
39 }
40
41 setStore(store: RendererStore): void {
42 this.#runtimeServices = store.shared.runtimeServices;
43 }
44
45 dispatchMainAction(action: Action): void {
46 this.#ipc.dispatchAction(action);
47 }
48
49 getRuntimeService(serviceId: string): RuntimeService | undefined {
50 if (this.#runtimeServices === undefined) {
51 throw new Error('runtime services map is not yet set');
52 }
53 return this.#runtimeServices.get(serviceId);
54 }
55}
diff --git a/packages/renderer/src/env/impl/__tests__/RendererEnvImpl.spec.ts b/packages/renderer/src/env/impl/__tests__/RendererEnvImpl.spec.ts
new file mode 100644
index 0000000..d36462c
--- /dev/null
+++ b/packages/renderer/src/env/impl/__tests__/RendererEnvImpl.spec.ts
@@ -0,0 +1,87 @@
1/*
2 * Copyright (C) 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 { jest } from '@jest/globals';
22import {
23 Action,
24 runtimeService,
25 RuntimeService,
26 SophieRenderer,
27} from '@sophie/shared';
28
29import { rendererStore } from '../../../stores/RendererStore';
30import RendererEnvImpl from '../RendererEnvImpl';
31
32const ipc: SophieRenderer = {
33 dispatchAction: jest.fn(),
34 onSharedStoreChange: jest.fn(),
35};
36let sut: RendererEnvImpl;
37
38beforeEach(() => {
39 sut = new RendererEnvImpl(ipc);
40});
41
42describe('dispatchMainAction', () => {
43 it('should dispatch actions via the IPC', () => {
44 const action: Action = {
45 action: 'set-theme-source',
46 themeSource: 'dark',
47 };
48 sut.dispatchMainAction(action);
49 expect(ipc.dispatchAction).toHaveBeenCalledWith(action);
50 });
51});
52
53describe('getRuntimeService', () => {
54 describe('when no store was set', () => {
55 it('should throw an error', () => {
56 expect(() => sut.getRuntimeService('someId')).toThrow();
57 });
58 });
59
60 describe('when the store was set', () => {
61 let runtimeServiceStore: RuntimeService;
62
63 beforeEach(() => {
64 runtimeServiceStore = runtimeService.create({
65 state: 'loaded',
66 });
67 const store = rendererStore.create({
68 shared: {
69 runtimeServices: {
70 someId: runtimeServiceStore,
71 },
72 },
73 });
74 sut.setStore(store);
75 });
76
77 it('should return the runtime service for the given ID', () => {
78 const returnedStore = sut.getRuntimeService('someId');
79 expect(returnedStore).toBe(runtimeServiceStore);
80 });
81
82 it('should return undefined for an unknown ID', () => {
83 const returnedStore = sut.getRuntimeService('unknownId');
84 expect(returnedStore).toBeUndefined();
85 });
86 });
87});
diff --git a/packages/renderer/src/stores/Config.ts b/packages/renderer/src/stores/Config.ts
new file mode 100644
index 0000000..070c4ec
--- /dev/null
+++ b/packages/renderer/src/stores/Config.ts
@@ -0,0 +1,32 @@
1/*
2 * Copyright (C) 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 { config as originalConfig } from '@sophie/shared';
22import { Instance, types } from 'mobx-state-tree';
23
24import { service } from './Service';
25
26export const config = originalConfig.props({
27 services: types.array(service),
28});
29
30export interface Config extends Instance<typeof config> {}
31
32export type { ConfigSnapshotIn, ConfigSnapshotOut } from '@sophie/shared';
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts
index f1915c9..d0e7843 100644
--- a/packages/renderer/src/stores/RendererStore.ts
+++ b/packages/renderer/src/stores/RendererStore.ts
@@ -18,20 +18,16 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { 21import { BrowserViewBounds, SophieRenderer, ThemeSource } from '@sophie/shared';
22 BrowserViewBounds,
23 Config,
24 Service,
25 sharedStore,
26 SophieRenderer,
27 ThemeSource,
28} from '@sophie/shared';
29import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; 22import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree';
30 23
24import getEnv from '../env/getEnv';
25import RendererEnvImpl from '../env/impl/RendererEnvImpl';
31import { getLogger } from '../utils/log'; 26import { getLogger } from '../utils/log';
32 27
33import type RendererEnv from './RendererEnv'; 28import type { Config } from './Config';
34import { getEnv } from './RendererEnv'; 29import type { Service } from './Service';
30import { sharedStore } from './SharedStore';
35 31
36const log = getLogger('RendererStore'); 32const log = getLogger('RendererStore');
37 33
@@ -82,10 +78,9 @@ export interface RendererStore extends Instance<typeof rendererStore> {}
82export function createAndConnectRendererStore( 78export function createAndConnectRendererStore(
83 ipc: SophieRenderer, 79 ipc: SophieRenderer,
84): RendererStore { 80): RendererStore {
85 const env: RendererEnv = { 81 const env = new RendererEnvImpl(ipc);
86 dispatchMainAction: ipc.dispatchAction,
87 };
88 const store = rendererStore.create({}, env); 82 const store = rendererStore.create({}, env);
83 env.setStore(store);
89 84
90 ipc 85 ipc
91 .onSharedStoreChange({ 86 .onSharedStoreChange({
diff --git a/packages/renderer/src/stores/Service.ts b/packages/renderer/src/stores/Service.ts
new file mode 100644
index 0000000..2f45106
--- /dev/null
+++ b/packages/renderer/src/stores/Service.ts
@@ -0,0 +1,38 @@
1/*
2 * Copyright (C) 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 {
22 runtimeService,
23 RuntimeService,
24 service as originalService,
25} from '@sophie/shared';
26import { Instance } from 'mobx-state-tree';
27
28import getEnv from '../env/getEnv';
29
30export const service = originalService.views((self) => ({
31 get runtime(): RuntimeService {
32 return getEnv(self).getRuntimeService(self.id) ?? runtimeService.create();
33 },
34}));
35
36export interface Service extends Instance<typeof service> {}
37
38export type { ServiceSnapshotIn, ServiceSnapshotOut } from '@sophie/shared';
diff --git a/packages/renderer/src/stores/SharedStore.ts b/packages/renderer/src/stores/SharedStore.ts
new file mode 100644
index 0000000..baaf061
--- /dev/null
+++ b/packages/renderer/src/stores/SharedStore.ts
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 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 { sharedStore as originalSharedStore } from '@sophie/shared';
22import { Instance, types } from 'mobx-state-tree';
23
24import { config } from './Config';
25
26export const sharedStore = originalSharedStore.props({
27 config: types.optional(config, {}),
28});
29
30export interface SharedStore extends Instance<typeof sharedStore> {}
31
32export type {
33 SharedStoreSnapshotIn,
34 SharedStoreSnapshotOut,
35} from '@sophie/shared';
diff --git a/packages/renderer/src/stores/__tests__/Service.spec.ts b/packages/renderer/src/stores/__tests__/Service.spec.ts
new file mode 100644
index 0000000..f835d41
--- /dev/null
+++ b/packages/renderer/src/stores/__tests__/Service.spec.ts
@@ -0,0 +1,63 @@
1/*
2 * Copyright (C) 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 { jest } from '@jest/globals';
22import { runtimeService } from '@sophie/shared';
23import { mocked } from 'jest-mock';
24
25import type RendererEnv from '../../env/RendererEnv';
26import { service, Service } from '../Service';
27
28const env: RendererEnv = {
29 dispatchMainAction: jest.fn(),
30 getRuntimeService: jest.fn(),
31};
32let sut: Service;
33
34beforeEach(() => {
35 sut = service.create(
36 {
37 id: 'serviceId',
38 name: 'Foo',
39 url: 'https://example.com',
40 profile: 'profileId',
41 },
42 env,
43 );
44});
45
46describe('runtime', () => {
47 it('should return the runtime service with for the service ID', () => {
48 const runtimeServiceStore = runtimeService.create({}, env);
49 mocked(env.getRuntimeService).mockReturnValueOnce(runtimeServiceStore);
50 const returnedStore = sut.runtime;
51 expect(env.getRuntimeService).toHaveBeenCalledWith('serviceId');
52 expect(returnedStore).toBe(runtimeServiceStore);
53 });
54
55 it('should return a valid runtime service even if none exists in the environment', () => {
56 /*
57 eslint-disable-next-line unicorn/no-useless-undefined --
58 `mockReturnValueOnce` expects 1 parameter.
59 */
60 mocked(env.getRuntimeService).mockReturnValueOnce(undefined);
61 expect(sut.runtime).toHaveProperty('state', 'hibernated');
62 });
63});
diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json
index 5453330..4fe3896 100644
--- a/packages/renderer/tsconfig.json
+++ b/packages/renderer/tsconfig.json
@@ -4,7 +4,7 @@
4 "noEmit": true, 4 "noEmit": true,
5 "jsx": "react", 5 "jsx": "react",
6 "lib": ["dom", "dom.iterable", "esnext"], 6 "lib": ["dom", "dom.iterable", "esnext"],
7 "types": ["vite/client"] 7 "types": ["@types/jest", "vite/client"]
8 }, 8 },
9 "references": [ 9 "references": [
10 { 10 {
@@ -16,6 +16,7 @@
16 "src/**/*.tsx", 16 "src/**/*.tsx",
17 "types/**/*.d.ts", 17 "types/**/*.d.ts",
18 ".eslintrc.cjs", 18 ".eslintrc.cjs",
19 "jest.config.js",
19 "vite.config.js" 20 "vite.config.js"
20 ] 21 ]
21} 22}