/* * 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 { debounce } from 'lodash'; import ms from 'ms'; import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree'; import type { ConfigPersistenceService } from '../services/ConfigPersistenceService'; import type { Config, ConfigSnapshotOut } from '../stores/Config'; import { DisposeHelper } from '../utils'; const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s'); export class ConfigController extends DisposeHelper { private config: Config | null = null; private lastSnapshotOnDisk: ConfigSnapshotOut | null = null; private writingConfig: boolean = false; constructor( private readonly persistenceService: ConfigPersistenceService, private readonly debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME, ) { super(); } async connect(config: Config): Promise { this.config = config; 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.registerDisposable(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) => { console.log('Failed to write config on config change', err); }) } }, this.debounceTime))); this.registerDisposable(this.persistenceService.watchConfig(async () => { if (!this.writingConfig) { await this.readConfig(); } }, this.debounceTime)); } private async readConfig(): Promise { const result = await this.persistenceService.readConfig(); if (result.found) { try { 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); } } return result.found; } private async writeConfig(): Promise { const snapshot = getSnapshot(this.config!); this.writingConfig = true; try { await this.persistenceService.writeConfig(snapshot); this.lastSnapshotOnDisk = snapshot; console.log('Wrote config'); } finally { this.writingConfig = false; } } }