aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-28 01:07:56 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-08 21:43:18 +0100
commit24bbe1f574ee6b0d6d0ebc8c29cbf014fc450584 (patch)
treee89b2d700930d6722eb0685d8d3a9be91df851cf
parenttest: Add tests for main window hardening (diff)
downloadsophie-24bbe1f574ee6b0d6d0ebc8c29cbf014fc450584.tar.gz
sophie-24bbe1f574ee6b0d6d0ebc8c29cbf014fc450584.tar.zst
sophie-24bbe1f574ee6b0d6d0ebc8c29cbf014fc450584.zip
feat: Save selected service to file
Sophie start off from the service that was selected when it was last open. Signed-off-by: Kristóf Marussy <kristof@marussy.com>
-rw-r--r--packages/main/src/index.ts2
-rw-r--r--packages/main/src/infrastructure/config/impl/ConfigFile.ts4
-rw-r--r--packages/main/src/reactions/synchronizeConfig.ts5
-rw-r--r--packages/main/src/stores/GlobalSettings.ts22
-rw-r--r--packages/main/src/stores/SharedStore.ts13
-rw-r--r--packages/main/src/stores/config/loadConfig.ts28
-rw-r--r--packages/renderer/src/components/ServiceSwitcher.tsx5
-rw-r--r--packages/renderer/src/stores/RendererStore.ts7
-rw-r--r--packages/shared/src/stores/GlobalSettings.ts3
-rw-r--r--packages/shared/src/stores/SharedStore.ts1
10 files changed, 49 insertions, 41 deletions
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index 2072017..f5f6be4 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -205,7 +205,7 @@ async function createWindow(): Promise<unknown> {
205 const actionToDispatch = Action.parse(rawAction); 205 const actionToDispatch = Action.parse(rawAction);
206 switch (actionToDispatch.action) { 206 switch (actionToDispatch.action) {
207 case 'set-selected-service-id': 207 case 'set-selected-service-id':
208 store.shared.setSelectedServiceId(actionToDispatch.serviceId); 208 store.settings.setSelectedServiceId(actionToDispatch.serviceId);
209 break; 209 break;
210 case 'set-browser-view-bounds': 210 case 'set-browser-view-bounds':
211 store.setBrowserViewBounds(actionToDispatch.browserViewBounds); 211 store.setBrowserViewBounds(actionToDispatch.browserViewBounds);
diff --git a/packages/main/src/infrastructure/config/impl/ConfigFile.ts b/packages/main/src/infrastructure/config/impl/ConfigFile.ts
index 90ee187..e8237b4 100644
--- a/packages/main/src/infrastructure/config/impl/ConfigFile.ts
+++ b/packages/main/src/infrastructure/config/impl/ConfigFile.ts
@@ -29,7 +29,7 @@ import type Config from '../../../stores/config/Config';
29import type Disposer from '../../../utils/Disposer'; 29import type Disposer from '../../../utils/Disposer';
30import { getLogger } from '../../../utils/log'; 30import { getLogger } from '../../../utils/log';
31import type ConfigRepository from '../ConfigRepository'; 31import type ConfigRepository from '../ConfigRepository';
32import type ReadConfigResult from '../ReadConfigResult'; 32import type { ReadConfigResult } from '../ConfigRepository';
33 33
34const log = getLogger('ConfigFile'); 34const log = getLogger('ConfigFile');
35 35
@@ -81,7 +81,7 @@ export default class ConfigFile implements ConfigRepository {
81 } finally { 81 } finally {
82 this.#writingConfig = false; 82 this.#writingConfig = false;
83 } 83 }
84 log.info('Wrote config file', this.#configFilePath); 84 log.debug('Wrote config file', this.#configFilePath);
85 } 85 }
86 86
87 watchConfig(callback: () => Promise<void>, throttleMs: number): Disposer { 87 watchConfig(callback: () => Promise<void>, throttleMs: number): Disposer {
diff --git a/packages/main/src/reactions/synchronizeConfig.ts b/packages/main/src/reactions/synchronizeConfig.ts
index 4a9c24b..9436c16 100644
--- a/packages/main/src/reactions/synchronizeConfig.ts
+++ b/packages/main/src/reactions/synchronizeConfig.ts
@@ -58,6 +58,9 @@ export default async function synchronizeConfig(
58 return true; 58 return true;
59 } 59 }
60 lastConfigOnDisk = sharedStore.config; 60 lastConfigOnDisk = sharedStore.config;
61 // We can't use `comparer.structural` from `mobx`, because
62 // it handles missing values and `undefined` values differently,
63 // but JSON5 is unable to distinguish them.
61 if (!deepEqual(result.data, lastConfigOnDisk, { strict: true })) { 64 if (!deepEqual(result.data, lastConfigOnDisk, { strict: true })) {
62 await writeConfig(); 65 await writeConfig();
63 } 66 }
@@ -75,7 +78,7 @@ export default async function synchronizeConfig(
75 () => sharedStore.config, 78 () => sharedStore.config,
76 debounce((config) => { 79 debounce((config) => {
77 // We can compare snapshots by reference, since it is only recreated on store changes. 80 // We can compare snapshots by reference, since it is only recreated on store changes.
78 if (lastConfigOnDisk !== config) { 81 if (!deepEqual(config, lastConfigOnDisk, { strict: true })) {
79 writeConfig().catch((error) => { 82 writeConfig().catch((error) => {
80 log.error('Failed to write config on config change', error); 83 log.error('Failed to write config on config change', error);
81 }); 84 });
diff --git a/packages/main/src/stores/GlobalSettings.ts b/packages/main/src/stores/GlobalSettings.ts
index 0a54aa3..7bbc089 100644
--- a/packages/main/src/stores/GlobalSettings.ts
+++ b/packages/main/src/stores/GlobalSettings.ts
@@ -22,12 +22,30 @@ import {
22 GlobalSettings as GlobalSettingsBase, 22 GlobalSettings as GlobalSettingsBase,
23 ThemeSource, 23 ThemeSource,
24} from '@sophie/shared'; 24} from '@sophie/shared';
25import { Instance } from 'mobx-state-tree'; 25import { Instance, resolveIdentifier, types } from 'mobx-state-tree';
26 26
27const GlobalSettings = GlobalSettingsBase.actions((self) => ({ 27import { getLogger } from '../utils/log';
28import overrideProps from '../utils/overrideProps';
29
30import Service from './Service';
31
32const log = getLogger('sharedStore');
33
34const GlobalSettings = overrideProps(GlobalSettingsBase, {
35 selectedService: types.safeReference(Service),
36}).actions((self) => ({
28 setThemeSource(mode: ThemeSource): void { 37 setThemeSource(mode: ThemeSource): void {
29 self.themeSource = mode; 38 self.themeSource = mode;
30 }, 39 },
40 setSelectedServiceId(serviceId: string): void {
41 const serviceInstance = resolveIdentifier(Service, self, serviceId);
42 if (serviceInstance === undefined) {
43 log.warn('Trying to select unknown service', serviceId);
44 return;
45 }
46 self.selectedService = serviceInstance;
47 log.debug('Selected service', serviceId);
48 },
31})); 49}));
32 50
33/* 51/*
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts
index b9983f6..182b693 100644
--- a/packages/main/src/stores/SharedStore.ts
+++ b/packages/main/src/stores/SharedStore.ts
@@ -21,7 +21,6 @@
21import { SharedStore as SharedStoreBase } from '@sophie/shared'; 21import { SharedStore as SharedStoreBase } from '@sophie/shared';
22import { getSnapshot, Instance, types } from 'mobx-state-tree'; 22import { getSnapshot, Instance, types } from 'mobx-state-tree';
23 23
24import { getLogger } from '../utils/log';
25import overrideProps from '../utils/overrideProps'; 24import overrideProps from '../utils/overrideProps';
26 25
27import GlobalSettings from './GlobalSettings'; 26import GlobalSettings from './GlobalSettings';
@@ -30,8 +29,6 @@ import Service from './Service';
30import type Config from './config/Config'; 29import type Config from './config/Config';
31import loadConfig from './config/loadConfig'; 30import loadConfig from './config/loadConfig';
32 31
33const log = getLogger('sharedStore');
34
35function getConfigs<T>(models: { config: T }[]): T[] | undefined { 32function getConfigs<T>(models: { config: T }[]): T[] | undefined {
36 return models.length === 0 ? undefined : models.map((model) => model.config); 33 return models.length === 0 ? undefined : models.map((model) => model.config);
37} 34}
@@ -42,7 +39,6 @@ const SharedStore = overrideProps(SharedStoreBase, {
42 profiles: types.array(types.reference(Profile)), 39 profiles: types.array(types.reference(Profile)),
43 servicesById: types.map(Service), 40 servicesById: types.map(Service),
44 services: types.array(types.reference(Service)), 41 services: types.array(types.reference(Service)),
45 selectedService: types.safeReference(Service),
46}) 42})
47 .views((self) => ({ 43 .views((self) => ({
48 get config(): Config { 44 get config(): Config {
@@ -62,15 +58,6 @@ const SharedStore = overrideProps(SharedStoreBase, {
62 setShouldUseDarkColors(shouldUseDarkColors: boolean): void { 58 setShouldUseDarkColors(shouldUseDarkColors: boolean): void {
63 self.shouldUseDarkColors = shouldUseDarkColors; 59 self.shouldUseDarkColors = shouldUseDarkColors;
64 }, 60 },
65 setSelectedServiceId(serviceId: string): void {
66 const serviceInstance = self.servicesById.get(serviceId);
67 if (serviceInstance === undefined) {
68 log.warn('Trying to select unknown service', serviceId);
69 return;
70 }
71 self.selectedService = serviceInstance;
72 log.debug('Selected service', serviceId);
73 },
74 })); 61 }));
75 62
76/* 63/*
diff --git a/packages/main/src/stores/config/loadConfig.ts b/packages/main/src/stores/config/loadConfig.ts
index 770d675..55d15c8 100644
--- a/packages/main/src/stores/config/loadConfig.ts
+++ b/packages/main/src/stores/config/loadConfig.ts
@@ -106,22 +106,13 @@ export default function loadConfig(
106 target: { 106 target: {
107 readonly profiles: IMSTArray<IReferenceType<typeof Profile>>; 107 readonly profiles: IMSTArray<IReferenceType<typeof Profile>>;
108 readonly profilesById: IMSTMap<typeof Profile>; 108 readonly profilesById: IMSTMap<typeof Profile>;
109 selectedService: Service | undefined;
110 readonly services: IMSTArray<IReferenceType<typeof Service>>; 109 readonly services: IMSTArray<IReferenceType<typeof Service>>;
111 readonly servicesById: IMSTMap<typeof Service>; 110 readonly servicesById: IMSTMap<typeof Service>;
112 readonly settings: GlobalSettings; 111 readonly settings: GlobalSettings;
113 }, 112 },
114 config: Config, 113 config: Config,
115): void { 114): void {
116 const { 115 const { profiles, profilesById, services, servicesById, settings } = target;
117 profiles,
118 profilesById,
119 selectedService,
120 services,
121 servicesById,
122 settings,
123 } = target;
124 const { id: selectedServiceId } = selectedService ?? { id: undefined };
125 const { 116 const {
126 profiles: profilesConfig, 117 profiles: profilesConfig,
127 services: servicesConfig, 118 services: servicesConfig,
@@ -134,13 +125,16 @@ export default function loadConfig(
134 ); 125 );
135 applySettings(profiles, profilesById, profilesToApply); 126 applySettings(profiles, profilesById, profilesToApply);
136 applySettings(services, servicesById, servicesToApply); 127 applySettings(services, servicesById, servicesToApply);
137 applySnapshot(settings, settingsToApply); 128 const { selectedService } = settingsToApply;
138 let newSelectedService: Service | undefined; 129 // Be more robust against when a deleted service is selected.
139 if (selectedServiceId !== undefined) { 130 if (
140 newSelectedService = servicesById.get(selectedServiceId); 131 typeof selectedService !== 'string' ||
132 !servicesById.has(selectedService)
133 ) {
134 settingsToApply.selectedService = undefined;
141 } 135 }
142 if (newSelectedService === undefined && services.length > 0) { 136 applySnapshot(settings, settingsToApply);
143 [newSelectedService] = services; 137 if (settings.selectedService === undefined && services.length > 0) {
138 [settings.selectedService] = services;
144 } 139 }
145 target.selectedService = newSelectedService;
146} 140}
diff --git a/packages/renderer/src/components/ServiceSwitcher.tsx b/packages/renderer/src/components/ServiceSwitcher.tsx
index 167153f..e4d371e 100644
--- a/packages/renderer/src/components/ServiceSwitcher.tsx
+++ b/packages/renderer/src/components/ServiceSwitcher.tsx
@@ -63,7 +63,10 @@ const ServiceSwitcherTab = styled(Tab, {
63 63
64export default observer(() => { 64export default observer(() => {
65 const store = useStore(); 65 const store = useStore();
66 const { selectedService, services } = store; 66 const {
67 settings: { selectedService },
68 services,
69 } = store;
67 70
68 return ( 71 return (
69 <ServiceSwitcherRoot 72 <ServiceSwitcherRoot
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts
index c5a94df..4d85929 100644
--- a/packages/renderer/src/stores/RendererStore.ts
+++ b/packages/renderer/src/stores/RendererStore.ts
@@ -24,6 +24,7 @@ import {
24 Service, 24 Service,
25 SophieRenderer, 25 SophieRenderer,
26 ThemeSource, 26 ThemeSource,
27 GlobalSettings,
27} from '@sophie/shared'; 28} from '@sophie/shared';
28import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; 29import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree';
29 30
@@ -38,12 +39,12 @@ const RendererStore = types
38 shared: types.optional(SharedStore, {}), 39 shared: types.optional(SharedStore, {}),
39 }) 40 })
40 .views((self) => ({ 41 .views((self) => ({
42 get settings(): GlobalSettings {
43 return self.shared.settings;
44 },
41 get services(): Service[] { 45 get services(): Service[] {
42 return self.shared.services; 46 return self.shared.services;
43 }, 47 },
44 get selectedService(): Service | undefined {
45 return self.shared.selectedService;
46 },
47 })) 48 }))
48 .actions((self) => ({ 49 .actions((self) => ({
49 setSelectedServiceId(serviceId: string): void { 50 setSelectedServiceId(serviceId: string): void {
diff --git a/packages/shared/src/stores/GlobalSettings.ts b/packages/shared/src/stores/GlobalSettings.ts
index 1c6d7f3..7a2c334 100644
--- a/packages/shared/src/stores/GlobalSettings.ts
+++ b/packages/shared/src/stores/GlobalSettings.ts
@@ -22,12 +22,15 @@ import { Instance, types, SnapshotIn, SnapshotOut } from 'mobx-state-tree';
22 22
23import { ThemeSource } from '../schemas'; 23import { ThemeSource } from '../schemas';
24 24
25import Service from './Service';
26
25const GlobalSettings = /* @__PURE__ */ (() => 27const GlobalSettings = /* @__PURE__ */ (() =>
26 types.model('GlobalSettings', { 28 types.model('GlobalSettings', {
27 themeSource: types.optional( 29 themeSource: types.optional(
28 types.enumeration(ThemeSource.options), 30 types.enumeration(ThemeSource.options),
29 'system', 31 'system',
30 ), 32 ),
33 selectedService: types.safeReference(Service),
31 }))(); 34 }))();
32 35
33/* 36/*
diff --git a/packages/shared/src/stores/SharedStore.ts b/packages/shared/src/stores/SharedStore.ts
index 4e064c6..d81a3d3 100644
--- a/packages/shared/src/stores/SharedStore.ts
+++ b/packages/shared/src/stores/SharedStore.ts
@@ -37,7 +37,6 @@ const SharedStore = /* @__PURE__ */ (() =>
37 profiles: types.array(types.reference(Profile)), 37 profiles: types.array(types.reference(Profile)),
38 servicesById: types.map(Service), 38 servicesById: types.map(Service),
39 services: types.array(types.reference(Service)), 39 services: types.array(types.reference(Service)),
40 selectedService: types.safeReference(Service),
41 shouldUseDarkColors: false, 40 shouldUseDarkColors: false,
42 }))(); 41 }))();
43 42