diff options
-rw-r--r-- | packages/main/src/stores/RuntimeService.ts | 74 | ||||
-rw-r--r-- | packages/main/src/stores/SharedStore.ts | 2 | ||||
-rw-r--r-- | packages/renderer/jest.config.js | 7 | ||||
-rw-r--r-- | packages/renderer/package.json | 5 | ||||
-rw-r--r-- | packages/renderer/src/env/RendererEnv.ts | 27 | ||||
-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.ts | 55 | ||||
-rw-r--r-- | packages/renderer/src/env/impl/__tests__/RendererEnvImpl.spec.ts | 87 | ||||
-rw-r--r-- | packages/renderer/src/stores/Config.ts | 32 | ||||
-rw-r--r-- | packages/renderer/src/stores/RendererStore.ts | 21 | ||||
-rw-r--r-- | packages/renderer/src/stores/Service.ts | 38 | ||||
-rw-r--r-- | packages/renderer/src/stores/SharedStore.ts | 35 | ||||
-rw-r--r-- | packages/renderer/src/stores/__tests__/Service.spec.ts | 63 | ||||
-rw-r--r-- | packages/renderer/tsconfig.json | 3 | ||||
-rw-r--r-- | packages/shared/src/index.ts | 7 | ||||
-rw-r--r-- | packages/shared/src/stores/RuntimeService.ts | 47 | ||||
-rw-r--r-- | packages/shared/src/stores/SharedStore.ts | 2 | ||||
-rw-r--r-- | yarn.lock | 5 |
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 | |||
21 | import { UnreadCount } from '@sophie/service-shared'; | ||
22 | import { runtimeService as originalRuntimeService } from '@sophie/shared'; | ||
23 | import { Instance } from 'mobx-state-tree'; | ||
24 | |||
25 | export 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 | |||
69 | export interface RuntimeService extends Instance<typeof runtimeService> {} | ||
70 | |||
71 | export 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'; | |||
22 | import { Instance, types } from 'mobx-state-tree'; | 22 | import { Instance, types } from 'mobx-state-tree'; |
23 | 23 | ||
24 | import { config } from './Config'; | 24 | import { config } from './Config'; |
25 | import { runtimeService } from './RuntimeService'; | ||
25 | 26 | ||
26 | export type { | 27 | export type { |
27 | SharedStoreSnapshotIn, | 28 | SharedStoreSnapshotIn, |
@@ -30,6 +31,7 @@ export type { | |||
30 | 31 | ||
31 | export const sharedStore = originalSharedStore.props({ | 32 | export const sharedStore = originalSharedStore.props({ |
32 | config: types.optional(config, {}), | 33 | config: types.optional(config, {}), |
34 | runtimeServices: types.map(runtimeService), | ||
33 | }); | 35 | }); |
34 | 36 | ||
35 | export interface SharedStore extends Instance<typeof sharedStore> {} | 37 | export 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 @@ | |||
1 | import rootConfig from '../../config/jest.config.base.js'; | ||
2 | |||
3 | /** @type {import('@jest/types').Config.InitialOptions} */ | ||
4 | export 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 | |||
21 | import type { Action, RuntimeService } from '@sophie/shared'; | ||
22 | |||
23 | export 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 | ||
21 | import type { Action } from '@sophie/shared'; | ||
22 | import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree'; | 21 | import { getEnv as getAnyEnv, IAnyStateTreeNode } from 'mobx-state-tree'; |
23 | 22 | ||
24 | export default interface RendererEnv { | 23 | import 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 | */ |
35 | export function getEnv(model: IAnyStateTreeNode): RendererEnv { | 32 | export 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 | |||
21 | import { | ||
22 | Action, | ||
23 | runtimeService, | ||
24 | RuntimeService, | ||
25 | SophieRenderer, | ||
26 | } from '@sophie/shared'; | ||
27 | import type { IMSTMap } from 'mobx-state-tree'; | ||
28 | |||
29 | import type { RendererStore } from '../../stores/RendererStore'; | ||
30 | import type RendererEnv from '../RendererEnv'; | ||
31 | |||
32 | export 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 | |||
21 | import { jest } from '@jest/globals'; | ||
22 | import { | ||
23 | Action, | ||
24 | runtimeService, | ||
25 | RuntimeService, | ||
26 | SophieRenderer, | ||
27 | } from '@sophie/shared'; | ||
28 | |||
29 | import { rendererStore } from '../../../stores/RendererStore'; | ||
30 | import RendererEnvImpl from '../RendererEnvImpl'; | ||
31 | |||
32 | const ipc: SophieRenderer = { | ||
33 | dispatchAction: jest.fn(), | ||
34 | onSharedStoreChange: jest.fn(), | ||
35 | }; | ||
36 | let sut: RendererEnvImpl; | ||
37 | |||
38 | beforeEach(() => { | ||
39 | sut = new RendererEnvImpl(ipc); | ||
40 | }); | ||
41 | |||
42 | describe('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 | |||
53 | describe('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 | |||
21 | import { config as originalConfig } from '@sophie/shared'; | ||
22 | import { Instance, types } from 'mobx-state-tree'; | ||
23 | |||
24 | import { service } from './Service'; | ||
25 | |||
26 | export const config = originalConfig.props({ | ||
27 | services: types.array(service), | ||
28 | }); | ||
29 | |||
30 | export interface Config extends Instance<typeof config> {} | ||
31 | |||
32 | export 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 | ||
21 | import { | 21 | import { BrowserViewBounds, SophieRenderer, ThemeSource } from '@sophie/shared'; |
22 | BrowserViewBounds, | ||
23 | Config, | ||
24 | Service, | ||
25 | sharedStore, | ||
26 | SophieRenderer, | ||
27 | ThemeSource, | ||
28 | } from '@sophie/shared'; | ||
29 | import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; | 22 | import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; |
30 | 23 | ||
24 | import getEnv from '../env/getEnv'; | ||
25 | import RendererEnvImpl from '../env/impl/RendererEnvImpl'; | ||
31 | import { getLogger } from '../utils/log'; | 26 | import { getLogger } from '../utils/log'; |
32 | 27 | ||
33 | import type RendererEnv from './RendererEnv'; | 28 | import type { Config } from './Config'; |
34 | import { getEnv } from './RendererEnv'; | 29 | import type { Service } from './Service'; |
30 | import { sharedStore } from './SharedStore'; | ||
35 | 31 | ||
36 | const log = getLogger('RendererStore'); | 32 | const log = getLogger('RendererStore'); |
37 | 33 | ||
@@ -82,10 +78,9 @@ export interface RendererStore extends Instance<typeof rendererStore> {} | |||
82 | export function createAndConnectRendererStore( | 78 | export 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 | |||
21 | import { | ||
22 | runtimeService, | ||
23 | RuntimeService, | ||
24 | service as originalService, | ||
25 | } from '@sophie/shared'; | ||
26 | import { Instance } from 'mobx-state-tree'; | ||
27 | |||
28 | import getEnv from '../env/getEnv'; | ||
29 | |||
30 | export const service = originalService.views((self) => ({ | ||
31 | get runtime(): RuntimeService { | ||
32 | return getEnv(self).getRuntimeService(self.id) ?? runtimeService.create(); | ||
33 | }, | ||
34 | })); | ||
35 | |||
36 | export interface Service extends Instance<typeof service> {} | ||
37 | |||
38 | export 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 | |||
21 | import { sharedStore as originalSharedStore } from '@sophie/shared'; | ||
22 | import { Instance, types } from 'mobx-state-tree'; | ||
23 | |||
24 | import { config } from './Config'; | ||
25 | |||
26 | export const sharedStore = originalSharedStore.props({ | ||
27 | config: types.optional(config, {}), | ||
28 | }); | ||
29 | |||
30 | export interface SharedStore extends Instance<typeof sharedStore> {} | ||
31 | |||
32 | export 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 | |||
21 | import { jest } from '@jest/globals'; | ||
22 | import { runtimeService } from '@sophie/shared'; | ||
23 | import { mocked } from 'jest-mock'; | ||
24 | |||
25 | import type RendererEnv from '../../env/RendererEnv'; | ||
26 | import { service, Service } from '../Service'; | ||
27 | |||
28 | const env: RendererEnv = { | ||
29 | dispatchMainAction: jest.fn(), | ||
30 | getRuntimeService: jest.fn(), | ||
31 | }; | ||
32 | let sut: Service; | ||
33 | |||
34 | beforeEach(() => { | ||
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 | |||
46 | describe('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 { | |||
40 | export { profile } from './stores/Profile'; | 40 | export { profile } from './stores/Profile'; |
41 | 41 | ||
42 | export type { | 42 | export type { |
43 | RuntimeService, | ||
44 | RuntimeServiceSnapshotIn, | ||
45 | RuntimeServiceSnapshotOut, | ||
46 | } from './stores/RuntimeService'; | ||
47 | export { runtimeService } from './stores/RuntimeService'; | ||
48 | |||
49 | export 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 | |||
21 | import { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree'; | ||
22 | |||
23 | export 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 | |||
41 | export interface RuntimeService extends Instance<typeof runtimeService> {} | ||
42 | |||
43 | export interface RuntimeServiceSnapshotIn | ||
44 | extends SnapshotIn<typeof runtimeService> {} | ||
45 | |||
46 | export 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 | ||
29 | import { config } from './Config'; | 29 | import { config } from './Config'; |
30 | import { runtimeService } from './RuntimeService'; | ||
30 | 31 | ||
31 | export const sharedStore = types.model('SharedStore', { | 32 | export 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 | ||
36 | export interface SharedStore extends Instance<typeof sharedStore> {} | 38 | export interface SharedStore extends Instance<typeof sharedStore> {} |
@@ -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 |