/* * 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 { applySnapshot, flow, getSnapshot, IDisposer, Instance, onSnapshot, } from 'mobx-state-tree'; import { config as originalConfig, ConfigSnapshotIn, ConfigSnapshotOut, ThemeSource, } from '@sophie/shared'; import { CONFIG_DEBOUNCE_TIME, ReadConfigResult } from '../services/ConfigPersistence'; import { getEnv } from '../services/MainEnv'; export const config = originalConfig.actions((self) => ({ setThemeSource(mode: ThemeSource) { self.themeSource = mode; }, })).actions((self) => { let lastSnapshotOnDisk: ConfigSnapshotOut | null = null; let writingConfig = false; let configMtime: Date | null = null; let onSnapshotDisposer: IDisposer | null = null; let watcherDisposer: IDisposer | null = null; function dispose() { onSnapshotDisposer?.(); watcherDisposer?.(); } const actions: { beforeDetach(): void, readConfig(): Promise; writeConfig(): Promise; initConfig(): Promise; } = { beforeDetach() { dispose(); }, readConfig: flow(function*() { const result: ReadConfigResult = yield getEnv(self).configPersistence.readConfig(); if (result.found) { try { applySnapshot(self, result.data); lastSnapshotOnDisk = getSnapshot(self); console.log('Loaded config'); } catch (err) { console.error('Failed to read config', result.data, err); } } return result.found; }), writeConfig: flow(function*() { const snapshot = getSnapshot(self); writingConfig = true; try { configMtime = yield getEnv(self).configPersistence.writeConfig(snapshot); lastSnapshotOnDisk = snapshot; console.log('Wrote config'); } finally { writingConfig = false; } }), initConfig: flow(function*() { dispose(); const foundConfig: boolean = yield actions.readConfig(); if (!foundConfig) { console.log('Creating new config file'); try { yield actions.writeConfig(); } catch (err) { console.error('Failed to initialize config'); } } onSnapshotDisposer = onSnapshot(self, debounce((snapshot) => { // We can compare snapshots by reference, since it is only recreated on store changes. if (lastSnapshotOnDisk !== snapshot) { actions.writeConfig().catch((err) => { console.log('Failed to write config on config change', err); }) } }, CONFIG_DEBOUNCE_TIME)); watcherDisposer = getEnv(self).configPersistence.watchConfig(async (mtime) => { if (!writingConfig && (configMtime === null || mtime > configMtime)) { await actions.readConfig(); } }); }), }; return actions; }); export interface Config extends Instance {} export type { ConfigSnapshotIn, ConfigSnapshotOut };