From e84a0c57171097eb32ab92e82f54e522b5e1e65f Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 26 Dec 2021 02:43:25 +0100 Subject: feat: Config persistence --- packages/main/src/index.ts | 93 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 8 deletions(-) (limited to 'packages') diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts index 6a8a74e..a79a6e2 100644 --- a/packages/main/src/index.ts +++ b/packages/main/src/index.ts @@ -25,9 +25,15 @@ import { ipcMain, nativeTheme, } from 'electron'; -import { readFile, readFileSync } from 'fs'; +import { readFileSync, watchFile } from 'fs'; +import { readFile, stat, writeFile } from 'fs/promises'; import { autorun } from 'mobx'; -import { getSnapshot, onPatch } from 'mobx-state-tree'; +import { + applySnapshot, + getSnapshot, + onPatch, + onSnapshot, +} from 'mobx-state-tree'; import { join } from 'path'; import { ServiceToMainIpcMessage, @@ -46,6 +52,7 @@ import { installDevToolsExtensions, openDevToolsWhenReady, } from './devTools'; +import { ConfigSnapshotOut } from './stores/Config'; import { createRootStore } from './stores/RootStore'; const isDevelopment = import.meta.env.MODE === 'development'; @@ -114,6 +121,77 @@ nativeTheme.on('updated', () => { store.setShouldUseDarkColors(nativeTheme.shouldUseDarkColors); }); +const configPath = join(app.getPath('userData'), 'config.json'); +let loadingConfig = false; +let savingConfig = false; +let configMtime: Date | null = null; + +async function loadConfig(): Promise { + let configStr: string; + try { + configStr = await readFile(configPath, 'utf8'); + } catch (err) { + if ((err as NodeJS.ErrnoException).code === 'ENOENT') { + console.log('Creating config', configPath); + return saveConfig(getSnapshot(store.config)); + } + throw err; + } + let configSnapshot: unknown; + try { + configSnapshot = JSON.parse(configStr); + } catch (err) { + console.error('Invalid config file', configPath, err); + return; + } + loadingConfig = true; + try { + applySnapshot(store.config, configSnapshot); + } catch (err) { + console.error('Falied to apply snaphot', configPath, configSnapshot, err); + } finally { + loadingConfig = false; + } +} + +async function saveConfig(configSnapshot: ConfigSnapshotOut): Promise { + const configJson = JSON.stringify(configSnapshot, null, 2); + savingConfig = true; + try { + await writeFile(configPath, configJson, 'utf8'); + const stats = await stat(configPath); + configMtime = stats.mtime; + } finally { + savingConfig = false; + } + console.log('Wrote config', configPath); +} + +onSnapshot(store.config, (snapshot) => { + if (!loadingConfig) { + saveConfig(snapshot).catch((err) => { + console.error('Failed to save config', configPath, err); + }); + } +}); + +async function initializeConfig(): Promise { + await loadConfig(); + watchFile(configPath, ({ mtime }) => { + if (!savingConfig && (configMtime === null || mtime > configMtime)) { + loadConfig().catch((err) => { + console.error('Failed to reload config', configPath, err); + }); + configMtime = mtime; + console.log('Reloaded config', configPath); + } + }); +} + +initializeConfig().catch((err) => { + console.error('Failed to load config', configPath, err); +}); + const rendererBaseUrl = getResourceUrl('../renderer/'); function shouldCancelMainWindowRequest(url: string, method: string): boolean { if (method !== 'GET') { @@ -205,12 +283,11 @@ function createWindow(): Promise { store.config.setThemeSource(themeSource.parse(args[0])) break; case RendererToMainIpcMessage.ReloadAllServices: - readFile(serviceInjectPath, 'utf8', (err, data) => { - if (err === null) { - serviceInject.code = data; - } else { - console.error('Error while reloading', serviceInjectPath, err); - } + readFile(serviceInjectPath, 'utf8').then((data) => { + serviceInject.code = data; + }).catch((err) => { + console.error('Error while reloading', serviceInjectPath, err); + }).then(() => { browserView.webContents.reload(); }); break; -- cgit v1.2.3-54-g00ecf