diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-02-27 00:57:44 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-03-06 18:56:46 +0100 |
commit | f05d54406c9bc4b69609a4935132ff17b8e28824 (patch) | |
tree | e7ffde8f8b3433e004932a6e068dedbb4f2196da | |
parent | design: Simpler message count indicators (diff) | |
download | sophie-f05d54406c9bc4b69609a4935132ff17b8e28824.tar.gz sophie-f05d54406c9bc4b69609a4935132ff17b8e28824.tar.zst sophie-f05d54406c9bc4b69609a4935132ff17b8e28824.zip |
refactor: Shared model type factories
Allows customization of stores both in the renderer and in the main
process. Instead of exposing a basic model type from the shared module
(which was be overwritted with more specific props in the main package),
we expose factory function that can create specific model types in
both the renderer and the main process.
Using these package-specific customization to stores, the renderer
package can attach IPC calls directly to store objects, which the main
package can attach the handlers for IPC calls and other internal
actions.
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
25 files changed, 348 insertions, 230 deletions
diff --git a/packages/main/src/stores/GlobalSettings.ts b/packages/main/src/stores/GlobalSettings.ts index 2af9da2..31b7e12 100644 --- a/packages/main/src/stores/GlobalSettings.ts +++ b/packages/main/src/stores/GlobalSettings.ts | |||
@@ -18,22 +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 { defineGlobalSettingsModel, ThemeSource } from '@sophie/shared'; |
22 | GlobalSettings as GlobalSettingsBase, | 22 | import { Instance, resolveIdentifier } from 'mobx-state-tree'; |
23 | ThemeSource, | ||
24 | } from '@sophie/shared'; | ||
25 | import { Instance, resolveIdentifier, types } from 'mobx-state-tree'; | ||
26 | 23 | ||
27 | import { getLogger } from '../utils/log'; | 24 | import { getLogger } from '../utils/log'; |
28 | import overrideProps from '../utils/overrideProps'; | ||
29 | 25 | ||
30 | import Service from './Service'; | 26 | import Service from './Service'; |
31 | 27 | ||
32 | const log = getLogger('sharedStore'); | 28 | const log = getLogger('sharedStore'); |
33 | 29 | ||
34 | const GlobalSettings = overrideProps(GlobalSettingsBase, { | 30 | const GlobalSettings = defineGlobalSettingsModel(Service).actions((self) => ({ |
35 | selectedService: types.safeReference(Service), | ||
36 | }).actions((self) => ({ | ||
37 | setThemeSource(mode: ThemeSource): void { | 31 | setThemeSource(mode: ThemeSource): void { |
38 | self.themeSource = mode; | 32 | self.themeSource = mode; |
39 | }, | 33 | }, |
diff --git a/packages/main/src/stores/Profile.ts b/packages/main/src/stores/Profile.ts index 0fd486e..836f4a8 100644 --- a/packages/main/src/stores/Profile.ts +++ b/packages/main/src/stores/Profile.ts | |||
@@ -21,14 +21,9 @@ | |||
21 | import { Profile as ProfileBase } from '@sophie/shared'; | 21 | import { Profile as ProfileBase } from '@sophie/shared'; |
22 | import { getSnapshot, Instance } from 'mobx-state-tree'; | 22 | import { getSnapshot, Instance } from 'mobx-state-tree'; |
23 | 23 | ||
24 | import overrideProps from '../utils/overrideProps'; | ||
25 | |||
26 | import ProfileSettings from './ProfileSettings'; | ||
27 | import type ProfileConfig from './config/ProfileConfig'; | 24 | import type ProfileConfig from './config/ProfileConfig'; |
28 | 25 | ||
29 | const Profile = overrideProps(ProfileBase, { | 26 | const Profile = ProfileBase.views((self) => ({ |
30 | settings: ProfileSettings, | ||
31 | }).views((self) => ({ | ||
32 | get config(): ProfileConfig { | 27 | get config(): ProfileConfig { |
33 | const { id, settings } = self; | 28 | const { id, settings } = self; |
34 | return { ...getSnapshot(settings), id }; | 29 | return { ...getSnapshot(settings), id }; |
diff --git a/packages/main/src/stores/Service.ts b/packages/main/src/stores/Service.ts index 5302dd4..abef7c2 100644 --- a/packages/main/src/stores/Service.ts +++ b/packages/main/src/stores/Service.ts | |||
@@ -19,18 +19,15 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import type { UnreadCount } from '@sophie/service-shared'; | 21 | import type { UnreadCount } from '@sophie/service-shared'; |
22 | import { Service as ServiceBase } from '@sophie/shared'; | 22 | import { defineServiceModel } from '@sophie/shared'; |
23 | import { Instance, getSnapshot } from 'mobx-state-tree'; | 23 | import { Instance, getSnapshot } from 'mobx-state-tree'; |
24 | 24 | ||
25 | import type { ServiceView } from '../infrastructure/electron/types'; | 25 | import type { ServiceView } from '../infrastructure/electron/types'; |
26 | import overrideProps from '../utils/overrideProps'; | ||
27 | 26 | ||
28 | import ServiceSettings from './ServiceSettings'; | 27 | import ServiceSettings from './ServiceSettings'; |
29 | import type ServiceConfig from './config/ServiceConfig'; | 28 | import type ServiceConfig from './config/ServiceConfig'; |
30 | 29 | ||
31 | const Service = overrideProps(ServiceBase, { | 30 | const Service = defineServiceModel(ServiceSettings) |
32 | settings: ServiceSettings, | ||
33 | }) | ||
34 | .views((self) => ({ | 31 | .views((self) => ({ |
35 | get config(): ServiceConfig { | 32 | get config(): ServiceConfig { |
36 | const { id, settings } = self; | 33 | const { id, settings } = self; |
diff --git a/packages/main/src/stores/ServiceSettings.ts b/packages/main/src/stores/ServiceSettings.ts index e6f48c6..5d37347 100644 --- a/packages/main/src/stores/ServiceSettings.ts +++ b/packages/main/src/stores/ServiceSettings.ts | |||
@@ -18,16 +18,12 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { ServiceSettings as ServiceSettingsBase } from '@sophie/shared'; | 21 | import { defineServiceSettingsModel } from '@sophie/shared'; |
22 | import { Instance, types } from 'mobx-state-tree'; | 22 | import { Instance } from 'mobx-state-tree'; |
23 | |||
24 | import overrideProps from '../utils/overrideProps'; | ||
25 | 23 | ||
26 | import Profile from './Profile'; | 24 | import Profile from './Profile'; |
27 | 25 | ||
28 | const ServiceSettings = overrideProps(ServiceSettingsBase, { | 26 | const ServiceSettings = defineServiceSettingsModel(Profile); |
29 | profile: types.reference(Profile), | ||
30 | }); | ||
31 | 27 | ||
32 | /* | 28 | /* |
33 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | 29 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts index 182b693..d72c532 100644 --- a/packages/main/src/stores/SharedStore.ts +++ b/packages/main/src/stores/SharedStore.ts | |||
@@ -18,10 +18,8 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { SharedStore as SharedStoreBase } from '@sophie/shared'; | 21 | import { defineSharedStoreModel } from '@sophie/shared'; |
22 | import { getSnapshot, Instance, types } from 'mobx-state-tree'; | 22 | import { getSnapshot, Instance } from 'mobx-state-tree'; |
23 | |||
24 | import overrideProps from '../utils/overrideProps'; | ||
25 | 23 | ||
26 | import GlobalSettings from './GlobalSettings'; | 24 | import GlobalSettings from './GlobalSettings'; |
27 | import Profile from './Profile'; | 25 | import Profile from './Profile'; |
@@ -33,13 +31,7 @@ function getConfigs<T>(models: { config: T }[]): T[] | undefined { | |||
33 | return models.length === 0 ? undefined : models.map((model) => model.config); | 31 | return models.length === 0 ? undefined : models.map((model) => model.config); |
34 | } | 32 | } |
35 | 33 | ||
36 | const SharedStore = overrideProps(SharedStoreBase, { | 34 | const SharedStore = defineSharedStoreModel(GlobalSettings, Profile, Service) |
37 | settings: types.optional(GlobalSettings, {}), | ||
38 | profilesById: types.map(Profile), | ||
39 | profiles: types.array(types.reference(Profile)), | ||
40 | servicesById: types.map(Service), | ||
41 | services: types.array(types.reference(Service)), | ||
42 | }) | ||
43 | .views((self) => ({ | 35 | .views((self) => ({ |
44 | get config(): Config { | 36 | get config(): Config { |
45 | const { settings, profiles, services } = self; | 37 | const { settings, profiles, services } = self; |
diff --git a/packages/main/src/stores/config/loadConfig.ts b/packages/main/src/stores/config/loadConfig.ts index 55d15c8..2463084 100644 --- a/packages/main/src/stores/config/loadConfig.ts +++ b/packages/main/src/stores/config/loadConfig.ts | |||
@@ -18,6 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { ProfileSettingsSnapshotIn } from '@sophie/shared'; | ||
21 | import { | 22 | import { |
22 | applySnapshot, | 23 | applySnapshot, |
23 | IMSTArray, | 24 | IMSTArray, |
@@ -31,7 +32,6 @@ import slug from 'slug'; | |||
31 | 32 | ||
32 | import GlobalSettings from '../GlobalSettings'; | 33 | import GlobalSettings from '../GlobalSettings'; |
33 | import type Profile from '../Profile'; | 34 | import type Profile from '../Profile'; |
34 | import type { ProfileSettingsSnapshotIn } from '../ProfileSettings'; | ||
35 | import type Service from '../Service'; | 35 | import type Service from '../Service'; |
36 | import type { ServiceSettingsSnapshotIn } from '../ServiceSettings'; | 36 | import type { ServiceSettingsSnapshotIn } from '../ServiceSettings'; |
37 | 37 | ||
diff --git a/packages/main/src/utils/overrideProps.ts b/packages/main/src/utils/overrideProps.ts deleted file mode 100644 index c626408..0000000 --- a/packages/main/src/utils/overrideProps.ts +++ /dev/null | |||
@@ -1,62 +0,0 @@ | |||
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 | /** | ||
22 | * @file This file implements a technique to force-override properties of a model. | ||
23 | * | ||
24 | * The overridden properties must conform to the SnapshotIt and SnapshotOut format | ||
25 | * of the original model. Essentially, this means that only views and actions can | ||
26 | * be added safely. | ||
27 | * | ||
28 | * @see https://github.com/mobxjs/mobx-state-tree/issues/1403#issuecomment-940843087 | ||
29 | */ | ||
30 | |||
31 | import { | ||
32 | IAnyModelType, | ||
33 | IModelType, | ||
34 | ModelProperties, | ||
35 | SnapshotIn, | ||
36 | SnapshotOut, | ||
37 | } from 'mobx-state-tree'; | ||
38 | |||
39 | export type IUnsafeOverriddenModelType< | ||
40 | BASE extends IAnyModelType, | ||
41 | PROPS extends ModelProperties, | ||
42 | > = BASE extends IModelType<infer P, infer O, infer CC, infer CS> | ||
43 | ? IModelType<Omit<P, keyof PROPS> & PROPS, O, CC, CS> | ||
44 | : never; | ||
45 | |||
46 | export type IOverriddenModelType< | ||
47 | BASE extends IAnyModelType, | ||
48 | PROPS extends ModelProperties, | ||
49 | > = SnapshotIn<BASE> extends SnapshotIn<IUnsafeOverriddenModelType<BASE, PROPS>> | ||
50 | ? SnapshotOut< | ||
51 | IUnsafeOverriddenModelType<BASE, PROPS> | ||
52 | > extends SnapshotOut<BASE> | ||
53 | ? IUnsafeOverriddenModelType<BASE, PROPS> | ||
54 | : never | ||
55 | : never; | ||
56 | |||
57 | export default function overrideProps< | ||
58 | BASE extends IAnyModelType, | ||
59 | PROPS extends ModelProperties, | ||
60 | >(base: BASE, props: PROPS): IOverriddenModelType<BASE, PROPS> { | ||
61 | return base.props(props) as IOverriddenModelType<BASE, PROPS>; | ||
62 | } | ||
diff --git a/packages/renderer/src/components/locationBar/LocationTextField.tsx b/packages/renderer/src/components/locationBar/LocationTextField.tsx index f436bf0..e6da59f 100644 --- a/packages/renderer/src/components/locationBar/LocationTextField.tsx +++ b/packages/renderer/src/components/locationBar/LocationTextField.tsx | |||
@@ -20,11 +20,12 @@ | |||
20 | 20 | ||
21 | import FilledInput from '@mui/material/FilledInput'; | 21 | import FilledInput from '@mui/material/FilledInput'; |
22 | import { styled } from '@mui/material/styles'; | 22 | import { styled } from '@mui/material/styles'; |
23 | import { Service } from '@sophie/shared'; | ||
24 | import { autorun } from 'mobx'; | 23 | import { autorun } from 'mobx'; |
25 | import { observer } from 'mobx-react-lite'; | 24 | import { observer } from 'mobx-react-lite'; |
26 | import React, { useCallback, useEffect, useState } from 'react'; | 25 | import React, { useCallback, useEffect, useState } from 'react'; |
27 | 26 | ||
27 | import Service from '../../stores/Service'; | ||
28 | |||
28 | import GoAdornment from './GoAdornment'; | 29 | import GoAdornment from './GoAdornment'; |
29 | import LocationOverlayInput from './LocationOverlayInput'; | 30 | import LocationOverlayInput from './LocationOverlayInput'; |
30 | import UrlAdornment from './UrlAdornment'; | 31 | import UrlAdornment from './UrlAdornment'; |
diff --git a/packages/renderer/src/components/locationBar/NavigationButtons.tsx b/packages/renderer/src/components/locationBar/NavigationButtons.tsx index 77b02b6..ce59692 100644 --- a/packages/renderer/src/components/locationBar/NavigationButtons.tsx +++ b/packages/renderer/src/components/locationBar/NavigationButtons.tsx | |||
@@ -26,10 +26,11 @@ import IconRefresh from '@mui/icons-material/Refresh'; | |||
26 | import { useTheme } from '@mui/material'; | 26 | import { useTheme } from '@mui/material'; |
27 | import Box from '@mui/material/Box'; | 27 | import Box from '@mui/material/Box'; |
28 | import IconButton from '@mui/material/IconButton'; | 28 | import IconButton from '@mui/material/IconButton'; |
29 | import { Service } from '@sophie/shared'; | ||
30 | import { observer } from 'mobx-react-lite'; | 29 | import { observer } from 'mobx-react-lite'; |
31 | import React from 'react'; | 30 | import React from 'react'; |
32 | 31 | ||
32 | import Service from '../../stores/Service'; | ||
33 | |||
33 | function NavigationButtons({ | 34 | function NavigationButtons({ |
34 | service, | 35 | service, |
35 | }: { | 36 | }: { |
diff --git a/packages/renderer/src/components/sidebar/ServiceIcon.tsx b/packages/renderer/src/components/sidebar/ServiceIcon.tsx index 144f860..5db8a0c 100644 --- a/packages/renderer/src/components/sidebar/ServiceIcon.tsx +++ b/packages/renderer/src/components/sidebar/ServiceIcon.tsx | |||
@@ -20,10 +20,11 @@ | |||
20 | 20 | ||
21 | import Badge from '@mui/material/Badge'; | 21 | import Badge from '@mui/material/Badge'; |
22 | import { styled, useTheme } from '@mui/material/styles'; | 22 | import { styled, useTheme } from '@mui/material/styles'; |
23 | import { Service } from '@sophie/shared'; | ||
24 | import { observer } from 'mobx-react-lite'; | 23 | import { observer } from 'mobx-react-lite'; |
25 | import React, { useEffect, useState } from 'react'; | 24 | import React, { useEffect, useState } from 'react'; |
26 | 25 | ||
26 | import type Service from '../../stores/Service'; | ||
27 | |||
27 | const ServiceIconRoot = styled('div', { | 28 | const ServiceIconRoot = styled('div', { |
28 | name: 'ServiceIcon', | 29 | name: 'ServiceIcon', |
29 | slot: 'Root', | 30 | slot: 'Root', |
diff --git a/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx b/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx index 2241476..eba21b5 100644 --- a/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx +++ b/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx | |||
@@ -65,11 +65,8 @@ const ServiceSwitcherTab = styled(Tab, { | |||
65 | })); | 65 | })); |
66 | 66 | ||
67 | function ServiceSwitcher(): JSX.Element { | 67 | function ServiceSwitcher(): JSX.Element { |
68 | const store = useStore(); | 68 | const { settings, services } = useStore(); |
69 | const { | 69 | const { selectedService } = settings; |
70 | settings: { selectedService }, | ||
71 | services, | ||
72 | } = store; | ||
73 | 70 | ||
74 | return ( | 71 | return ( |
75 | <ServiceSwitcherRoot | 72 | <ServiceSwitcherRoot |
@@ -77,7 +74,7 @@ function ServiceSwitcher(): JSX.Element { | |||
77 | orientation="vertical" | 74 | orientation="vertical" |
78 | value={selectedService === undefined ? false : selectedService.id} | 75 | value={selectedService === undefined ? false : selectedService.id} |
79 | onChange={(_event, newValue: string) => | 76 | onChange={(_event, newValue: string) => |
80 | store.setSelectedServiceId(newValue) | 77 | settings.setSelectedServiceId(newValue) |
81 | } | 78 | } |
82 | > | 79 | > |
83 | {services.map((service) => ( | 80 | {services.map((service) => ( |
diff --git a/packages/renderer/src/components/sidebar/ToggleDarkModeButton.tsx b/packages/renderer/src/components/sidebar/ToggleDarkModeButton.tsx index 164b066..bacbf07 100644 --- a/packages/renderer/src/components/sidebar/ToggleDarkModeButton.tsx +++ b/packages/renderer/src/components/sidebar/ToggleDarkModeButton.tsx | |||
@@ -27,15 +27,13 @@ import React from 'react'; | |||
27 | import { useStore } from '../StoreProvider'; | 27 | import { useStore } from '../StoreProvider'; |
28 | 28 | ||
29 | export default observer(() => { | 29 | export default observer(() => { |
30 | const store = useStore(); | 30 | const { shared } = useStore(); |
31 | const { | 31 | const { shouldUseDarkColors } = shared; |
32 | shared: { shouldUseDarkColors }, | ||
33 | } = store; | ||
34 | 32 | ||
35 | return ( | 33 | return ( |
36 | <IconButton | 34 | <IconButton |
37 | aria-label="Toggle dark mode" | 35 | aria-label="Toggle dark mode" |
38 | onClick={() => store.toggleDarkMode()} | 36 | onClick={() => shared.toggleDarkMode()} |
39 | > | 37 | > |
40 | {shouldUseDarkColors ? <LightModeIcon /> : <DarkModeIcon />} | 38 | {shouldUseDarkColors ? <LightModeIcon /> : <DarkModeIcon />} |
41 | </IconButton> | 39 | </IconButton> |
diff --git a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx index 60033b0..d2f0745 100644 --- a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx +++ b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx | |||
@@ -45,17 +45,15 @@ function ToggleLocationBarIcon({ | |||
45 | } | 45 | } |
46 | 46 | ||
47 | function ToggleLocationBarButton(): JSX.Element { | 47 | function ToggleLocationBarButton(): JSX.Element { |
48 | const store = useStore(); | 48 | const { settings } = useStore(); |
49 | const { | 49 | const { selectedService, showLocationBar } = settings; |
50 | settings: { selectedService, showLocationBar }, | ||
51 | } = store; | ||
52 | 50 | ||
53 | return ( | 51 | return ( |
54 | <IconButton | 52 | <IconButton |
55 | aria-pressed={showLocationBar} | 53 | aria-pressed={showLocationBar} |
56 | aria-controls={LOCATION_BAR_ID} | 54 | aria-controls={LOCATION_BAR_ID} |
57 | aria-label="Show location bar" | 55 | aria-label="Show location bar" |
58 | onClick={() => store.toggleLocationBar()} | 56 | onClick={() => settings.toggleLocationBar()} |
59 | > | 57 | > |
60 | <ToggleLocationBarIcon | 58 | <ToggleLocationBarIcon |
61 | loading={selectedService?.state === 'loading'} | 59 | loading={selectedService?.state === 'loading'} |
diff --git a/packages/renderer/src/stores/GlobalSettings.ts b/packages/renderer/src/stores/GlobalSettings.ts new file mode 100644 index 0000000..79815ba --- /dev/null +++ b/packages/renderer/src/stores/GlobalSettings.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 { defineGlobalSettingsModel, ThemeSource } from '@sophie/shared'; | ||
22 | import { Instance } from 'mobx-state-tree'; | ||
23 | |||
24 | import getEnv from '../env/getEnv'; | ||
25 | |||
26 | import Service from './Service'; | ||
27 | |||
28 | const GlobalSettings = defineGlobalSettingsModel(Service).actions((self) => ({ | ||
29 | setSelectedServiceId(serviceId: string): void { | ||
30 | getEnv(self).dispatchMainAction({ | ||
31 | action: 'set-selected-service-id', | ||
32 | serviceId, | ||
33 | }); | ||
34 | }, | ||
35 | setThemeSource(themeSource: ThemeSource): void { | ||
36 | getEnv(self).dispatchMainAction({ | ||
37 | action: 'set-theme-source', | ||
38 | themeSource, | ||
39 | }); | ||
40 | }, | ||
41 | setShowLocationBar(showLocationBar: boolean): void { | ||
42 | getEnv(self).dispatchMainAction({ | ||
43 | action: 'set-show-location-bar', | ||
44 | showLocationBar, | ||
45 | }); | ||
46 | }, | ||
47 | toggleLocationBar(): void { | ||
48 | this.setShowLocationBar(!self.showLocationBar); | ||
49 | }, | ||
50 | })); | ||
51 | |||
52 | /* | ||
53 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | ||
54 | Intentionally naming the type the same as the store definition. | ||
55 | */ | ||
56 | interface GlobalSettings extends Instance<typeof GlobalSettings> {} | ||
57 | |||
58 | export default GlobalSettings; | ||
59 | |||
60 | export type { | ||
61 | GlobalSettingsSnapshotIn, | ||
62 | GlobalSettingsSnapshotOut, | ||
63 | } from '@sophie/shared'; | ||
diff --git a/packages/main/src/stores/ProfileSettings.ts b/packages/renderer/src/stores/Profile.ts index eed51e3..20a3a17 100644 --- a/packages/main/src/stores/ProfileSettings.ts +++ b/packages/renderer/src/stores/Profile.ts | |||
@@ -18,13 +18,15 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { ProfileSettings } from '@sophie/shared'; | 21 | import { Profile as ProfileBase } from '@sophie/shared'; |
22 | import { Instance } from 'mobx-state-tree'; | ||
22 | 23 | ||
23 | // TODO Export a modified ProfileSettings once we need to add actions to it. | 24 | const Profile = ProfileBase; |
24 | // eslint-disable-next-line unicorn/prefer-export-from -- Can't export from default. | ||
25 | export default ProfileSettings; | ||
26 | 25 | ||
27 | export type { | 26 | /* |
28 | ProfileSettingsSnapshotIn, | 27 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
29 | ProfileSettingsSnapshotOut, | 28 | Intentionally naming the type the same as the store definition. |
30 | } from '@sophie/shared'; | 29 | */ |
30 | interface Profile extends Instance<typeof Profile> {} | ||
31 | |||
32 | export default Profile; | ||
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts index 1acc605..8f424f6 100644 --- a/packages/renderer/src/stores/RendererStore.ts +++ b/packages/renderer/src/stores/RendererStore.ts | |||
@@ -18,20 +18,17 @@ | |||
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 } from '@sophie/shared'; |
22 | BrowserViewBounds, | ||
23 | SharedStore, | ||
24 | Service, | ||
25 | SophieRenderer, | ||
26 | ThemeSource, | ||
27 | GlobalSettings, | ||
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 | ||
31 | import RendererEnv from '../env/RendererEnv'; | 24 | import RendererEnv from '../env/RendererEnv'; |
32 | import getEnv from '../env/getEnv'; | 25 | import getEnv from '../env/getEnv'; |
33 | import { getLogger } from '../utils/log'; | 26 | import { getLogger } from '../utils/log'; |
34 | 27 | ||
28 | import GlobalSettings from './GlobalSettings'; | ||
29 | import Service from './Service'; | ||
30 | import SharedStore from './SharedStore'; | ||
31 | |||
35 | const log = getLogger('RendererStore'); | 32 | const log = getLogger('RendererStore'); |
36 | 33 | ||
37 | const RendererStore = types | 34 | const RendererStore = types |
@@ -47,40 +44,12 @@ const RendererStore = types | |||
47 | }, | 44 | }, |
48 | })) | 45 | })) |
49 | .actions((self) => ({ | 46 | .actions((self) => ({ |
50 | setSelectedServiceId(serviceId: string): void { | ||
51 | getEnv(self).dispatchMainAction({ | ||
52 | action: 'set-selected-service-id', | ||
53 | serviceId, | ||
54 | }); | ||
55 | }, | ||
56 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { | 47 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { |
57 | getEnv(self).dispatchMainAction({ | 48 | getEnv(self).dispatchMainAction({ |
58 | action: 'set-browser-view-bounds', | 49 | action: 'set-browser-view-bounds', |
59 | browserViewBounds, | 50 | browserViewBounds, |
60 | }); | 51 | }); |
61 | }, | 52 | }, |
62 | setThemeSource(themeSource: ThemeSource): void { | ||
63 | getEnv(self).dispatchMainAction({ | ||
64 | action: 'set-theme-source', | ||
65 | themeSource, | ||
66 | }); | ||
67 | }, | ||
68 | toggleDarkMode(): void { | ||
69 | if (self.shared.shouldUseDarkColors) { | ||
70 | this.setThemeSource('light'); | ||
71 | } else { | ||
72 | this.setThemeSource('dark'); | ||
73 | } | ||
74 | }, | ||
75 | setShowLocationBar(showLocationBar: boolean): void { | ||
76 | getEnv(self).dispatchMainAction({ | ||
77 | action: 'set-show-location-bar', | ||
78 | showLocationBar, | ||
79 | }); | ||
80 | }, | ||
81 | toggleLocationBar(): void { | ||
82 | this.setShowLocationBar(!self.settings.showLocationBar); | ||
83 | }, | ||
84 | })); | 53 | })); |
85 | 54 | ||
86 | /* | 55 | /* |
diff --git a/packages/renderer/src/stores/Service.ts b/packages/renderer/src/stores/Service.ts new file mode 100644 index 0000000..c2c938a --- /dev/null +++ b/packages/renderer/src/stores/Service.ts | |||
@@ -0,0 +1,34 @@ | |||
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 { defineServiceModel } from '@sophie/shared'; | ||
22 | import { Instance } from 'mobx-state-tree'; | ||
23 | |||
24 | import ServiceSettings from './ServiceSettings'; | ||
25 | |||
26 | const Service = defineServiceModel(ServiceSettings); | ||
27 | |||
28 | /* | ||
29 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | ||
30 | Intentionally naming the type the same as the store definition. | ||
31 | */ | ||
32 | interface Service extends Instance<typeof Service> {} | ||
33 | |||
34 | export default Service; | ||
diff --git a/packages/shared/src/stores/ServiceSettings.ts b/packages/renderer/src/stores/ServiceSettings.ts index a5811f5..5d37347 100644 --- a/packages/shared/src/stores/ServiceSettings.ts +++ b/packages/renderer/src/stores/ServiceSettings.ts | |||
@@ -18,17 +18,12 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree'; | 21 | import { defineServiceSettingsModel } from '@sophie/shared'; |
22 | import { Instance } from 'mobx-state-tree'; | ||
22 | 23 | ||
23 | import Profile from './Profile'; | 24 | import Profile from './Profile'; |
24 | 25 | ||
25 | const ServiceSettings = /* @__PURE__ */ (() => | 26 | const ServiceSettings = defineServiceSettingsModel(Profile); |
26 | types.model('ServiceSettings', { | ||
27 | name: types.string, | ||
28 | profile: types.reference(Profile), | ||
29 | // TODO: Remove this once recipes are added. | ||
30 | url: types.string, | ||
31 | }))(); | ||
32 | 27 | ||
33 | /* | 28 | /* |
34 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | 29 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
@@ -38,8 +33,7 @@ interface ServiceSettings extends Instance<typeof ServiceSettings> {} | |||
38 | 33 | ||
39 | export default ServiceSettings; | 34 | export default ServiceSettings; |
40 | 35 | ||
41 | export interface ServiceSettingsSnapshotIn | 36 | export type { |
42 | extends SnapshotIn<typeof ServiceSettings> {} | 37 | ServiceSettingsSnapshotIn, |
43 | 38 | ServiceSettingsSnapshotOut, | |
44 | export interface ServiceSettingsSnapshotOut | 39 | } from '@sophie/shared'; |
45 | extends SnapshotOut<typeof ServiceSettings> {} | ||
diff --git a/packages/shared/src/stores/SharedStore.ts b/packages/renderer/src/stores/SharedStore.ts index d81a3d3..062479d 100644 --- a/packages/shared/src/stores/SharedStore.ts +++ b/packages/renderer/src/stores/SharedStore.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,27 +18,26 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { | 21 | import { defineSharedStoreModel } from '@sophie/shared'; |
22 | IJsonPatch, | 22 | import { Instance } from 'mobx-state-tree'; |
23 | Instance, | ||
24 | types, | ||
25 | SnapshotIn, | ||
26 | SnapshotOut, | ||
27 | } from 'mobx-state-tree'; | ||
28 | 23 | ||
29 | import GlobalSettings from './GlobalSettings'; | 24 | import GlobalSettings from './GlobalSettings'; |
30 | import Profile from './Profile'; | 25 | import Profile from './Profile'; |
31 | import Service from './Service'; | 26 | import Service from './Service'; |
32 | 27 | ||
33 | const SharedStore = /* @__PURE__ */ (() => | 28 | const SharedStore = defineSharedStoreModel( |
34 | types.model('SharedStore', { | 29 | GlobalSettings, |
35 | settings: types.optional(GlobalSettings, {}), | 30 | Profile, |
36 | profilesById: types.map(Profile), | 31 | Service, |
37 | profiles: types.array(types.reference(Profile)), | 32 | ).actions((self) => ({ |
38 | servicesById: types.map(Service), | 33 | toggleDarkMode(): void { |
39 | services: types.array(types.reference(Service)), | 34 | if (self.shouldUseDarkColors) { |
40 | shouldUseDarkColors: false, | 35 | self.settings.setThemeSource('light'); |
41 | }))(); | 36 | } else { |
37 | self.settings.setThemeSource('dark'); | ||
38 | } | ||
39 | }, | ||
40 | })); | ||
42 | 41 | ||
43 | /* | 42 | /* |
44 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | 43 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
@@ -48,13 +47,7 @@ interface SharedStore extends Instance<typeof SharedStore> {} | |||
48 | 47 | ||
49 | export default SharedStore; | 48 | export default SharedStore; |
50 | 49 | ||
51 | export interface SharedStoreSnapshotIn extends SnapshotIn<typeof SharedStore> {} | 50 | export type { |
52 | 51 | SharedStoreSnapshotIn, | |
53 | export interface SharedStoreSnapshotOut | 52 | SharedStoreSnapshotOut, |
54 | extends SnapshotOut<typeof SharedStore> {} | 53 | } from '@sophie/shared'; |
55 | |||
56 | export interface SharedStoreListener { | ||
57 | onSnapshot(snapshot: SharedStoreSnapshotIn): void; | ||
58 | |||
59 | onPatch(patches: IJsonPatch[]): void; | ||
60 | } | ||
diff --git a/packages/shared/src/contextBridge/SophieRenderer.ts b/packages/shared/src/contextBridge/SophieRenderer.ts index 28dc0b7..9e087da 100644 --- a/packages/shared/src/contextBridge/SophieRenderer.ts +++ b/packages/shared/src/contextBridge/SophieRenderer.ts | |||
@@ -19,7 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Action } from '../schemas'; | 21 | import { Action } from '../schemas'; |
22 | import { SharedStoreListener } from '../stores/SharedStore'; | 22 | import { SharedStoreListener } from '../stores/SharedStoreBase'; |
23 | 23 | ||
24 | export default interface SophieRenderer { | 24 | export default interface SophieRenderer { |
25 | onSharedStoreChange(this: void, listener: SharedStoreListener): Promise<void>; | 25 | onSharedStoreChange(this: void, listener: SharedStoreListener): Promise<void>; |
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 3d30488..66debf7 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts | |||
@@ -27,8 +27,11 @@ export { Action, BrowserViewBounds, ThemeSource } from './schemas'; | |||
27 | export type { | 27 | export type { |
28 | GlobalSettingsSnapshotIn, | 28 | GlobalSettingsSnapshotIn, |
29 | GlobalSettingsSnapshotOut, | 29 | GlobalSettingsSnapshotOut, |
30 | } from './stores/GlobalSettings'; | 30 | } from './stores/GlobalSettingsBase'; |
31 | export { default as GlobalSettings } from './stores/GlobalSettings'; | 31 | export { |
32 | default as GlobalSettingsBase, | ||
33 | defineGlobalSettingsModel, | ||
34 | } from './stores/GlobalSettingsBase'; | ||
32 | 35 | ||
33 | export { default as Profile } from './stores/Profile'; | 36 | export { default as Profile } from './stores/Profile'; |
34 | 37 | ||
@@ -38,17 +41,26 @@ export type { | |||
38 | } from './stores/ProfileSettings'; | 41 | } from './stores/ProfileSettings'; |
39 | export { default as ProfileSettings } from './stores/ProfileSettings'; | 42 | export { default as ProfileSettings } from './stores/ProfileSettings'; |
40 | 43 | ||
41 | export { default as Service } from './stores/Service'; | 44 | export { |
45 | default as ServiceBase, | ||
46 | defineServiceModel, | ||
47 | } from './stores/ServiceBase'; | ||
42 | 48 | ||
43 | export type { | 49 | export type { |
44 | ServiceSettingsSnapshotIn, | 50 | ServiceSettingsSnapshotIn, |
45 | ServiceSettingsSnapshotOut, | 51 | ServiceSettingsSnapshotOut, |
46 | } from './stores/ServiceSettings'; | 52 | } from './stores/ServiceSettingsBase'; |
47 | export { default as ServiceSettings } from './stores/ServiceSettings'; | 53 | export { |
54 | default as ServiceSettingsBase, | ||
55 | defineServiceSettingsModel, | ||
56 | } from './stores/ServiceSettingsBase'; | ||
48 | 57 | ||
49 | export type { | 58 | export type { |
50 | SharedStoreListener, | 59 | SharedStoreListener, |
51 | SharedStoreSnapshotIn, | 60 | SharedStoreSnapshotIn, |
52 | SharedStoreSnapshotOut, | 61 | SharedStoreSnapshotOut, |
53 | } from './stores/SharedStore'; | 62 | } from './stores/SharedStoreBase'; |
54 | export { default as SharedStore } from './stores/SharedStore'; | 63 | export { |
64 | default as SharedStoreBase, | ||
65 | defineSharedStoreModel, | ||
66 | } from './stores/SharedStoreBase'; | ||
diff --git a/packages/shared/src/stores/GlobalSettings.ts b/packages/shared/src/stores/GlobalSettingsBase.ts index f316af9..48092fd 100644 --- a/packages/shared/src/stores/GlobalSettings.ts +++ b/packages/shared/src/stores/GlobalSettingsBase.ts | |||
@@ -18,32 +18,44 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree'; | 21 | import { |
22 | Instance, | ||
23 | types, | ||
24 | SnapshotIn, | ||
25 | SnapshotOut, | ||
26 | IAnyModelType, | ||
27 | } from 'mobx-state-tree'; | ||
22 | 28 | ||
23 | import { ThemeSource } from '../schemas'; | 29 | import { ThemeSource } from '../schemas'; |
24 | 30 | ||
25 | import Service from './Service'; | 31 | import ServiceBase from './ServiceBase'; |
26 | 32 | ||
27 | const GlobalSettings = /* @__PURE__ */ (() => | 33 | export function defineGlobalSettingsModel<TS extends IAnyModelType>( |
28 | types.model('GlobalSettings', { | 34 | service: TS, |
35 | ) { | ||
36 | return types.model('GlobalSettings', { | ||
29 | themeSource: types.optional( | 37 | themeSource: types.optional( |
30 | types.enumeration(ThemeSource.options), | 38 | types.enumeration(ThemeSource.options), |
31 | 'system', | 39 | 'system', |
32 | ), | 40 | ), |
33 | showLocationBar: false, | 41 | showLocationBar: false, |
34 | selectedService: types.safeReference(Service), | 42 | selectedService: types.safeReference(service), |
35 | }))(); | 43 | }); |
44 | } | ||
45 | |||
46 | const GlobalSettingsBase = /* @__PURE__ */ (() => | ||
47 | defineGlobalSettingsModel(ServiceBase))(); | ||
36 | 48 | ||
37 | /* | 49 | /* |
38 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | 50 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
39 | Intentionally naming the type the same as the store definition. | 51 | Intentionally naming the type the same as the store definition. |
40 | */ | 52 | */ |
41 | interface GlobalSettings extends Instance<typeof GlobalSettings> {} | 53 | interface GlobalSettingsBase extends Instance<typeof GlobalSettingsBase> {} |
42 | 54 | ||
43 | export default GlobalSettings; | 55 | export default GlobalSettingsBase; |
44 | 56 | ||
45 | export interface GlobalSettingsSnapshotIn | 57 | export interface GlobalSettingsSnapshotIn |
46 | extends SnapshotIn<typeof GlobalSettings> {} | 58 | extends SnapshotIn<typeof GlobalSettingsBase> {} |
47 | 59 | ||
48 | export interface GlobalSettingsSnapshotOut | 60 | export interface GlobalSettingsSnapshotOut |
49 | extends SnapshotOut<typeof GlobalSettings> {} | 61 | extends SnapshotOut<typeof GlobalSettingsBase> {} |
diff --git a/packages/shared/src/stores/Service.ts b/packages/shared/src/stores/ServiceBase.ts index a4e3c92..cde403b 100644 --- a/packages/shared/src/stores/Service.ts +++ b/packages/shared/src/stores/ServiceBase.ts | |||
@@ -18,14 +18,14 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { Instance, types } from 'mobx-state-tree'; | 21 | import { IAnyModelType, Instance, types } from 'mobx-state-tree'; |
22 | 22 | ||
23 | import ServiceSettings from './ServiceSettings'; | 23 | import ServiceSettingsBase from './ServiceSettingsBase'; |
24 | 24 | ||
25 | const Service = /* @__PURE__ */ (() => | 25 | export function defineServiceModel<TS extends IAnyModelType>(settings: TS) { |
26 | types.model('Service', { | 26 | return types.model('Service', { |
27 | id: types.identifier, | 27 | id: types.identifier, |
28 | settings: ServiceSettings, | 28 | settings, |
29 | currentUrl: types.maybe(types.string), | 29 | currentUrl: types.maybe(types.string), |
30 | canGoBack: false, | 30 | canGoBack: false, |
31 | canGoForward: false, | 31 | canGoForward: false, |
@@ -36,12 +36,16 @@ const Service = /* @__PURE__ */ (() => | |||
36 | ), | 36 | ), |
37 | directMessageCount: 0, | 37 | directMessageCount: 0, |
38 | indirectMessageCount: 0, | 38 | indirectMessageCount: 0, |
39 | }))(); | 39 | }); |
40 | } | ||
41 | |||
42 | const ServiceBase = /* @__PURE__ */ (() => | ||
43 | defineServiceModel(ServiceSettingsBase))(); | ||
40 | 44 | ||
41 | /* | 45 | /* |
42 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | 46 | eslint-disable-next-line @typescript-eslint/no-redeclare -- |
43 | Intentionally naming the type the same as the store definition. | 47 | Intentionally naming the type the same as the store definition. |
44 | */ | 48 | */ |
45 | interface Service extends Instance<typeof Service> {} | 49 | interface ServiceBase extends Instance<typeof ServiceBase> {} |
46 | 50 | ||
47 | export default Service; | 51 | export default ServiceBase; |
diff --git a/packages/shared/src/stores/ServiceSettingsBase.ts b/packages/shared/src/stores/ServiceSettingsBase.ts new file mode 100644 index 0000000..45eb15d --- /dev/null +++ b/packages/shared/src/stores/ServiceSettingsBase.ts | |||
@@ -0,0 +1,57 @@ | |||
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 | Instance, | ||
23 | types, | ||
24 | SnapshotIn, | ||
25 | SnapshotOut, | ||
26 | IAnyModelType, | ||
27 | } from 'mobx-state-tree'; | ||
28 | |||
29 | import ProfileBase from './Profile'; | ||
30 | |||
31 | export function defineServiceSettingsModel<TP extends IAnyModelType>( | ||
32 | profile: TP, | ||
33 | ) { | ||
34 | return types.model('ServiceSettings', { | ||
35 | name: types.string, | ||
36 | profile: types.reference(profile), | ||
37 | // TODO: Remove this once recipes are added. | ||
38 | url: types.string, | ||
39 | }); | ||
40 | } | ||
41 | |||
42 | const ServiceSettingsBase = /* @__PURE__ */ (() => | ||
43 | defineServiceSettingsModel(ProfileBase))(); | ||
44 | |||
45 | /* | ||
46 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | ||
47 | Intentionally naming the type the same as the store definition. | ||
48 | */ | ||
49 | interface ServiceSettingsBase extends Instance<typeof ServiceSettingsBase> {} | ||
50 | |||
51 | export default ServiceSettingsBase; | ||
52 | |||
53 | export interface ServiceSettingsSnapshotIn | ||
54 | extends SnapshotIn<typeof ServiceSettingsBase> {} | ||
55 | |||
56 | export interface ServiceSettingsSnapshotOut | ||
57 | extends SnapshotOut<typeof ServiceSettingsBase> {} | ||
diff --git a/packages/shared/src/stores/SharedStoreBase.ts b/packages/shared/src/stores/SharedStoreBase.ts new file mode 100644 index 0000000..8d6624b --- /dev/null +++ b/packages/shared/src/stores/SharedStoreBase.ts | |||
@@ -0,0 +1,70 @@ | |||
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 { | ||
22 | IJsonPatch, | ||
23 | Instance, | ||
24 | types, | ||
25 | SnapshotIn, | ||
26 | SnapshotOut, | ||
27 | IAnyModelType, | ||
28 | } from 'mobx-state-tree'; | ||
29 | |||
30 | import GlobalSettingsBase from './GlobalSettingsBase'; | ||
31 | import ProfileBase from './Profile'; | ||
32 | import ServiceBase from './ServiceBase'; | ||
33 | |||
34 | export function defineSharedStoreModel< | ||
35 | TG extends IAnyModelType, | ||
36 | TP extends IAnyModelType, | ||
37 | TS extends IAnyModelType, | ||
38 | >(globalSettings: TG, profile: TP, service: TS) { | ||
39 | return types.model('SharedStore', { | ||
40 | settings: types.optional(globalSettings, {}), | ||
41 | profilesById: types.map(profile), | ||
42 | profiles: types.array(types.reference(profile)), | ||
43 | servicesById: types.map(service), | ||
44 | services: types.array(types.reference(service)), | ||
45 | shouldUseDarkColors: false, | ||
46 | }); | ||
47 | } | ||
48 | |||
49 | const SharedStoreBase = /* @__PURE__ */ (() => | ||
50 | defineSharedStoreModel(GlobalSettingsBase, ProfileBase, ServiceBase))(); | ||
51 | |||
52 | /* | ||
53 | eslint-disable-next-line @typescript-eslint/no-redeclare -- | ||
54 | Intentionally naming the type the same as the store definition. | ||
55 | */ | ||
56 | interface SharedStoreBase extends Instance<typeof SharedStoreBase> {} | ||
57 | |||
58 | export default SharedStoreBase; | ||
59 | |||
60 | export interface SharedStoreSnapshotIn | ||
61 | extends SnapshotIn<typeof SharedStoreBase> {} | ||
62 | |||
63 | export interface SharedStoreSnapshotOut | ||
64 | extends SnapshotOut<typeof SharedStoreBase> {} | ||
65 | |||
66 | export interface SharedStoreListener { | ||
67 | onSnapshot(snapshot: SharedStoreSnapshotIn): void; | ||
68 | |||
69 | onPatch(patches: IJsonPatch[]): void; | ||
70 | } | ||