aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--packages/main/src/stores/RuntimeService.ts74
-rw-r--r--packages/main/src/stores/SharedStore.ts2
-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
-rw-r--r--packages/shared/src/index.ts7
-rw-r--r--packages/shared/src/stores/RuntimeService.ts47
-rw-r--r--packages/shared/src/stores/SharedStore.ts2
-rw-r--r--yarn.lock5
18 files changed, 499 insertions, 20 deletions
diff --git a/packages/main/src/stores/RuntimeService.ts b/packages/main/src/stores/RuntimeService.ts
new file mode 100644
index 0000000..ecb1942
--- /dev/null
+++ b/packages/main/src/stores/RuntimeService.ts
@@ -0,0 +1,74 @@
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 { UnreadCount } from '@sophie/service-shared';
22import { runtimeService as originalRuntimeService } from '@sophie/shared';
23import { Instance } from 'mobx-state-tree';
24
25export const runtimeService = originalRuntimeService.actions((self) => ({
26 setLocation({
27 url,
28 canGoBack,
29 canGoForward,
30 }: {
31 url: string;
32 canGoBack: boolean;
33 canGoForward: boolean;
34 }): void {
35 self.url = url;
36 self.canGoBack = canGoBack;
37 self.canGoForward = canGoForward;
38 },
39 setTitle(title: string): void {
40 self.title = title;
41 },
42 hibernated(): void {
43 self.canGoBack = false;
44 self.canGoForward = false;
45 self.state = 'hibernated';
46 },
47 startedLoading(): void {
48 self.state = 'loading';
49 },
50 finishedLoading(): void {
51 if (self.state === 'loading') {
52 // Do not overwrite crashed state if the service haven't been reloaded yet.
53 self.state = 'loaded';
54 }
55 },
56 crashed(): void {
57 self.state = 'crashed';
58 },
59 setUnreadCount({ direct, indirect }: UnreadCount): void {
60 if (direct !== undefined) {
61 self.directMessageCount = direct;
62 }
63 if (indirect !== undefined) {
64 self.indirectMessageCount = indirect;
65 }
66 },
67}));
68
69export interface RuntimeService extends Instance<typeof runtimeService> {}
70
71export type {
72 RuntimeServiceSnapshotIn,
73 RuntimeServiceSnapshotOut,
74} from '@sophie/shared';
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts
index 73245cd..76ce05c 100644
--- a/packages/main/src/stores/SharedStore.ts
+++ b/packages/main/src/stores/SharedStore.ts
@@ -22,6 +22,7 @@ import { sharedStore as originalSharedStore } from '@sophie/shared';
22import { Instance, types } from 'mobx-state-tree'; 22import { Instance, types } from 'mobx-state-tree';
23 23
24import { config } from './Config'; 24import { config } from './Config';
25import { runtimeService } from './RuntimeService';
25 26
26export type { 27export type {
27 SharedStoreSnapshotIn, 28 SharedStoreSnapshotIn,
@@ -30,6 +31,7 @@ export type {
30 31
31export const sharedStore = originalSharedStore.props({ 32export const sharedStore = originalSharedStore.props({
32 config: types.optional(config, {}), 33 config: types.optional(config, {}),
34 runtimeServices: types.map(runtimeService),
33}); 35});
34 36
35export interface SharedStore extends Instance<typeof sharedStore> {} 37export interface SharedStore extends Instance<typeof sharedStore> {}
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}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 9f4e9b3..df02854 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -40,6 +40,13 @@ export type {
40export { profile } from './stores/Profile'; 40export { profile } from './stores/Profile';
41 41
42export type { 42export type {
43 RuntimeService,
44 RuntimeServiceSnapshotIn,
45 RuntimeServiceSnapshotOut,
46} from './stores/RuntimeService';
47export { runtimeService } from './stores/RuntimeService';
48
49export type {
43 Service, 50 Service,
44 ServiceSnapshotIn, 51 ServiceSnapshotIn,
45 ServiceSnapshotOut, 52 ServiceSnapshotOut,
diff --git a/packages/shared/src/stores/RuntimeService.ts b/packages/shared/src/stores/RuntimeService.ts
new file mode 100644
index 0000000..c5b9031
--- /dev/null
+++ b/packages/shared/src/stores/RuntimeService.ts
@@ -0,0 +1,47 @@
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 { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree';
22
23export const runtimeService = types.model({
24 url: types.maybe(types.string),
25 canGoBack: false,
26 canGoForward: false,
27 title: types.maybe(types.string),
28 state: types.optional(
29 types.enumeration('ServiceState', [
30 'hibernated',
31 'loading',
32 'loaded',
33 'crashed',
34 ]),
35 'hibernated',
36 ),
37 directMessageCount: 0,
38 indirectMessageCount: 0,
39});
40
41export interface RuntimeService extends Instance<typeof runtimeService> {}
42
43export interface RuntimeServiceSnapshotIn
44 extends SnapshotIn<typeof runtimeService> {}
45
46export interface RuntimeServiceSnapshotOut
47 extends SnapshotOut<typeof runtimeService> {}
diff --git a/packages/shared/src/stores/SharedStore.ts b/packages/shared/src/stores/SharedStore.ts
index cb14394..ffa387f 100644
--- a/packages/shared/src/stores/SharedStore.ts
+++ b/packages/shared/src/stores/SharedStore.ts
@@ -27,10 +27,12 @@ import {
27} from 'mobx-state-tree'; 27} from 'mobx-state-tree';
28 28
29import { config } from './Config'; 29import { config } from './Config';
30import { runtimeService } from './RuntimeService';
30 31
31export const sharedStore = types.model('SharedStore', { 32export const sharedStore = types.model('SharedStore', {
32 config: types.optional(config, {}), 33 config: types.optional(config, {}),
33 shouldUseDarkColors: false, 34 shouldUseDarkColors: false,
35 runtimeServices: types.map(runtimeService),
34}); 36});
35 37
36export interface SharedStore extends Instance<typeof sharedStore> {} 38export interface SharedStore extends Instance<typeof sharedStore> {}
diff --git a/yarn.lock b/yarn.lock
index e6f05ba..8a0bf4e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1291,13 +1291,18 @@ __metadata:
1291 "@emotion/react": ^11.7.1 1291 "@emotion/react": ^11.7.1
1292 "@emotion/styled": ^11.6.0 1292 "@emotion/styled": ^11.6.0
1293 "@fontsource/roboto": ^4.5.2 1293 "@fontsource/roboto": ^4.5.2
1294 "@jest/globals": ^27.4.6
1294 "@mui/icons-material": ^5.3.1 1295 "@mui/icons-material": ^5.3.1
1295 "@mui/material": ^5.3.1 1296 "@mui/material": ^5.3.1
1296 "@sophie/shared": "workspace:*" 1297 "@sophie/shared": "workspace:*"
1298 "@types/jest": ^27.4.0
1297 "@types/lodash-es": ^4.17.5 1299 "@types/lodash-es": ^4.17.5
1298 "@types/react": ^17.0.38 1300 "@types/react": ^17.0.38
1299 "@types/react-dom": ^17.0.11 1301 "@types/react-dom": ^17.0.11
1300 "@vitejs/plugin-react": ^1.1.4 1302 "@vitejs/plugin-react": ^1.1.4
1303 jest: ^27.4.7
1304 jest-mock: ^27.4.6
1305 jsdom: ^19.0.0
1301 lodash-es: ^4.17.21 1306 lodash-es: ^4.17.21
1302 loglevel: ^1.8.0 1307 loglevel: ^1.8.0
1303 loglevel-plugin-prefix: ^0.8.4 1308 loglevel-plugin-prefix: ^0.8.4