aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main
diff options
context:
space:
mode:
Diffstat (limited to 'packages/main')
-rw-r--r--packages/main/.eslintrc.cjs6
-rw-r--r--packages/main/.eslintrc.json6
-rw-r--r--packages/main/esbuild.config.js8
-rw-r--r--packages/main/package.json2
-rw-r--r--packages/main/src/controllers/__tests__/initConfig.spec.ts (renamed from packages/main/src/controllers/__tests__/config.spec.ts)18
-rw-r--r--packages/main/src/controllers/__tests__/initNativeTheme.spec.ts (renamed from packages/main/src/controllers/__tests__/nativeTheme.spec.ts)4
-rw-r--r--packages/main/src/controllers/initConfig.ts (renamed from packages/main/src/controllers/config.ts)12
-rw-r--r--packages/main/src/controllers/initNativeTheme.ts (renamed from packages/main/src/controllers/nativeTheme.ts)8
-rw-r--r--packages/main/src/devTools.ts7
-rw-r--r--packages/main/src/index.ts109
-rw-r--r--packages/main/src/init.ts (renamed from packages/main/src/compositionRoot.ts)10
-rw-r--r--packages/main/src/services/ConfigPersistenceService.ts6
-rw-r--r--packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts20
-rw-r--r--packages/main/src/stores/Config.ts2
-rw-r--r--packages/main/src/stores/MainStore.ts2
-rw-r--r--packages/main/src/stores/SharedStore.ts2
-rw-r--r--packages/main/src/utils/Disposer.ts (renamed from packages/main/src/utils/disposer.ts)4
-rw-r--r--packages/main/src/utils/log.ts (renamed from packages/main/src/utils/logging.ts)10
-rw-r--r--packages/main/tsconfig.json10
19 files changed, 139 insertions, 107 deletions
diff --git a/packages/main/.eslintrc.cjs b/packages/main/.eslintrc.cjs
new file mode 100644
index 0000000..548ea34
--- /dev/null
+++ b/packages/main/.eslintrc.cjs
@@ -0,0 +1,6 @@
1module.exports = {
2 env: {
3 node: true,
4 browser: false,
5 },
6};
diff --git a/packages/main/.eslintrc.json b/packages/main/.eslintrc.json
deleted file mode 100644
index 6b736e2..0000000
--- a/packages/main/.eslintrc.json
+++ /dev/null
@@ -1,6 +0,0 @@
1{
2 "globals": {
3 "NodeJS": false,
4 "require": false
5 }
6}
diff --git a/packages/main/esbuild.config.js b/packages/main/esbuild.config.js
index c24d6e1..49fba6b 100644
--- a/packages/main/esbuild.config.js
+++ b/packages/main/esbuild.config.js
@@ -1,8 +1,8 @@
1/* eslint-disable no-process-env */
2import getRepoInfo from 'git-repo-info'; 1import getRepoInfo from 'git-repo-info';
2
3import { node } from '../../config/buildConstants.js'; 3import { node } from '../../config/buildConstants.js';
4import { getConfig } from '../../config/esbuildConfig.js'; 4import fileURLToDirname from '../../config/fileURLToDirname.js';
5import { fileURLToDirname } from '../../config/utils.js'; 5import getEsbuildConfig from '../../config/getEsbuildConfig.js';
6 6
7const externalPackages = ['electron']; 7const externalPackages = ['electron'];
8 8
@@ -12,7 +12,7 @@ if (process.env.MODE !== 'development') {
12 12
13const gitInfo = getRepoInfo(); 13const gitInfo = getRepoInfo();
14 14
15export default getConfig({ 15export default getEsbuildConfig({
16 absWorkingDir: fileURLToDirname(import.meta.url), 16 absWorkingDir: fileURLToDirname(import.meta.url),
17 entryPoints: [ 17 entryPoints: [
18 'src/index.ts', 18 'src/index.ts',
diff --git a/packages/main/package.json b/packages/main/package.json
index e1b3f49..d9abf51 100644
--- a/packages/main/package.json
+++ b/packages/main/package.json
@@ -5,7 +5,7 @@
5 "type": "module", 5 "type": "module",
6 "types": "dist-types/index.d.ts", 6 "types": "dist-types/index.d.ts",
7 "scripts": { 7 "scripts": {
8 "typecheck": "tsc" 8 "typecheck:workspace": "yarn g:typecheck"
9 }, 9 },
10 "dependencies": { 10 "dependencies": {
11 "@sophie/service-shared": "workspace:*", 11 "@sophie/service-shared": "workspace:*",
diff --git a/packages/main/src/controllers/__tests__/config.spec.ts b/packages/main/src/controllers/__tests__/initConfig.spec.ts
index eb67df0..e386a07 100644
--- a/packages/main/src/controllers/__tests__/config.spec.ts
+++ b/packages/main/src/controllers/__tests__/initConfig.spec.ts
@@ -22,20 +22,20 @@ import { jest } from '@jest/globals';
22import { mocked } from 'jest-mock'; 22import { mocked } from 'jest-mock';
23import ms from 'ms'; 23import ms from 'ms';
24 24
25import { initConfig } from '../config'; 25import type ConfigPersistenceService from '../../services/ConfigPersistenceService';
26import type { ConfigPersistenceService } from '../../services/ConfigPersistenceService';
27import { Config, config as configModel } from '../../stores/Config'; 26import { Config, config as configModel } from '../../stores/Config';
28import { Disposer } from '../../utils/disposer'; 27import type Disposer from '../../utils/Disposer';
29import { silenceLogger } from '../../utils/logging'; 28import { silenceLogger } from '../../utils/log';
29import initConfig from '../initConfig';
30 30
31let config: Config; 31let config: Config;
32let persistenceService: ConfigPersistenceService = { 32const persistenceService: ConfigPersistenceService = {
33 readConfig: jest.fn(), 33 readConfig: jest.fn(),
34 writeConfig: jest.fn(), 34 writeConfig: jest.fn(),
35 watchConfig: jest.fn(), 35 watchConfig: jest.fn(),
36}; 36};
37let lessThanThrottleMs = ms('0.1s'); 37const lessThanThrottleMs = ms('0.1s');
38let throttleMs = ms('1s'); 38const throttleMs = ms('1s');
39 39
40beforeAll(() => { 40beforeAll(() => {
41 jest.useFakeTimers(); 41 jest.useFakeTimers();
@@ -108,7 +108,7 @@ describe('when initializing', () => {
108 108
109describe('when it has loaded the config', () => { 109describe('when it has loaded the config', () => {
110 let sutDisposer: Disposer; 110 let sutDisposer: Disposer;
111 let watcherDisposer: Disposer = jest.fn(); 111 const watcherDisposer: Disposer = jest.fn();
112 let configChangedCallback: () => Promise<void>; 112 let configChangedCallback: () => Promise<void>;
113 113
114 beforeEach(async () => { 114 beforeEach(async () => {
@@ -118,7 +118,7 @@ describe('when it has loaded the config', () => {
118 }); 118 });
119 mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer); 119 mocked(persistenceService.watchConfig).mockReturnValueOnce(watcherDisposer);
120 sutDisposer = await initConfig(config, persistenceService, throttleMs); 120 sutDisposer = await initConfig(config, persistenceService, throttleMs);
121 configChangedCallback = mocked(persistenceService.watchConfig).mock.calls[0][0]; 121 [[configChangedCallback]] = mocked(persistenceService.watchConfig).mock.calls;
122 jest.resetAllMocks(); 122 jest.resetAllMocks();
123 }); 123 });
124 124
diff --git a/packages/main/src/controllers/__tests__/nativeTheme.spec.ts b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts
index 85d6dd2..bd33f48 100644
--- a/packages/main/src/controllers/__tests__/nativeTheme.spec.ts
+++ b/packages/main/src/controllers/__tests__/initNativeTheme.spec.ts
@@ -22,7 +22,7 @@ import { jest } from '@jest/globals';
22import { mocked } from 'jest-mock'; 22import { mocked } from 'jest-mock';
23 23
24import { createMainStore, MainStore } from '../../stores/MainStore'; 24import { createMainStore, MainStore } from '../../stores/MainStore';
25import { Disposer } from '../../utils/disposer'; 25import type Disposer from '../../utils/Disposer';
26 26
27let shouldUseDarkColors = false; 27let shouldUseDarkColors = false;
28 28
@@ -38,7 +38,7 @@ jest.unstable_mockModule('electron', () => ({
38})); 38}));
39 39
40const { nativeTheme } = await import('electron'); 40const { nativeTheme } = await import('electron');
41const { initNativeTheme } = await import('../nativeTheme'); 41const { default: initNativeTheme } = await import('../initNativeTheme');
42 42
43let store: MainStore; 43let store: MainStore;
44let disposeSut: Disposer; 44let disposeSut: Disposer;
diff --git a/packages/main/src/controllers/config.ts b/packages/main/src/controllers/initConfig.ts
index deaeac2..1d40762 100644
--- a/packages/main/src/controllers/config.ts
+++ b/packages/main/src/controllers/initConfig.ts
@@ -19,19 +19,19 @@
19 */ 19 */
20 20
21import { debounce } from 'lodash-es'; 21import { debounce } from 'lodash-es';
22import ms from 'ms';
23import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree'; 22import { applySnapshot, getSnapshot, onSnapshot } from 'mobx-state-tree';
23import ms from 'ms';
24 24
25import type { ConfigPersistenceService } from '../services/ConfigPersistenceService.js'; 25import type ConfigPersistenceService from '../services/ConfigPersistenceService';
26import type { Config, ConfigSnapshotOut } from '../stores/Config.js'; 26import type { Config, ConfigSnapshotOut } from '../stores/Config';
27import { Disposer } from '../utils/disposer'; 27import type Disposer from '../utils/Disposer';
28import { getLogger } from '../utils/logging'; 28import { getLogger } from '../utils/log';
29 29
30const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s'); 30const DEFAULT_CONFIG_DEBOUNCE_TIME = ms('1s');
31 31
32const log = getLogger('config'); 32const log = getLogger('config');
33 33
34export async function initConfig( 34export default async function initConfig(
35 config: Config, 35 config: Config,
36 persistenceService: ConfigPersistenceService, 36 persistenceService: ConfigPersistenceService,
37 debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME, 37 debounceTime: number = DEFAULT_CONFIG_DEBOUNCE_TIME,
diff --git a/packages/main/src/controllers/nativeTheme.ts b/packages/main/src/controllers/initNativeTheme.ts
index ccd12d8..d2074ab 100644
--- a/packages/main/src/controllers/nativeTheme.ts
+++ b/packages/main/src/controllers/initNativeTheme.ts
@@ -21,13 +21,13 @@
21import { nativeTheme } from 'electron'; 21import { nativeTheme } from 'electron';
22import { autorun } from 'mobx'; 22import { autorun } from 'mobx';
23 23
24import type { MainStore } from '../stores/MainStore.js'; 24import type { MainStore } from '../stores/MainStore';
25import { Disposer } from '../utils/disposer'; 25import type Disposer from '../utils/Disposer';
26import { getLogger } from '../utils/logging'; 26import { getLogger } from '../utils/log';
27 27
28const log = getLogger('nativeTheme'); 28const log = getLogger('nativeTheme');
29 29
30export function initNativeTheme(store: MainStore): Disposer { 30export default function initNativeTheme(store: MainStore): Disposer {
31 log.trace('Initializing nativeTheme controller'); 31 log.trace('Initializing nativeTheme controller');
32 32
33 const disposeThemeSourceReaction = autorun(() => { 33 const disposeThemeSourceReaction = autorun(() => {
diff --git a/packages/main/src/devTools.ts b/packages/main/src/devTools.ts
index 398904c..0486c36 100644
--- a/packages/main/src/devTools.ts
+++ b/packages/main/src/devTools.ts
@@ -46,7 +46,12 @@ export async function installDevToolsExtensions(): Promise<void> {
46 default: installExtension, 46 default: installExtension,
47 REACT_DEVELOPER_TOOLS, 47 REACT_DEVELOPER_TOOLS,
48 REDUX_DEVTOOLS, 48 REDUX_DEVTOOLS,
49 } = require('electron-devtools-installer'); 49 /* eslint-disable-next-line
50 import/no-extraneous-dependencies,
51 global-require,
52 @typescript-eslint/no-var-requires
53 */
54 } = require('electron-devtools-installer') as typeof import('electron-devtools-installer');
50 await installExtension( 55 await installExtension(
51 [ 56 [
52 REACT_DEVELOPER_TOOLS, 57 REACT_DEVELOPER_TOOLS,
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index d0191b7..bc10b4c 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -19,18 +19,10 @@
19 * SPDX-License-Identifier: AGPL-3.0-only 19 * SPDX-License-Identifier: AGPL-3.0-only
20 */ 20 */
21 21
22import {
23 app,
24 BrowserView,
25 BrowserWindow,
26 ipcMain,
27} from 'electron';
28import { arch } from 'os'; 22import { arch } from 'os';
29import osName from 'os-name';
30import { ensureDirSync, readFile, readFileSync } from 'fs-extra';
31import { autorun } from 'mobx';
32import { getSnapshot, onPatch } from 'mobx-state-tree';
33import { join } from 'path'; 23import { join } from 'path';
24import { URL } from 'url';
25
34import { 26import {
35 ServiceToMainIpcMessage, 27 ServiceToMainIpcMessage,
36 unreadCount, 28 unreadCount,
@@ -41,18 +33,30 @@ import {
41 MainToRendererIpcMessage, 33 MainToRendererIpcMessage,
42 RendererToMainIpcMessage, 34 RendererToMainIpcMessage,
43} from '@sophie/shared'; 35} from '@sophie/shared';
44import { URL } from 'url'; 36import {
37 app,
38 BrowserView,
39 BrowserWindow,
40 ipcMain,
41} from 'electron';
42import { ensureDirSync, readFile, readFileSync } from 'fs-extra';
43import { autorun } from 'mobx';
44import { getSnapshot, onPatch } from 'mobx-state-tree';
45import osName from 'os-name';
45 46
46import { init } from './compositionRoot';
47import { 47import {
48 DEVMODE_ALLOWED_URL_PREFIXES, 48 DEVMODE_ALLOWED_URL_PREFIXES,
49 installDevToolsExtensions, 49 installDevToolsExtensions,
50 openDevToolsWhenReady, 50 openDevToolsWhenReady,
51} from './devTools'; 51} from './devTools';
52import init from './init';
52import { createMainStore } from './stores/MainStore'; 53import { createMainStore } from './stores/MainStore';
54import { getLogger } from './utils/log';
53 55
54const isDevelopment = import.meta.env.MODE === 'development'; 56const isDevelopment = import.meta.env.MODE === 'development';
55 57
58const log = getLogger('index');
59
56// Always enable sandboxing. 60// Always enable sandboxing.
57app.enableSandbox(); 61app.enableSandbox();
58 62
@@ -93,7 +97,7 @@ app.setAboutPanelOptions({
93 `Node.js: ${process.versions.node}`, 97 `Node.js: ${process.versions.node}`,
94 `Platform: ${osName()}`, 98 `Platform: ${osName()}`,
95 `Arch: ${arch()}`, 99 `Arch: ${arch()}`,
96 `Build date: ${new Date(Number(import.meta.env.BUILD_DATE))}`, 100 `Build date: ${new Date(Number(import.meta.env.BUILD_DATE)).toLocaleString()}`,
97 `Git SHA: ${import.meta.env.GIT_SHA}`, 101 `Git SHA: ${import.meta.env.GIT_SHA}`,
98 `Git branch: ${import.meta.env.GIT_BRANCH}`, 102 `Git branch: ${import.meta.env.GIT_BRANCH}`,
99 ].join('\n'), 103 ].join('\n'),
@@ -109,9 +113,9 @@ function getResourceUrl(relativePath: string): string {
109 return new URL(relativePath, baseUrl).toString(); 113 return new URL(relativePath, baseUrl).toString();
110} 114}
111 115
112let serviceInjectRelativePath = '../../service-inject/dist/index.js'; 116const serviceInjectRelativePath = '../../service-inject/dist/index.js';
113let serviceInjectPath = getResourcePath(serviceInjectRelativePath); 117const serviceInjectPath = getResourcePath(serviceInjectRelativePath);
114let serviceInject: WebSource = { 118const serviceInject: WebSource = {
115 code: readFileSync(serviceInjectPath, 'utf8'), 119 code: readFileSync(serviceInjectPath, 'utf8'),
116 url: getResourceUrl(serviceInjectRelativePath), 120 url: getResourceUrl(serviceInjectRelativePath),
117}; 121};
@@ -122,7 +126,7 @@ const store = createMainStore();
122init(store).then((disposeCompositionRoot) => { 126init(store).then((disposeCompositionRoot) => {
123 app.on('will-quit', disposeCompositionRoot); 127 app.on('will-quit', disposeCompositionRoot);
124}).catch((err) => { 128}).catch((err) => {
125 console.log('Failed to initialize application', err); 129 log.log('Failed to initialize application', err);
126}); 130});
127 131
128const rendererBaseUrl = getResourceUrl('../renderer/'); 132const rendererBaseUrl = getResourceUrl('../renderer/');
@@ -211,7 +215,7 @@ async function createWindow(): Promise<unknown> {
211 215
212 ipcMain.handle(RendererToMainIpcMessage.GetSharedStoreSnapshot, (event) => { 216 ipcMain.handle(RendererToMainIpcMessage.GetSharedStoreSnapshot, (event) => {
213 if (event.sender.id !== webContents.id) { 217 if (event.sender.id !== webContents.id) {
214 console.warn( 218 log.warn(
215 'Unexpected', 219 'Unexpected',
216 RendererToMainIpcMessage.GetSharedStoreSnapshot, 220 RendererToMainIpcMessage.GetSharedStoreSnapshot,
217 'from webContents', 221 'from webContents',
@@ -224,7 +228,7 @@ async function createWindow(): Promise<unknown> {
224 228
225 ipcMain.on(RendererToMainIpcMessage.DispatchAction, (event, rawAction) => { 229 ipcMain.on(RendererToMainIpcMessage.DispatchAction, (event, rawAction) => {
226 if (event.sender.id !== webContents.id) { 230 if (event.sender.id !== webContents.id) {
227 console.warn( 231 log.warn(
228 'Unexpected', 232 'Unexpected',
229 RendererToMainIpcMessage.DispatchAction, 233 RendererToMainIpcMessage.DispatchAction,
230 'from webContents', 234 'from webContents',
@@ -242,17 +246,26 @@ async function createWindow(): Promise<unknown> {
242 store.config.setThemeSource(actionToDispatch.themeSource); 246 store.config.setThemeSource(actionToDispatch.themeSource);
243 break; 247 break;
244 case 'reload-all-services': 248 case 'reload-all-services':
245 readFile(serviceInjectPath, 'utf8').then((data) => { 249 readFile(serviceInjectPath, 'utf8')
246 serviceInject.code = data; 250 .then((data) => {
247 }).catch((err) => { 251 serviceInject.code = data;
248 console.error('Error while reloading', serviceInjectPath, err); 252 })
249 }).then(() => { 253 .catch((err) => {
250 browserView.webContents.reload(); 254 log.error('Error while reloading', serviceInjectPath, err);
251 }); 255 })
256 .then(() => {
257 browserView.webContents.reload();
258 })
259 .catch((err) => {
260 log.error('Failed to reload browserView', err);
261 });
262 break;
263 default:
264 log.error('Unexpected action from UI renderer:', actionToDispatch);
252 break; 265 break;
253 } 266 }
254 } catch (err) { 267 } catch (err) {
255 console.error('Error while dispatching renderer action', rawAction, err); 268 log.error('Error while dispatching renderer action', rawAction, err);
256 } 269 }
257 }); 270 });
258 271
@@ -260,11 +273,10 @@ async function createWindow(): Promise<unknown> {
260 webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); 273 webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch);
261 }); 274 });
262 275
263 ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { 276 ipcMain.handle(
264 return event.sender.id === browserView.webContents.id 277 ServiceToMainIpcMessage.ApiExposedInMainWorld,
265 ? serviceInject 278 (event) => (event.sender.id === browserView.webContents.id ? serviceInject : null),
266 : null; 279 );
267 });
268 280
269 browserView.webContents.on('ipc-message', (_event, channel, ...args) => { 281 browserView.webContents.on('ipc-message', (_event, channel, ...args) => {
270 try { 282 try {
@@ -274,14 +286,14 @@ async function createWindow(): Promise<unknown> {
274 // otherwise electron emits a no handler registered warning. 286 // otherwise electron emits a no handler registered warning.
275 break; 287 break;
276 case ServiceToMainIpcMessage.SetUnreadCount: 288 case ServiceToMainIpcMessage.SetUnreadCount:
277 console.log('Unread count:', unreadCount.parse(args[0])); 289 log.log('Unread count:', unreadCount.parse(args[0]));
278 break; 290 break;
279 default: 291 default:
280 console.error('Unknown IPC message:', channel, args); 292 log.error('Unknown IPC message:', channel, args);
281 break; 293 break;
282 } 294 }
283 } catch (err) { 295 } catch (err) {
284 console.error('Error while processing IPC message:', channel, args, err); 296 log.error('Error while processing IPC message:', channel, args, err);
285 } 297 }
286 }); 298 });
287 299
@@ -291,17 +303,22 @@ async function createWindow(): Promise<unknown> {
291 }, 303 },
292 ); 304 );
293 305
294 browserView.webContents.session.webRequest.onBeforeSendHeaders(({ url, requestHeaders }, callback) => { 306 browserView.webContents.session.webRequest.onBeforeSendHeaders(
295 if (url.match(/^[^:]+:\/\/accounts\.google\.[^.\/]+\//)) { 307 ({ url, requestHeaders }, callback) => {
296 requestHeaders['User-Agent'] = chromelessUserAgent; 308 const requestUserAgent = url.match(/^[^:]+:\/\/accounts\.google\.[^./]+\//)
297 } else { 309 ? chromelessUserAgent
298 requestHeaders['User-Agent'] = userAgent; 310 : userAgent;
299 } 311 callback({
300 callback({ requestHeaders }); 312 requestHeaders: {
301 }); 313 ...requestHeaders,
314 'User-Agent': requestUserAgent,
315 },
316 });
317 },
318 );
302 319
303 browserView.webContents.loadURL('https://gitlab.com/say-hi-to-sophie/sophie').catch((err) => { 320 browserView.webContents.loadURL('https://gitlab.com/say-hi-to-sophie/sophie').catch((err) => {
304 console.error('Failed to load browser', err); 321 log.error('Failed to load browser', err);
305 }); 322 });
306 323
307 return mainWindow.loadURL(pageUrl); 324 return mainWindow.loadURL(pageUrl);
@@ -330,12 +347,12 @@ app.whenReady().then(async () => {
330 try { 347 try {
331 await installDevToolsExtensions(); 348 await installDevToolsExtensions();
332 } catch (err) { 349 } catch (err) {
333 console.error('Failed to install devtools extensions', err); 350 log.error('Failed to install devtools extensions', err);
334 } 351 }
335 } 352 }
336 353
337 return createWindow(); 354 return createWindow();
338}).catch((err) => { 355}).catch((err) => {
339 console.error('Failed to create window', err); 356 log.error('Failed to create window', err);
340 process.exit(1); 357 process.exit(1);
341}); 358});
diff --git a/packages/main/src/compositionRoot.ts b/packages/main/src/init.ts
index 76835a1..4487cc4 100644
--- a/packages/main/src/compositionRoot.ts
+++ b/packages/main/src/init.ts
@@ -20,13 +20,13 @@
20 20
21import { app } from 'electron'; 21import { app } from 'electron';
22 22
23import { initConfig } from './controllers/config'; 23import initConfig from './controllers/initConfig';
24import { initNativeTheme } from './controllers/nativeTheme'; 24import initNativeTheme from './controllers/initNativeTheme';
25import { ConfigPersistenceServiceImpl } from './services/impl/ConfigPersistenceServiceImpl'; 25import ConfigPersistenceServiceImpl from './services/impl/ConfigPersistenceServiceImpl';
26import { MainStore } from './stores/MainStore'; 26import { MainStore } from './stores/MainStore';
27import { Disposer } from './utils/disposer'; 27import type Disposer from './utils/Disposer';
28 28
29export async function init(store: MainStore): Promise<Disposer> { 29export default async function init(store: MainStore): Promise<Disposer> {
30 const configPersistenceService = new ConfigPersistenceServiceImpl(app.getPath('userData')); 30 const configPersistenceService = new ConfigPersistenceServiceImpl(app.getPath('userData'));
31 const disposeConfigController = await initConfig(store.config, configPersistenceService); 31 const disposeConfigController = await initConfig(store.config, configPersistenceService);
32 const disposeNativeThemeController = initNativeTheme(store); 32 const disposeNativeThemeController = initNativeTheme(store);
diff --git a/packages/main/src/services/ConfigPersistenceService.ts b/packages/main/src/services/ConfigPersistenceService.ts
index aed0ba3..7d508c5 100644
--- a/packages/main/src/services/ConfigPersistenceService.ts
+++ b/packages/main/src/services/ConfigPersistenceService.ts
@@ -18,12 +18,12 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import type { ConfigSnapshotOut } from '../stores/Config'; 21import type { ConfigSnapshotOut } from '../stores/Config';
22import { Disposer } from '../utils/disposer'; 22import type Disposer from '../utils/Disposer';
23 23
24export type ReadConfigResult = { found: true; data: unknown; } | { found: false; }; 24export type ReadConfigResult = { found: true; data: unknown; } | { found: false; };
25 25
26export interface ConfigPersistenceService { 26export default interface ConfigPersistenceService {
27 readConfig(): Promise<ReadConfigResult>; 27 readConfig(): Promise<ReadConfigResult>;
28 28
29 writeConfig(configSnapshot: ConfigSnapshotOut): Promise<void>; 29 writeConfig(configSnapshot: ConfigSnapshotOut): Promise<void>;
diff --git a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts
index 2d19632..df8c807 100644
--- a/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts
+++ b/packages/main/src/services/impl/ConfigPersistenceServiceImpl.ts
@@ -19,18 +19,20 @@
19 */ 19 */
20import { watch } from 'fs'; 20import { watch } from 'fs';
21import { readFile, stat, writeFile } from 'fs/promises'; 21import { readFile, stat, writeFile } from 'fs/promises';
22import JSON5 from 'json5';
23import { throttle } from 'lodash-es';
24import { join } from 'path'; 22import { join } from 'path';
25 23
26import type { ConfigPersistenceService, ReadConfigResult } from '../ConfigPersistenceService.js'; 24import JSON5 from 'json5';
27import type { ConfigSnapshotOut } from '../../stores/Config.js'; 25import throttle from 'lodash-es/throttle';
28import { Disposer } from '../../utils/disposer'; 26
29import { getLogger } from '../../utils/logging'; 27import type { ConfigSnapshotOut } from '../../stores/Config';
28import type Disposer from '../../utils/Disposer';
29import { getLogger } from '../../utils/log';
30import type ConfigPersistenceService from '../ConfigPersistenceService';
31import type { ReadConfigResult } from '../ConfigPersistenceService';
30 32
31const log = getLogger('configPersistence'); 33const log = getLogger('configPersistence');
32 34
33export class ConfigPersistenceServiceImpl implements ConfigPersistenceService { 35export default class ConfigPersistenceServiceImpl implements ConfigPersistenceService {
34 private readonly configFilePath: string; 36 private readonly configFilePath: string;
35 37
36 private writingConfig = false; 38 private writingConfig = false;
@@ -103,7 +105,7 @@ export class ConfigPersistenceServiceImpl implements ConfigPersistenceService {
103 'whish is newer than last written', 105 'whish is newer than last written',
104 this.timeLastWritten, 106 this.timeLastWritten,
105 ); 107 );
106 return callback(); 108 await callback();
107 } 109 }
108 }, throttleMs); 110 }, throttleMs);
109 111
@@ -115,7 +117,7 @@ export class ConfigPersistenceServiceImpl implements ConfigPersistenceService {
115 if (eventType === 'change' 117 if (eventType === 'change'
116 && (filename === this.configFileName || filename === null)) { 118 && (filename === this.configFileName || filename === null)) {
117 configChanged()?.catch((err) => { 119 configChanged()?.catch((err) => {
118 console.log('Unhandled error while listening for config changes', err); 120 log.error('Unhandled error while listening for config changes', err);
119 }); 121 });
120 } 122 }
121 }); 123 });
diff --git a/packages/main/src/stores/Config.ts b/packages/main/src/stores/Config.ts
index 7d1168f..06dbdeb 100644
--- a/packages/main/src/stores/Config.ts
+++ b/packages/main/src/stores/Config.ts
@@ -18,13 +18,13 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { Instance } from 'mobx-state-tree';
22import { 21import {
23 config as originalConfig, 22 config as originalConfig,
24 ConfigSnapshotIn, 23 ConfigSnapshotIn,
25 ConfigSnapshotOut, 24 ConfigSnapshotOut,
26 ThemeSource, 25 ThemeSource,
27} from '@sophie/shared'; 26} from '@sophie/shared';
27import { Instance } from 'mobx-state-tree';
28 28
29export const config = originalConfig.actions((self) => ({ 29export const config = originalConfig.actions((self) => ({
30 setThemeSource(mode: ThemeSource) { 30 setThemeSource(mode: ThemeSource) {
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts
index f8a09d6..7b26c52 100644
--- a/packages/main/src/stores/MainStore.ts
+++ b/packages/main/src/stores/MainStore.ts
@@ -18,8 +18,8 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { applySnapshot, Instance, types } from 'mobx-state-tree';
22import { BrowserViewBounds } from '@sophie/shared'; 21import { BrowserViewBounds } from '@sophie/shared';
22import { applySnapshot, Instance, types } from 'mobx-state-tree';
23 23
24import type { Config } from './Config.js'; 24import type { Config } from './Config.js';
25import { sharedStore } from './SharedStore'; 25import { sharedStore } from './SharedStore';
diff --git a/packages/main/src/stores/SharedStore.ts b/packages/main/src/stores/SharedStore.ts
index e20150d..c023fc7 100644
--- a/packages/main/src/stores/SharedStore.ts
+++ b/packages/main/src/stores/SharedStore.ts
@@ -18,8 +18,8 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { Instance, types } from 'mobx-state-tree';
22import { sharedStore as originalSharedStore } from '@sophie/shared'; 21import { sharedStore as originalSharedStore } from '@sophie/shared';
22import { Instance, types } from 'mobx-state-tree';
23 23
24import { config } from './Config'; 24import { config } from './Config';
25 25
diff --git a/packages/main/src/utils/disposer.ts b/packages/main/src/utils/Disposer.ts
index 0d469dd..2e0ca25 100644
--- a/packages/main/src/utils/disposer.ts
+++ b/packages/main/src/utils/Disposer.ts
@@ -20,4 +20,6 @@
20 20
21import { IDisposer } from 'mobx-state-tree'; 21import { IDisposer } from 'mobx-state-tree';
22 22
23export type Disposer = IDisposer; 23type Disposer = IDisposer;
24
25export default Disposer;
diff --git a/packages/main/src/utils/logging.ts b/packages/main/src/utils/log.ts
index f703749..c704797 100644
--- a/packages/main/src/utils/logging.ts
+++ b/packages/main/src/utils/log.ts
@@ -45,7 +45,11 @@ prefix.reg(loglevel);
45prefix.apply(loglevel, { 45prefix.apply(loglevel, {
46 format(level, name, timestamp) { 46 format(level, name, timestamp) {
47 const levelColor = getColor(level); 47 const levelColor = getColor(level);
48 return `${chalk.gray(`[${timestamp}]`)} ${levelColor(level)} ${chalk.green(`${name}:`)}`; 48 const timeStr = timestamp.toString();
49 const nameStr = typeof name === 'undefined'
50 ? levelColor(':')
51 : ` ${chalk.green(`${name}:`)}`;
52 return `${chalk.gray(`[${timeStr}]`)} ${levelColor(level)}${nameStr}`;
49 }, 53 },
50}); 54});
51 55
@@ -56,7 +60,7 @@ export function getLogger(loggerName: string): Logger {
56export function silenceLogger(): void { 60export function silenceLogger(): void {
57 loglevel.disableAll(); 61 loglevel.disableAll();
58 const loggers = loglevel.getLoggers(); 62 const loggers = loglevel.getLoggers();
59 for (const loggerName of Object.keys(loggers)) { 63 Object.keys(loggers).forEach((loggerName) => {
60 loggers[loggerName].disableAll(); 64 loggers[loggerName].disableAll();
61 } 65 });
62} 66}
diff --git a/packages/main/tsconfig.json b/packages/main/tsconfig.json
index 1401445..00a1985 100644
--- a/packages/main/tsconfig.json
+++ b/packages/main/tsconfig.json
@@ -1,5 +1,5 @@
1{ 1{
2 "extends": "../../tsconfig.json", 2 "extends": "../../config/tsconfig.base.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "noEmit": true, 4 "noEmit": true,
5 "types": [ 5 "types": [
@@ -9,14 +9,16 @@
9 }, 9 },
10 "references": [ 10 "references": [
11 { 11 {
12 "path": "../service-shared" 12 "path": "../service-shared/tsconfig.build.json"
13 }, 13 },
14 { 14 {
15 "path": "../shared" 15 "path": "../shared/tsconfig.build.json"
16 } 16 }
17 ], 17 ],
18 "include": [ 18 "include": [
19 "src/**/*.ts", 19 "src/**/*.ts",
20 "types/**/*.d.ts" 20 "types/**/*.d.ts",
21 "esbuild.config.js",
22 "jest.config.js"
21 ] 23 ]
22} 24}