diff options
Diffstat (limited to 'packages/main/src/stores/config')
-rw-r--r-- | packages/main/src/stores/config/Config.ts | 30 | ||||
-rw-r--r-- | packages/main/src/stores/config/ProfileConfig.ts | 25 | ||||
-rw-r--r-- | packages/main/src/stores/config/ServiceConfig.ts | 29 | ||||
-rw-r--r-- | packages/main/src/stores/config/loadConfig.ts | 146 |
4 files changed, 230 insertions, 0 deletions
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 | |||
21 | import type { GlobalSettingsSnapshotIn } from '../GlobalSettings'; | ||
22 | |||
23 | import type ProfileConfig from './ProfileConfig'; | ||
24 | import type ServiceConfig from './ServiceConfig'; | ||
25 | |||
26 | export default interface Config extends GlobalSettingsSnapshotIn { | ||
27 | profiles?: ProfileConfig[] | undefined; | ||
28 | |||
29 | services?: ServiceConfig[] | undefined; | ||
30 | } | ||
diff --git a/packages/main/src/stores/config/ProfileConfig.ts b/packages/main/src/stores/config/ProfileConfig.ts new file mode 100644 index 0000000..ce276d4 --- /dev/null +++ b/packages/main/src/stores/config/ProfileConfig.ts | |||
@@ -0,0 +1,25 @@ | |||
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 { ProfileSettingsSnapshotIn } from '@sophie/shared'; | ||
22 | |||
23 | export default interface ProfileConfig extends ProfileSettingsSnapshotIn { | ||
24 | id?: string | undefined; | ||
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 | |||
21 | import { ServiceSettingsSnapshotIn } from '@sophie/shared'; | ||
22 | import { ReferenceIdentifier } from 'mobx-state-tree'; | ||
23 | |||
24 | export 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 | |||
21 | import { | ||
22 | applySnapshot, | ||
23 | IMSTArray, | ||
24 | IMSTMap, | ||
25 | IReferenceType, | ||
26 | IStateTreeNode, | ||
27 | IType, | ||
28 | } from 'mobx-state-tree'; | ||
29 | import { nanoid } from 'nanoid'; | ||
30 | import slug from 'slug'; | ||
31 | |||
32 | import GlobalSettings from '../GlobalSettings'; | ||
33 | import type Profile from '../Profile'; | ||
34 | import type { ProfileSettingsSnapshotIn } from '../ProfileSettings'; | ||
35 | import type Service from '../Service'; | ||
36 | import type { ServiceSettingsSnapshotIn } from '../ServiceSettings'; | ||
37 | |||
38 | import type Config from './Config'; | ||
39 | import type ProfileConfig from './ProfileConfig'; | ||
40 | import type ServiceConfig from './ServiceConfig'; | ||
41 | |||
42 | function generateId(name?: string | undefined): string { | ||
43 | const nameSlug = typeof name === 'undefined' ? '' : slug(name); | ||
44 | return `${nameSlug}_${nanoid()}`; | ||
45 | } | ||
46 | |||
47 | function 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 | |||
56 | function 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 | |||
75 | type TypeWithSettings<C> = IType< | ||
76 | { id: string; settings: C }, | ||
77 | unknown, | ||
78 | { settings: IStateTreeNode<IType<C, unknown, unknown>> } | ||
79 | >; | ||
80 | |||
81 | function 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 | |||
105 | export 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 | } | ||