From f5f27eddc93314e8e10ab96c7bdb5c626142a1d3 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 27 Dec 2021 19:41:46 +0100 Subject: refactor: Inversion of control with typed-inject --- packages/main/src/controllers/ConfigController.ts | 105 +++++++++------------ packages/main/src/controllers/MainController.ts | 38 ++++++++ .../main/src/controllers/NativeThemeController.ts | 37 +++++--- 3 files changed, 106 insertions(+), 74 deletions(-) create mode 100644 packages/main/src/controllers/MainController.ts (limited to 'packages/main/src/controllers') diff --git a/packages/main/src/controllers/ConfigController.ts b/packages/main/src/controllers/ConfigController.ts index 6690548..a28746c 100644 --- a/packages/main/src/controllers/ConfigController.ts +++ b/packages/main/src/controllers/ConfigController.ts @@ -25,56 +25,62 @@ import { IDisposer, onSnapshot, } from 'mobx-state-tree'; -import ms from 'ms'; -import { ConfigPersistenceService } from '../services/ConfigPersistenceService'; -import { Config, ConfigSnapshotOut } from '../stores/Config'; +import type { ConfigPersistenceService } from '../services/ConfigPersistenceService'; +import type { Config, ConfigSnapshotOut } from '../stores/Config'; -const DEFAULT_DEBOUNCE_TIME = ms('1s'); +export class ConfigController { + static inject = ['configPersistenceService', 'configDebounceTime'] as const; -class ConfigController { - readonly #config: Config; + private config: Config | null = null; - readonly #persistenceService: ConfigPersistenceService; + private onSnapshotDisposer: IDisposer | null = null; - readonly #onSnapshotDisposer: IDisposer; + private lastSnapshotOnDisk: ConfigSnapshotOut | null = null; - readonly #watcherDisposer: IDisposer; + private writingConfig: boolean = false; - #lastSnapshotOnDisk: ConfigSnapshotOut | null = null; + constructor( + private readonly persistenceService: ConfigPersistenceService, + private readonly debounceTime: number, + ) { + } - #writingConfig: boolean = false; + async initConfig(config: Config): Promise { + this.config = config; - #configMTime: Date | null = null; + const foundConfig: boolean = await this.readConfig(); + if (!foundConfig) { + console.log('Creating new config file'); + try { + await this.writeConfig(); + } catch (err) { + console.error('Failed to initialize config'); + } + } - constructor( - config: Config, - persistenceService: ConfigPersistenceService, - debounceTime: number, - ) { - this.#config = config; - this.#persistenceService = persistenceService; - this.#onSnapshotDisposer = onSnapshot(this.#config, debounce((snapshot) => { + this.onSnapshotDisposer = onSnapshot(this.config, debounce((snapshot) => { // We can compare snapshots by reference, since it is only recreated on store changes. - if (this.#lastSnapshotOnDisk !== snapshot) { - this.#writeConfig().catch((err) => { + if (this.lastSnapshotOnDisk !== snapshot) { + this.writeConfig().catch((err) => { console.log('Failed to write config on config change', err); }) } - }, debounceTime)); - this.#watcherDisposer = this.#persistenceService.watchConfig(async (mtime) => { - if (!this.#writingConfig && (this.#configMTime === null || mtime > this.#configMTime)) { - await this.#readConfig(); + }, this.debounceTime)); + + this.persistenceService.watchConfig(async () => { + if (!this.writingConfig) { + await this.readConfig(); } - }, debounceTime); + }, this.debounceTime); } - async #readConfig(): Promise { - const result = await this.#persistenceService.readConfig(); + private async readConfig(): Promise { + const result = await this.persistenceService.readConfig(); if (result.found) { try { - applySnapshot(this.#config, result.data); - this.#lastSnapshotOnDisk = getSnapshot(this.#config); + applySnapshot(this.config!, result.data); + this.lastSnapshotOnDisk = getSnapshot(this.config!); console.log('Loaded config'); } catch (err) { console.error('Failed to read config', result.data, err); @@ -83,42 +89,19 @@ class ConfigController { return result.found; } - async #writeConfig(): Promise { - const snapshot = getSnapshot(this.#config); - this.#writingConfig = true; + private async writeConfig(): Promise { + const snapshot = getSnapshot(this.config!); + this.writingConfig = true; try { - this.#configMTime = await this.#persistenceService.writeConfig(snapshot); - this.#lastSnapshotOnDisk = snapshot; + await this.persistenceService.writeConfig(snapshot); + this.lastSnapshotOnDisk = snapshot; console.log('Wrote config'); } finally { - this.#writingConfig = false; - } - } - - async initConfig(): Promise { - const foundConfig: boolean = await this.#readConfig(); - if (!foundConfig) { - console.log('Creating new config file'); - try { - await this.#writeConfig(); - } catch (err) { - console.error('Failed to initialize config'); - } + this.writingConfig = false; } } dispose(): void { - this.#onSnapshotDisposer(); - this.#watcherDisposer(); + this.onSnapshotDisposer?.(); } } - -export async function initConfig( - config: Config, - persistenceService: ConfigPersistenceService, - debounceTime: number = DEFAULT_DEBOUNCE_TIME, -): Promise { - const controller = new ConfigController(config, persistenceService, debounceTime); - await controller.initConfig(); - return () => controller.dispose(); -} diff --git a/packages/main/src/controllers/MainController.ts b/packages/main/src/controllers/MainController.ts new file mode 100644 index 0000000..6b97330 --- /dev/null +++ b/packages/main/src/controllers/MainController.ts @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021-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 { ConfigController } from './ConfigController'; +import type { NativeThemeController } from './NativeThemeController'; +import type { MainStore } from '../stores/MainStore'; + +export class MainController { + static inject = ['configController', 'nativeThemeController'] as const; + + constructor( + private readonly configController: ConfigController, + private readonly nativeThemeController: NativeThemeController, + ) { + } + + async connect(store: MainStore): Promise { + await this.configController.initConfig(store.config); + this.nativeThemeController.connect(store); + } +} diff --git a/packages/main/src/controllers/NativeThemeController.ts b/packages/main/src/controllers/NativeThemeController.ts index 07a3292..a50d41e 100644 --- a/packages/main/src/controllers/NativeThemeController.ts +++ b/packages/main/src/controllers/NativeThemeController.ts @@ -18,21 +18,32 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { nativeTheme } from 'electron'; import { autorun } from 'mobx'; -import type { IDisposer } from 'mobx-state-tree'; +import { IDisposer } from 'mobx-state-tree'; -import type { NativeThemeService } from '../services/NativeThemeService'; import type { MainStore } from '../stores/MainStore'; -export function initNativeTheme(store: MainStore, service: NativeThemeService): IDisposer { - const themeSourceReactionDisposer = autorun(() => { - service.setThemeSource(store.config.themeSource); - }); - const onShouldUseDarkColorsUpdatedDisposer = service.onShouldUseDarkColorsUpdated( - store.setShouldUseDarkColors, - ); - return () => { - onShouldUseDarkColorsUpdatedDisposer(); - themeSourceReactionDisposer(); - }; +export class NativeThemeController { + private autorunDisposer: IDisposer | null = null; + + private shouldUseDarkColorsListener: (() => void) | null = null; + + connect(store: MainStore): void { + this.autorunDisposer = autorun(() => { + nativeTheme.themeSource = store.config.themeSource; + }); + store.setShouldUseDarkColors(nativeTheme.shouldUseDarkColors); + this.shouldUseDarkColorsListener = () => { + store.setShouldUseDarkColors(nativeTheme.shouldUseDarkColors); + }; + nativeTheme.on('updated', this.shouldUseDarkColorsListener); + } + + dispose(): void { + if (this.shouldUseDarkColorsListener !== null) { + nativeTheme.off('updated', this.shouldUseDarkColorsListener); + } + this.autorunDisposer?.(); + } } -- cgit v1.2.3-54-g00ecf