aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-01-27 01:02:02 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-02-08 21:43:17 +0100
commitda610a51f49807d1409f36b98e06e89447a4202b (patch)
treea4f7911f45ca52626ef02f370c3f260d33f8272e
parentrefactor: Coding conventions (diff)
downloadsophie-da610a51f49807d1409f36b98e06e89447a4202b.tar.gz
sophie-da610a51f49807d1409f36b98e06e89447a4202b.tar.zst
sophie-da610a51f49807d1409f36b98e06e89447a4202b.zip
refactor: Extract config handling
Move the handling of the contents of the config file out of the stores and into dedicated files to simplify the code of the stores. Signed-off-by: Kristóf Marussy <kristof@marussy.com>
-rw-r--r--packages/main/src/infrastructure/config/ConfigRepository.ts2
-rw-r--r--packages/main/src/infrastructure/config/impl/ConfigFile.ts (renamed from packages/main/src/infrastructure/config/ConfigFile.ts)11
-rw-r--r--packages/main/src/initReactions.ts2
-rw-r--r--packages/main/src/reactions/synchronizeConfig.ts2
-rw-r--r--packages/main/src/stores/Profile.ts30
-rw-r--r--packages/main/src/stores/ProfileSettings.ts30
-rw-r--r--packages/main/src/stores/Service.ts35
-rw-r--r--packages/main/src/stores/SharedStore.ts91
-rw-r--r--packages/main/src/stores/__tests__/SharedStore.spec.ts7
-rw-r--r--packages/main/src/stores/config/Config.ts30
-rw-r--r--packages/main/src/stores/config/ProfileConfig.ts (renamed from packages/main/src/utils/generateId.ts)10
-rw-r--r--packages/main/src/stores/config/ServiceConfig.ts29
-rw-r--r--packages/main/src/stores/config/loadConfig.ts146
13 files changed, 268 insertions, 157 deletions
diff --git a/packages/main/src/infrastructure/config/ConfigRepository.ts b/packages/main/src/infrastructure/config/ConfigRepository.ts
index 0ce7fc1..e00f5a0 100644
--- a/packages/main/src/infrastructure/config/ConfigRepository.ts
+++ b/packages/main/src/infrastructure/config/ConfigRepository.ts
@@ -18,7 +18,7 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import type { Config } from '../../stores/SharedStore'; 21import type Config from '../../stores/config/Config';
22import type Disposer from '../../utils/Disposer'; 22import type Disposer from '../../utils/Disposer';
23 23
24export type ReadConfigResult = 24export type ReadConfigResult =
diff --git a/packages/main/src/infrastructure/config/ConfigFile.ts b/packages/main/src/infrastructure/config/impl/ConfigFile.ts
index 193a20d..90ee187 100644
--- a/packages/main/src/infrastructure/config/ConfigFile.ts
+++ b/packages/main/src/infrastructure/config/impl/ConfigFile.ts
@@ -25,12 +25,11 @@ import path from 'node:path';
25import JSON5 from 'json5'; 25import JSON5 from 'json5';
26import { throttle } from 'lodash-es'; 26import { throttle } from 'lodash-es';
27 27
28import type { Config } from '../../stores/SharedStore'; 28import 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';
31 31import type ConfigRepository from '../ConfigRepository';
32import type ConfigRepository from './ConfigRepository'; 32import type ReadConfigResult from '../ReadConfigResult';
33import type ReadConfigResult from './ReadConfigResult';
34 33
35const log = getLogger('ConfigFile'); 34const log = getLogger('ConfigFile');
36 35
diff --git a/packages/main/src/initReactions.ts b/packages/main/src/initReactions.ts
index 50e561d..87ad425 100644
--- a/packages/main/src/initReactions.ts
+++ b/packages/main/src/initReactions.ts
@@ -20,7 +20,7 @@
20 20
21import { app } from 'electron'; 21import { app } from 'electron';
22 22
23import ConfigFile from './infrastructure/config/ConfigFile'; 23import ConfigFile from './infrastructure/config/impl/ConfigFile';
24import synchronizeConfig from './reactions/synchronizeConfig'; 24import synchronizeConfig from './reactions/synchronizeConfig';
25import synchronizeNativeTheme from './reactions/synchronizeNativeTheme'; 25import synchronizeNativeTheme from './reactions/synchronizeNativeTheme';
26import type MainStore from './stores/MainStore'; 26import type MainStore from './stores/MainStore';
diff --git a/packages/main/src/reactions/synchronizeConfig.ts b/packages/main/src/reactions/synchronizeConfig.ts
index 480cc1a..4a9c24b 100644
--- a/packages/main/src/reactions/synchronizeConfig.ts
+++ b/packages/main/src/reactions/synchronizeConfig.ts
@@ -25,7 +25,7 @@ import ms from 'ms';
25 25
26import type ConfigRepository from '../infrastructure/config/ConfigRepository'; 26import type ConfigRepository from '../infrastructure/config/ConfigRepository';
27import type SharedStore from '../stores/SharedStore'; 27import type SharedStore from '../stores/SharedStore';
28import type { Config } from '../stores/SharedStore'; 28import 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';
31 31
diff --git a/packages/main/src/stores/Profile.ts b/packages/main/src/stores/Profile.ts
index ec2a64b..0fd486e 100644
--- a/packages/main/src/stores/Profile.ts
+++ b/packages/main/src/stores/Profile.ts
@@ -18,19 +18,17 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { 21import { Profile as ProfileBase } from '@sophie/shared';
22 Profile as ProfileBase,
23 ProfileSettingsSnapshotIn,
24} from '@sophie/shared';
25import { getSnapshot, Instance } from 'mobx-state-tree'; 22import { getSnapshot, Instance } from 'mobx-state-tree';
26 23
27import generateId from '../utils/generateId'; 24import overrideProps from '../utils/overrideProps';
28 25
29export interface ProfileConfig extends ProfileSettingsSnapshotIn { 26import ProfileSettings from './ProfileSettings';
30 id?: string | undefined; 27import type ProfileConfig from './config/ProfileConfig';
31}
32 28
33const Profile = ProfileBase.views((self) => ({ 29const Profile = overrideProps(ProfileBase, {
30 settings: ProfileSettings,
31}).views((self) => ({
34 get config(): ProfileConfig { 32 get config(): ProfileConfig {
35 const { id, settings } = self; 33 const { id, settings } = self;
36 return { ...getSnapshot(settings), id }; 34 return { ...getSnapshot(settings), id };
@@ -44,17 +42,3 @@ const Profile = ProfileBase.views((self) => ({
44interface Profile extends Instance<typeof Profile> {} 42interface Profile extends Instance<typeof Profile> {}
45 43
46export default Profile; 44export default Profile;
47
48export type ProfileSettingsSnapshotWithId = [string, ProfileSettingsSnapshotIn];
49
50export function addMissingProfileIds(
51 profileConfigs: ProfileConfig[] | undefined,
52): ProfileSettingsSnapshotWithId[] {
53 return (profileConfigs ?? []).map((profileConfig) => {
54 const { id, ...settings } = profileConfig;
55 return [
56 typeof id === 'undefined' ? generateId(settings.name) : id,
57 settings,
58 ];
59 });
60}
diff --git a/packages/main/src/stores/ProfileSettings.ts b/packages/main/src/stores/ProfileSettings.ts
new file mode 100644
index 0000000..eed51e3
--- /dev/null
+++ b/packages/main/src/stores/ProfileSettings.ts
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { ProfileSettings } from '@sophie/shared';
22
23// TODO Export a modified ProfileSettings once we need to add actions to it.
24// eslint-disable-next-line unicorn/prefer-export-from -- Can't export from default.
25export default ProfileSettings;
26
27export type {
28 ProfileSettingsSnapshotIn,
29 ProfileSettingsSnapshotOut,
30} from '@sophie/shared';
diff --git a/packages/main/src/stores/Service.ts b/packages/main/src/stores/Service.ts
index e70caa6..fea0bdf 100644
--- a/packages/main/src/stores/Service.ts
+++ b/packages/main/src/stores/Service.ts
@@ -20,20 +20,12 @@
20 20
21import type { UnreadCount } from '@sophie/service-shared'; 21import type { UnreadCount } from '@sophie/service-shared';
22import { Service as ServiceBase } from '@sophie/shared'; 22import { Service as ServiceBase } from '@sophie/shared';
23import { Instance, getSnapshot, ReferenceIdentifier } from 'mobx-state-tree'; 23import { Instance, getSnapshot } from 'mobx-state-tree';
24 24
25import generateId from '../utils/generateId';
26import overrideProps from '../utils/overrideProps'; 25import overrideProps from '../utils/overrideProps';
27 26
28import { ProfileSettingsSnapshotWithId } from './Profile'; 27import ServiceSettings from './ServiceSettings';
29import ServiceSettings, { ServiceSettingsSnapshotIn } from './ServiceSettings'; 28import type ServiceConfig from './config/ServiceConfig';
30
31export interface ServiceConfig
32 extends Omit<ServiceSettingsSnapshotIn, 'profile'> {
33 id?: string | undefined;
34
35 profile?: ReferenceIdentifier | undefined;
36}
37 29
38const Service = overrideProps(ServiceBase, { 30const Service = overrideProps(ServiceBase, {
39 settings: ServiceSettings, 31 settings: ServiceSettings,
@@ -90,24 +82,3 @@ const Service = overrideProps(ServiceBase, {
90interface Service extends Instance<typeof Service> {} 82interface Service extends Instance<typeof Service> {}
91 83
92export default Service; 84export default Service;
93
94export type ServiceSettingsSnapshotWithId = [string, ServiceSettingsSnapshotIn];
95
96export function addMissingServiceIdsAndProfiles(
97 serviceConfigs: ServiceConfig[] | undefined,
98 profiles: ProfileSettingsSnapshotWithId[],
99): ServiceSettingsSnapshotWithId[] {
100 return (serviceConfigs ?? []).map((serviceConfig) => {
101 const { id, ...settings } = serviceConfig;
102 const { name } = settings;
103 let { profile: profileId } = settings;
104 if (profileId === undefined) {
105 profileId = generateId(name);
106 profiles.push([profileId, { name }]);
107 }
108 return [
109 id === undefined ? generateId(name) : id,
110 { ...settings, profile: profileId },
111 ];
112 });
113}
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts
index c34af75..b9983f6 100644
--- a/packages/main/src/stores/SharedStore.ts
+++ b/packages/main/src/stores/SharedStore.ts
@@ -19,71 +19,23 @@
19 */ 19 */
20 20
21import { SharedStore as SharedStoreBase } from '@sophie/shared'; 21import { SharedStore as SharedStoreBase } from '@sophie/shared';
22import { 22import { getSnapshot, Instance, types } from 'mobx-state-tree';
23 applySnapshot,
24 getSnapshot,
25 IMSTArray,
26 IMSTMap,
27 Instance,
28 IReferenceType,
29 IStateTreeNode,
30 IType,
31 types,
32} from 'mobx-state-tree';
33 23
34import { getLogger } from '../utils/log'; 24import { getLogger } from '../utils/log';
35import overrideProps from '../utils/overrideProps'; 25import overrideProps from '../utils/overrideProps';
36 26
37import GlobalSettings, { GlobalSettingsSnapshotIn } from './GlobalSettings'; 27import GlobalSettings from './GlobalSettings';
38import Profile, { addMissingProfileIds, ProfileConfig } from './Profile'; 28import Profile from './Profile';
39import Service, { 29import Service from './Service';
40 addMissingServiceIdsAndProfiles, 30import type Config from './config/Config';
41 ServiceConfig, 31import loadConfig from './config/loadConfig';
42} from './Service';
43 32
44const log = getLogger('sharedStore'); 33const log = getLogger('sharedStore');
45 34
46export interface Config extends GlobalSettingsSnapshotIn {
47 profiles?: ProfileConfig[] | undefined;
48
49 services?: ServiceConfig[] | undefined;
50}
51
52function getConfigs<T>(models: { config: T }[]): T[] | undefined { 35function getConfigs<T>(models: { config: T }[]): T[] | undefined {
53 return models.length === 0 ? undefined : models.map((model) => model.config); 36 return models.length === 0 ? undefined : models.map((model) => model.config);
54} 37}
55 38
56function applySettings<
57 C,
58 D extends IType<
59 { id: string; settings: C },
60 unknown,
61 { settings: IStateTreeNode<IType<C, unknown, unknown>> }
62 >,
63>(
64 current: IMSTArray<IReferenceType<D>>,
65 currentById: IMSTMap<D>,
66 toApply: [string, C][],
67): void {
68 const toApplyById = new Map(toApply);
69 const toDelete = new Set(currentById.keys());
70 toApplyById.forEach((settingsSnapshot, id) => {
71 const model = currentById.get(id);
72 if (model === undefined) {
73 currentById.set(id, {
74 id,
75 settings: settingsSnapshot,
76 });
77 } else {
78 toDelete.delete(id);
79 applySnapshot(model.settings, settingsSnapshot);
80 }
81 });
82 current.clear();
83 toDelete.forEach((id) => currentById.delete(id));
84 current.push(...toApply.map(([id]) => id));
85}
86
87const SharedStore = overrideProps(SharedStoreBase, { 39const SharedStore = overrideProps(SharedStoreBase, {
88 settings: types.optional(GlobalSettings, {}), 40 settings: types.optional(GlobalSettings, {}),
89 profilesById: types.map(Profile), 41 profilesById: types.map(Profile),
@@ -105,36 +57,7 @@ const SharedStore = overrideProps(SharedStoreBase, {
105 })) 57 }))
106 .actions((self) => ({ 58 .actions((self) => ({
107 loadConfig(config: Config): void { 59 loadConfig(config: Config): void {
108 const { 60 loadConfig(self, config);
109 profiles,
110 profilesById,
111 selectedService,
112 services,
113 servicesById,
114 settings,
115 } = self;
116 const { id: selectedServiceId } = selectedService ?? { id: undefined };
117 const {
118 profiles: profilesConfig,
119 services: servicesConfig,
120 ...settingsToApply
121 } = config;
122 const profilesToApply = addMissingProfileIds(profilesConfig);
123 const servicesToApply = addMissingServiceIdsAndProfiles(
124 servicesConfig,
125 profilesToApply,
126 );
127 applySettings(profiles, profilesById, profilesToApply);
128 applySettings(services, servicesById, servicesToApply);
129 applySnapshot(settings, settingsToApply);
130 let newSelectedService;
131 if (selectedServiceId !== undefined) {
132 newSelectedService = servicesById.get(selectedServiceId);
133 }
134 if (newSelectedService === undefined && services.length > 0) {
135 [newSelectedService] = services;
136 }
137 self.selectedService = newSelectedService;
138 }, 61 },
139 setShouldUseDarkColors(shouldUseDarkColors: boolean): void { 62 setShouldUseDarkColors(shouldUseDarkColors: boolean): void {
140 self.shouldUseDarkColors = shouldUseDarkColors; 63 self.shouldUseDarkColors = shouldUseDarkColors;
diff --git a/packages/main/src/stores/__tests__/SharedStore.spec.ts b/packages/main/src/stores/__tests__/SharedStore.spec.ts
index dfd59a1..268ce3f 100644
--- a/packages/main/src/stores/__tests__/SharedStore.spec.ts
+++ b/packages/main/src/stores/__tests__/SharedStore.spec.ts
@@ -18,9 +18,10 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import type { ProfileConfig } from '../Profile'; 21import SharedStore from '../SharedStore';
22import type { ServiceConfig } from '../Service'; 22import type Config from '../config/Config';
23import SharedStore, { Config } from '../SharedStore'; 23import type ProfileConfig from '../config/ProfileConfig';
24import type ServiceConfig from '../config/ServiceConfig';
24 25
25const profileProps: ProfileConfig = { 26const profileProps: ProfileConfig = {
26 name: 'Test profile', 27 name: 'Test profile',
diff --git a/packages/main/src/stores/config/Config.ts b/packages/main/src/stores/config/Config.ts
new file mode 100644
index 0000000..c38e3c5
--- /dev/null
+++ b/packages/main/src/stores/config/Config.ts
@@ -0,0 +1,30 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import type { GlobalSettingsSnapshotIn } from '../GlobalSettings';
22
23import type ProfileConfig from './ProfileConfig';
24import type ServiceConfig from './ServiceConfig';
25
26export default interface Config extends GlobalSettingsSnapshotIn {
27 profiles?: ProfileConfig[] | undefined;
28
29 services?: ServiceConfig[] | undefined;
30}
diff --git a/packages/main/src/utils/generateId.ts b/packages/main/src/stores/config/ProfileConfig.ts
index 8a87e5a..ce276d4 100644
--- a/packages/main/src/utils/generateId.ts
+++ b/packages/main/src/stores/config/ProfileConfig.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,10 +18,8 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { nanoid } from 'nanoid'; 21import { ProfileSettingsSnapshotIn } from '@sophie/shared';
22import slug from 'slug';
23 22
24export default function generateId(name?: string | undefined) { 23export default interface ProfileConfig extends ProfileSettingsSnapshotIn {
25 const nameSlug = typeof name === 'undefined' ? '' : slug(name); 24 id?: string | undefined;
26 return `${nameSlug}_${nanoid()}`;
27} 25}
diff --git a/packages/main/src/stores/config/ServiceConfig.ts b/packages/main/src/stores/config/ServiceConfig.ts
new file mode 100644
index 0000000..40ea4c9
--- /dev/null
+++ b/packages/main/src/stores/config/ServiceConfig.ts
@@ -0,0 +1,29 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { ServiceSettingsSnapshotIn } from '@sophie/shared';
22import { ReferenceIdentifier } from 'mobx-state-tree';
23
24export default interface ServiceConfig
25 extends Omit<ServiceSettingsSnapshotIn, 'profile'> {
26 id?: string | undefined;
27
28 profile?: ReferenceIdentifier | undefined;
29}
diff --git a/packages/main/src/stores/config/loadConfig.ts b/packages/main/src/stores/config/loadConfig.ts
new file mode 100644
index 0000000..770d675
--- /dev/null
+++ b/packages/main/src/stores/config/loadConfig.ts
@@ -0,0 +1,146 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import {
22 applySnapshot,
23 IMSTArray,
24 IMSTMap,
25 IReferenceType,
26 IStateTreeNode,
27 IType,
28} from 'mobx-state-tree';
29import { nanoid } from 'nanoid';
30import slug from 'slug';
31
32import GlobalSettings from '../GlobalSettings';
33import type Profile from '../Profile';
34import type { ProfileSettingsSnapshotIn } from '../ProfileSettings';
35import type Service from '../Service';
36import type { ServiceSettingsSnapshotIn } from '../ServiceSettings';
37
38import type Config from './Config';
39import type ProfileConfig from './ProfileConfig';
40import type ServiceConfig from './ServiceConfig';
41
42function generateId(name?: string | undefined): string {
43 const nameSlug = typeof name === 'undefined' ? '' : slug(name);
44 return `${nameSlug}_${nanoid()}`;
45}
46
47function addMissingProfileIds(
48 profileConfigs: ProfileConfig[] | undefined,
49): [string, ProfileSettingsSnapshotIn][] {
50 return (profileConfigs ?? []).map((profileConfig) => {
51 const { id, ...settings } = profileConfig;
52 return [id === undefined ? generateId(settings.name) : id, settings];
53 });
54}
55
56function addMissingServiceIdsAndProfiles(
57 serviceConfigs: ServiceConfig[] | undefined,
58 profiles: [string, ProfileSettingsSnapshotIn][],
59): [string, ServiceSettingsSnapshotIn][] {
60 return (serviceConfigs ?? []).map((serviceConfig) => {
61 const { id, ...settings } = serviceConfig;
62 const { name } = settings;
63 let { profile: profileId } = settings;
64 if (profileId === undefined) {
65 profileId = generateId(name);
66 profiles.push([profileId, { name }]);
67 }
68 return [
69 id === undefined ? generateId(name) : id,
70 { ...settings, profile: profileId },
71 ];
72 });
73}
74
75type TypeWithSettings<C> = IType<
76 { id: string; settings: C },
77 unknown,
78 { settings: IStateTreeNode<IType<C, unknown, unknown>> }
79>;
80
81function applySettings<C, D extends TypeWithSettings<C>>(
82 current: IMSTArray<IReferenceType<D>>,
83 currentById: IMSTMap<D>,
84 toApply: [string, C][],
85): void {
86 const toApplyById = new Map(toApply);
87 const toDelete = new Set(currentById.keys());
88 toApplyById.forEach((settingsSnapshot, id) => {
89 const model = currentById.get(id);
90 if (model === undefined) {
91 currentById.set(id, {
92 id,
93 settings: settingsSnapshot,
94 });
95 } else {
96 toDelete.delete(id);
97 applySnapshot(model.settings, settingsSnapshot);
98 }
99 });
100 toDelete.forEach((id) => currentById.delete(id));
101 current.clear();
102 current.push(...toApply.map(([id]) => id));
103}
104
105export default function loadConfig(
106 target: {
107 readonly profiles: IMSTArray<IReferenceType<typeof Profile>>;
108 readonly profilesById: IMSTMap<typeof Profile>;
109 selectedService: Service | undefined;
110 readonly services: IMSTArray<IReferenceType<typeof Service>>;
111 readonly servicesById: IMSTMap<typeof Service>;
112 readonly settings: GlobalSettings;
113 },
114 config: Config,
115): void {
116 const {
117 profiles,
118 profilesById,
119 selectedService,
120 services,
121 servicesById,
122 settings,
123 } = target;
124 const { id: selectedServiceId } = selectedService ?? { id: undefined };
125 const {
126 profiles: profilesConfig,
127 services: servicesConfig,
128 ...settingsToApply
129 } = config;
130 const profilesToApply = addMissingProfileIds(profilesConfig);
131 const servicesToApply = addMissingServiceIdsAndProfiles(
132 servicesConfig,
133 profilesToApply,
134 );
135 applySettings(profiles, profilesById, profilesToApply);
136 applySettings(services, servicesById, servicesToApply);
137 applySnapshot(settings, settingsToApply);
138 let newSelectedService: Service | undefined;
139 if (selectedServiceId !== undefined) {
140 newSelectedService = servicesById.get(selectedServiceId);
141 }
142 if (newSelectedService === undefined && services.length > 0) {
143 [newSelectedService] = services;
144 }
145 target.selectedService = newSelectedService;
146}