aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-26 02:43:25 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-26 02:43:25 +0100
commite84a0c57171097eb32ab92e82f54e522b5e1e65f (patch)
treee6dfb4004d1a32dfca3c116955840986f02e0837
parentfeat: Set nativeTheme theme source on dark mode (diff)
downloadsophie-e84a0c57171097eb32ab92e82f54e522b5e1e65f.tar.gz
sophie-e84a0c57171097eb32ab92e82f54e522b5e1e65f.tar.zst
sophie-e84a0c57171097eb32ab92e82f54e522b5e1e65f.zip
feat: Config persistence
-rw-r--r--packages/main/src/index.ts93
1 files changed, 85 insertions, 8 deletions
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 {
25 ipcMain, 25 ipcMain,
26 nativeTheme, 26 nativeTheme,
27} from 'electron'; 27} from 'electron';
28import { readFile, readFileSync } from 'fs'; 28import { readFileSync, watchFile } from 'fs';
29import { readFile, stat, writeFile } from 'fs/promises';
29import { autorun } from 'mobx'; 30import { autorun } from 'mobx';
30import { getSnapshot, onPatch } from 'mobx-state-tree'; 31import {
32 applySnapshot,
33 getSnapshot,
34 onPatch,
35 onSnapshot,
36} from 'mobx-state-tree';
31import { join } from 'path'; 37import { join } from 'path';
32import { 38import {
33 ServiceToMainIpcMessage, 39 ServiceToMainIpcMessage,
@@ -46,6 +52,7 @@ import {
46 installDevToolsExtensions, 52 installDevToolsExtensions,
47 openDevToolsWhenReady, 53 openDevToolsWhenReady,
48} from './devTools'; 54} from './devTools';
55import { ConfigSnapshotOut } from './stores/Config';
49import { createRootStore } from './stores/RootStore'; 56import { createRootStore } from './stores/RootStore';
50 57
51const isDevelopment = import.meta.env.MODE === 'development'; 58const isDevelopment = import.meta.env.MODE === 'development';
@@ -114,6 +121,77 @@ nativeTheme.on('updated', () => {
114 store.setShouldUseDarkColors(nativeTheme.shouldUseDarkColors); 121 store.setShouldUseDarkColors(nativeTheme.shouldUseDarkColors);
115}); 122});
116 123
124const configPath = join(app.getPath('userData'), 'config.json');
125let loadingConfig = false;
126let savingConfig = false;
127let configMtime: Date | null = null;
128
129async function loadConfig(): Promise<void> {
130 let configStr: string;
131 try {
132 configStr = await readFile(configPath, 'utf8');
133 } catch (err) {
134 if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
135 console.log('Creating config', configPath);
136 return saveConfig(getSnapshot(store.config));
137 }
138 throw err;
139 }
140 let configSnapshot: unknown;
141 try {
142 configSnapshot = JSON.parse(configStr);
143 } catch (err) {
144 console.error('Invalid config file', configPath, err);
145 return;
146 }
147 loadingConfig = true;
148 try {
149 applySnapshot(store.config, configSnapshot);
150 } catch (err) {
151 console.error('Falied to apply snaphot', configPath, configSnapshot, err);
152 } finally {
153 loadingConfig = false;
154 }
155}
156
157async function saveConfig(configSnapshot: ConfigSnapshotOut): Promise<void> {
158 const configJson = JSON.stringify(configSnapshot, null, 2);
159 savingConfig = true;
160 try {
161 await writeFile(configPath, configJson, 'utf8');
162 const stats = await stat(configPath);
163 configMtime = stats.mtime;
164 } finally {
165 savingConfig = false;
166 }
167 console.log('Wrote config', configPath);
168}
169
170onSnapshot(store.config, (snapshot) => {
171 if (!loadingConfig) {
172 saveConfig(snapshot).catch((err) => {
173 console.error('Failed to save config', configPath, err);
174 });
175 }
176});
177
178async function initializeConfig(): Promise<void> {
179 await loadConfig();
180 watchFile(configPath, ({ mtime }) => {
181 if (!savingConfig && (configMtime === null || mtime > configMtime)) {
182 loadConfig().catch((err) => {
183 console.error('Failed to reload config', configPath, err);
184 });
185 configMtime = mtime;
186 console.log('Reloaded config', configPath);
187 }
188 });
189}
190
191initializeConfig().catch((err) => {
192 console.error('Failed to load config', configPath, err);
193});
194
117const rendererBaseUrl = getResourceUrl('../renderer/'); 195const rendererBaseUrl = getResourceUrl('../renderer/');
118function shouldCancelMainWindowRequest(url: string, method: string): boolean { 196function shouldCancelMainWindowRequest(url: string, method: string): boolean {
119 if (method !== 'GET') { 197 if (method !== 'GET') {
@@ -205,12 +283,11 @@ function createWindow(): Promise<unknown> {
205 store.config.setThemeSource(themeSource.parse(args[0])) 283 store.config.setThemeSource(themeSource.parse(args[0]))
206 break; 284 break;
207 case RendererToMainIpcMessage.ReloadAllServices: 285 case RendererToMainIpcMessage.ReloadAllServices:
208 readFile(serviceInjectPath, 'utf8', (err, data) => { 286 readFile(serviceInjectPath, 'utf8').then((data) => {
209 if (err === null) { 287 serviceInject.code = data;
210 serviceInject.code = data; 288 }).catch((err) => {
211 } else { 289 console.error('Error while reloading', serviceInjectPath, err);
212 console.error('Error while reloading', serviceInjectPath, err); 290 }).then(() => {
213 }
214 browserView.webContents.reload(); 291 browserView.webContents.reload();
215 }); 292 });
216 break; 293 break;