From da610a51f49807d1409f36b98e06e89447a4202b Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 27 Jan 2022 01:02:02 +0100 Subject: refactor: Extract config handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- packages/main/src/stores/config/Config.ts | 30 +++++ packages/main/src/stores/config/ProfileConfig.ts | 25 ++++ packages/main/src/stores/config/ServiceConfig.ts | 29 +++++ packages/main/src/stores/config/loadConfig.ts | 146 +++++++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 packages/main/src/stores/config/Config.ts create mode 100644 packages/main/src/stores/config/ProfileConfig.ts create mode 100644 packages/main/src/stores/config/ServiceConfig.ts create mode 100644 packages/main/src/stores/config/loadConfig.ts (limited to 'packages/main/src/stores/config') 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import type { GlobalSettingsSnapshotIn } from '../GlobalSettings'; + +import type ProfileConfig from './ProfileConfig'; +import type ServiceConfig from './ServiceConfig'; + +export default interface Config extends GlobalSettingsSnapshotIn { + profiles?: ProfileConfig[] | undefined; + + services?: ServiceConfig[] | undefined; +} 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ProfileSettingsSnapshotIn } from '@sophie/shared'; + +export default interface ProfileConfig extends ProfileSettingsSnapshotIn { + id?: string | undefined; +} 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { ServiceSettingsSnapshotIn } from '@sophie/shared'; +import { ReferenceIdentifier } from 'mobx-state-tree'; + +export default interface ServiceConfig + extends Omit { + id?: string | undefined; + + profile?: ReferenceIdentifier | undefined; +} 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 @@ +/* + * Copyright (C) 2022 Kristóf Marussy + * + * This file is part of Sophie. + * + * Sophie is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { + applySnapshot, + IMSTArray, + IMSTMap, + IReferenceType, + IStateTreeNode, + IType, +} from 'mobx-state-tree'; +import { nanoid } from 'nanoid'; +import slug from 'slug'; + +import GlobalSettings from '../GlobalSettings'; +import type Profile from '../Profile'; +import type { ProfileSettingsSnapshotIn } from '../ProfileSettings'; +import type Service from '../Service'; +import type { ServiceSettingsSnapshotIn } from '../ServiceSettings'; + +import type Config from './Config'; +import type ProfileConfig from './ProfileConfig'; +import type ServiceConfig from './ServiceConfig'; + +function generateId(name?: string | undefined): string { + const nameSlug = typeof name === 'undefined' ? '' : slug(name); + return `${nameSlug}_${nanoid()}`; +} + +function addMissingProfileIds( + profileConfigs: ProfileConfig[] | undefined, +): [string, ProfileSettingsSnapshotIn][] { + return (profileConfigs ?? []).map((profileConfig) => { + const { id, ...settings } = profileConfig; + return [id === undefined ? generateId(settings.name) : id, settings]; + }); +} + +function addMissingServiceIdsAndProfiles( + serviceConfigs: ServiceConfig[] | undefined, + profiles: [string, ProfileSettingsSnapshotIn][], +): [string, ServiceSettingsSnapshotIn][] { + return (serviceConfigs ?? []).map((serviceConfig) => { + const { id, ...settings } = serviceConfig; + const { name } = settings; + let { profile: profileId } = settings; + if (profileId === undefined) { + profileId = generateId(name); + profiles.push([profileId, { name }]); + } + return [ + id === undefined ? generateId(name) : id, + { ...settings, profile: profileId }, + ]; + }); +} + +type TypeWithSettings = IType< + { id: string; settings: C }, + unknown, + { settings: IStateTreeNode> } +>; + +function applySettings>( + current: IMSTArray>, + currentById: IMSTMap, + toApply: [string, C][], +): void { + const toApplyById = new Map(toApply); + const toDelete = new Set(currentById.keys()); + toApplyById.forEach((settingsSnapshot, id) => { + const model = currentById.get(id); + if (model === undefined) { + currentById.set(id, { + id, + settings: settingsSnapshot, + }); + } else { + toDelete.delete(id); + applySnapshot(model.settings, settingsSnapshot); + } + }); + toDelete.forEach((id) => currentById.delete(id)); + current.clear(); + current.push(...toApply.map(([id]) => id)); +} + +export default function loadConfig( + target: { + readonly profiles: IMSTArray>; + readonly profilesById: IMSTMap; + selectedService: Service | undefined; + readonly services: IMSTArray>; + readonly servicesById: IMSTMap; + readonly settings: GlobalSettings; + }, + config: Config, +): void { + const { + profiles, + profilesById, + selectedService, + services, + servicesById, + settings, + } = target; + const { id: selectedServiceId } = selectedService ?? { id: undefined }; + const { + profiles: profilesConfig, + services: servicesConfig, + ...settingsToApply + } = config; + const profilesToApply = addMissingProfileIds(profilesConfig); + const servicesToApply = addMissingServiceIdsAndProfiles( + servicesConfig, + profilesToApply, + ); + applySettings(profiles, profilesById, profilesToApply); + applySettings(services, servicesById, servicesToApply); + applySnapshot(settings, settingsToApply); + let newSelectedService: Service | undefined; + if (selectedServiceId !== undefined) { + newSelectedService = servicesById.get(selectedServiceId); + } + if (newSelectedService === undefined && services.length > 0) { + [newSelectedService] = services; + } + target.selectedService = newSelectedService; +} -- cgit v1.2.3-70-g09d2