From a8dabfd6e5ee21c29951fe914c85f46a89cb777d Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:48:58 +1100 Subject: [PATCH 01/10] Rename window settings: AppWindowSettings --- src/main-process/appWindow.ts | 8 ++++---- src/store/index.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main-process/appWindow.ts b/src/main-process/appWindow.ts index 380d33d5..2d1089bc 100644 --- a/src/main-process/appWindow.ts +++ b/src/main-process/appWindow.ts @@ -1,7 +1,7 @@ import { BrowserWindow, screen, app, shell, ipcMain, Tray, Menu, dialog, MenuItem } from 'electron'; import path from 'node:path'; import Store from 'electron-store'; -import { StoreType } from '../store'; +import { AppWindowSettings } from '../store'; import log from 'electron-log/main'; import { IPC_CHANNELS, ProgressStatus, ServerArgs } from '../constants'; import { getAppResourcesPath } from '../install/resourcePaths'; @@ -12,7 +12,7 @@ import { getAppResourcesPath } from '../install/resourcePaths'; */ export class AppWindow { private window: BrowserWindow; - private store: Store; + private store: Store; private messageQueue: Array<{ channel: string; data: any }> = []; private rendererReady: boolean = false; @@ -142,10 +142,10 @@ export class AppWindow { * There are edge cases where this might not be a catastrophic failure, but inability * to write to our own datastore may result in unexpected user data loss. */ - private loadWindowStore(): Store { + private loadWindowStore(): Store { try { // Separate file for non-critical convenience settings - just resets itself if invalid - return new Store({ + return new Store({ clearInvalidConfig: true, name: 'window', }); diff --git a/src/store/index.ts b/src/store/index.ts index 64997873..a739c99c 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,4 +1,4 @@ -export type StoreType = { +export type AppWindowSettings = { windowWidth: number; windowHeight: number; windowX: number | undefined; From 66769691408d9048c2206628fa091baf341de951 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:38:03 +1100 Subject: [PATCH 02/10] Add settings store, required for startup If present, the config must be valid to proceed with app startup. --- src/main.ts | 17 +++++++- src/store/store.ts | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/store/store.ts diff --git a/src/main.ts b/src/main.ts index fc5d8cea..d804b37e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { AppInfoHandlers } from './handlers/appInfoHandlers'; import { ComfyDesktopApp } from './main-process/comfyDesktopApp'; import { LevelOption } from 'electron-log'; import SentryLogging from './services/sentry'; +import { useDesktopStore } from './store/store'; dotenv.config(); log.initialize(); @@ -40,6 +41,17 @@ if (!gotTheLock) { app.on('ready', async () => { log.debug('App ready'); + const store = await useDesktopStore().loadStore(); + if (store) { + startApp(); + } else { + app.exit(20); + } + }); +} + +async function startApp() { + try { const appWindow = new AppWindow(); appWindow.onClose(() => { log.info('App window closed. Quitting application.'); @@ -79,5 +91,8 @@ if (!gotTheLock) { appWindow.sendServerStartProgress(ProgressStatus.ERROR); appWindow.send(IPC_CHANNELS.LOG_MESSAGE, error); } - }); + } catch (error) { + log.error('Fatal error occurred during app startup.', error); + app.exit(2024); + } } diff --git a/src/store/store.ts b/src/store/store.ts new file mode 100644 index 00000000..2f9552d6 --- /dev/null +++ b/src/store/store.ts @@ -0,0 +1,99 @@ +import log from 'electron-log/main'; +import ElectronStore from 'electron-store'; +import { app, dialog } from 'electron'; +import path from 'node:path'; +import fs from 'fs/promises'; +import type { DesktopSettings } from '.'; + +let currentStore: ElectronStore; + +/** Generic wrapper class to load electron stores and handle errors. */ +export function useDesktopStore() { + const store = currentStore; + + async function loadStore( + options?: ConstructorParameters>[0] + ): Promise | undefined> { + try { + currentStore = new ElectronStore(options); + return currentStore; + } catch (error) { + const configFilePath = path.join(getUserDataOrQuit(), `${options?.name ?? 'config'}.json`); + + if (error instanceof SyntaxError) { + // The .json file is invalid. Prompt user to reset. + const { response } = await showResetPrompt(configFilePath); + + // You sure? + if (response === 0) { + const { response } = await showConfirmReset(configFilePath); + + if (response === 0) { + // Delete all settings + await tryDeleteConfigFile(configFilePath); + + // Causing a stack overflow from this recursion would take immense patience. + return loadStore(options); + } + } + + // User chose to exit + app.quit(); + } else { + // Crash: Unknown filesystem error, permission denied on user data folder, etc + log.error(`Unknown error whilst loading configuration file: ${configFilePath}`, error); + dialog.showErrorBox('User Data', `Unknown error whilst writing to user data folder:\n\n${configFilePath}`); + } + } + } + + return { + store, + loadStore, + }; +} + +function showResetPrompt(configFilePath: string): Promise { + return dialog.showMessageBox({ + title: 'Invalid configuration file', + type: 'error', + message: `Format of the configuration file is invalid:\n\n${configFilePath}`, + buttons: ['&Reset the configuration file', '&Quit'], + defaultId: 1, + cancelId: 1, + normalizeAccessKeys: true, + }); +} + +function showConfirmReset(configFilePath: string): Promise { + return dialog.showMessageBox({ + title: 'Confirm reset settings', + type: 'warning', + message: `The configuration file below will be cleared and all settings will be reset. You should back this file up before deleting it.\n\n${configFilePath}`, + buttons: ['Try to open the &file', '&Yes, delete all settings', '&Quit'], + defaultId: 1, + cancelId: 1, + normalizeAccessKeys: true, + }); +} + +async function tryDeleteConfigFile(configFilePath: string) { + try { + await fs.rm(configFilePath); + } catch (error) { + log.error(`Unable to delete configuration file: ${configFilePath}`, error); + dialog.showErrorBox('Delete Failed', `Unknown error whilst attempting to delete config file:\n\n${configFilePath}`); + } +} + +function getUserDataOrQuit() { + try { + return app.getPath('userData'); + } catch (error) { + // Crash: Can't even find the user userData folder + log.error('Cannot find user data folder.', error); + dialog.showErrorBox('User Data', 'Unknown error whilst attempting to determine user data folder.'); + app.quit(); + throw error; + } +} From 34d9353918c34fef46d868a35b449f04bf16e690 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:51:26 +1100 Subject: [PATCH 03/10] Add shell open option to debug --- src/store/store.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/store/store.ts b/src/store/store.ts index 2f9552d6..30f8e9af 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,6 +1,6 @@ import log from 'electron-log/main'; import ElectronStore from 'electron-store'; -import { app, dialog } from 'electron'; +import { app, dialog, shell } from 'electron'; import path from 'node:path'; import fs from 'fs/promises'; import type { DesktopSettings } from '.'; @@ -24,11 +24,17 @@ export function useDesktopStore() { // The .json file is invalid. Prompt user to reset. const { response } = await showResetPrompt(configFilePath); - // You sure? - if (response === 0) { + if (response === 1) { + // Open dir with file selected + shell.showItemInFolder(configFilePath); + } else if (response === 0) { + // Reset - you sure? const { response } = await showConfirmReset(configFilePath); if (response === 0) { + // Open dir with file selected + shell.showItemInFolder(configFilePath); + } else if (response === 1) { // Delete all settings await tryDeleteConfigFile(configFilePath); @@ -57,10 +63,10 @@ function showResetPrompt(configFilePath: string): Promise Date: Tue, 26 Nov 2024 20:14:59 +1100 Subject: [PATCH 04/10] Add initial DesktopSettings type --- src/store/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/store/index.ts b/src/store/index.ts index a739c99c..f9553513 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -5,3 +5,7 @@ export type AppWindowSettings = { windowY: number | undefined; windowMaximized?: boolean; }; + +export type DesktopSettings = { + installState: 'started' | 'installed' | undefined; +} From 45c37485b835ec87c2b72e3e95e2a96387d32ade Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:40:39 +1100 Subject: [PATCH 05/10] Use app settings store for install state --- src/main-process/comfyDesktopApp.ts | 16 +++++++++++----- src/store/index.ts | 5 +++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/main-process/comfyDesktopApp.ts b/src/main-process/comfyDesktopApp.ts index 9e93f5d6..48653207 100644 --- a/src/main-process/comfyDesktopApp.ts +++ b/src/main-process/comfyDesktopApp.ts @@ -16,8 +16,9 @@ import { DownloadManager } from '../models/DownloadManager'; import { VirtualEnvironment } from '../virtualEnvironment'; import { InstallWizard } from '../install/installWizard'; import { Terminal } from '../terminal'; +import { useDesktopStore } from '../store/store'; import { restoreCustomNodes } from '../services/backup'; -import Store from 'electron-store'; + export class ComfyDesktopApp { public comfyServer: ComfyServer | null = null; private terminal: Terminal | null = null; // Only created after server starts. @@ -144,7 +145,11 @@ export class ComfyDesktopApp { return new Promise((resolve) => { ipcMain.on(IPC_CHANNELS.INSTALL_COMFYUI, async (event, installOptions: InstallOptions) => { const installWizard = new InstallWizard(installOptions); + const { store } = useDesktopStore(); + store.set('basePath', installWizard.basePath); + await installWizard.install(); + store.set('installState', 'installed'); resolve(installWizard.basePath); }); }); @@ -181,7 +186,7 @@ export class ComfyDesktopApp { this.appWindow.send(IPC_CHANNELS.LOG_MESSAGE, data); }, }); - const store = new Store(); + const { store } = useDesktopStore(); if (!store.get('Comfy-Desktop.RestoredCustomNodes', false)) { try { await restoreCustomNodes(virtualEnvironment, this.appWindow); @@ -199,9 +204,10 @@ export class ComfyDesktopApp { } static async create(appWindow: AppWindow): Promise { - const basePath = ComfyServerConfig.exists() - ? await ComfyServerConfig.readBasePathFromConfig(ComfyServerConfig.configPath) - : await this.install(appWindow); + const { store } = useDesktopStore(); + + const installed = store.get('installState') === 'installed'; + const basePath = installed ? store.get('basePath') : await this.install(appWindow); if (!basePath) { throw new Error(`Base path not found! ${ComfyServerConfig.configPath} is probably corrupted.`); diff --git a/src/store/index.ts b/src/store/index.ts index f9553513..7c83a149 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -7,5 +7,6 @@ export type AppWindowSettings = { }; export type DesktopSettings = { - installState: 'started' | 'installed' | undefined; -} + basePath?: string; + installState?: 'started' | 'installed'; +}; From f5fed9dfe0844f0ad79193bc68dc9e1bbe710fa1 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Wed, 27 Nov 2024 01:00:24 +1100 Subject: [PATCH 06/10] Add setting store reset to resetInstall script --- scripts/resetInstall.js | 54 +++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/scripts/resetInstall.js b/scripts/resetInstall.js index 4d25605c..4d82b0e3 100644 --- a/scripts/resetInstall.js +++ b/scripts/resetInstall.js @@ -6,15 +6,16 @@ const readline = require('readline'); /** * Get the path to the extra_models_config.yaml file based on the platform. + * @param {string} filename The name of the file to find in the user data folder * @returns The path to the extra_models_config.yaml file. */ -function getConfigPath() { +function getConfigPath(filename) { switch (process.platform) { case 'darwin': // macOS - return path.join(os.homedir(), 'Library', 'Application Support', 'ComfyUI', 'extra_models_config.yaml'); + return path.join(os.homedir(), 'Library', 'Application Support', 'ComfyUI', filename); case 'win32': // Windows - return path.join(process.env.APPDATA, 'ComfyUI', 'extra_models_config.yaml'); + return path.join(process.env.APPDATA, 'ComfyUI', filename); default: console.log('Platform not supported for this operation'); process.exit(1); @@ -37,27 +38,54 @@ async function askForConfirmation(question) { async function main() { try { - const configPath = getConfigPath(); + const configPath = getConfigPath('config.json'); + const windowStorePath = getConfigPath('window.json'); + const modelsConfigPath = getConfigPath('extra_models_config.yaml'); + let desktopBasePath = null; let basePath = null; - // Read base_path before deleting the config file + // Read basePath from desktop config if (fs.existsSync(configPath)) { const configContent = fs.readFileSync(configPath, 'utf8'); + const parsed = JSON.parse(configContent); + desktopBasePath = parsed?.basePath; + } + + // Read base_path before deleting the config file + if (fs.existsSync(modelsConfigPath)) { + const configContent = fs.readFileSync(modelsConfigPath, 'utf8'); const config = yaml.parse(configContent); basePath = config?.comfyui?.base_path; - - // Delete config file - fs.unlinkSync(configPath); - console.log(`Successfully removed ${configPath}`); } else { console.log('Config file not found, nothing to remove'); } - // If base_path exists, ask user if they want to delete it - if (basePath && fs.existsSync(basePath)) { - console.log(`Found ComfyUI installation directory at: ${basePath}`); + // Delete all config files + for (const file of [configPath, windowStorePath, modelsConfigPath]) { + if (fs.existsSync(file)) { + fs.unlinkSync(file); + console.log(`Successfully removed ${file}`); + } + } + + // If config.json basePath exists, ask user if they want to delete it + if (desktopBasePath && fs.existsSync(desktopBasePath)) { + console.log(`Found ComfyUI installation directory at: ${desktopBasePath}`); + const shouldDelete = await askForConfirmation('Would you like to delete this directory as well?'); + + if (shouldDelete) { + fs.rmSync(desktopBasePath, { recursive: true, force: true }); + console.log(`Successfully removed ComfyUI directory at ${desktopBasePath}`); + } else { + console.log('Skipping ComfyUI directory deletion'); + } + } + + // If base_path exists and does not match basePath, ask user if they want to delete it + if (basePath && basePath !== desktopBasePath && fs.existsSync(basePath)) { + console.log(`Found ComfyUI models directory at: ${basePath}`); const shouldDelete = await askForConfirmation('Would you like to delete this directory as well?'); - + if (shouldDelete) { fs.rmSync(basePath, { recursive: true, force: true }); console.log(`Successfully removed ComfyUI directory at ${basePath}`); From d1a1d5134beafbd3868d5aa1a00d3ec98bbbfc76 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Thu, 28 Nov 2024 01:12:00 +1100 Subject: [PATCH 07/10] Add file validation to pre-window checks Provides the user with some information about what went wrong and why - and where they can look to fix it. Needs help / support button added. --- src/install/installationValidator.ts | 29 +++++++++++++++++++ src/main-process/comfyDesktopApp.ts | 42 ++++++++++++++++++++++++---- src/store/index.ts | 2 +- 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/install/installationValidator.ts diff --git a/src/install/installationValidator.ts b/src/install/installationValidator.ts new file mode 100644 index 00000000..45abfe52 --- /dev/null +++ b/src/install/installationValidator.ts @@ -0,0 +1,29 @@ +import { app, dialog, shell } from 'electron'; + +export class InstallationValidator { + /** + * Shows a dialog box with an option to open the problematic file in the native shell file viewer. + * @param options The options paramter of {@link dialog.showMessageBox}, filled with defaults for invalid config + * @returns + */ + static async showInvalidFileAndQuit(file: string, options: Electron.MessageBoxOptions): Promise { + const defaults: Electron.MessageBoxOptions = { + // Message must be set by caller. + message: `Was unable to read the file shown below. It could be missing, inaccessible, or corrupt.\n\n${file}`, + title: 'Invalid file', + type: 'error', + buttons: ['Locate the &file (then quit)', '&Quit'], + defaultId: 0, + cancelId: 1, + normalizeAccessKeys: true, + }; + const opt = Object.assign(defaults, options); + + const result = await dialog.showMessageBox(opt); + + if (result.response === 0) shell.showItemInFolder(file); + app.quit(); + // Wait patiently for graceful termination. + await new Promise(() => {}); + } +} diff --git a/src/main-process/comfyDesktopApp.ts b/src/main-process/comfyDesktopApp.ts index 48653207..f3bef42c 100644 --- a/src/main-process/comfyDesktopApp.ts +++ b/src/main-process/comfyDesktopApp.ts @@ -17,6 +17,7 @@ import { VirtualEnvironment } from '../virtualEnvironment'; import { InstallWizard } from '../install/installWizard'; import { Terminal } from '../terminal'; import { useDesktopStore } from '../store/store'; +import { InstallationValidator } from '../install/installationValidator'; import { restoreCustomNodes } from '../services/backup'; export class ComfyDesktopApp { @@ -205,16 +206,47 @@ export class ComfyDesktopApp { static async create(appWindow: AppWindow): Promise { const { store } = useDesktopStore(); + // Migrate settings from old version if required + const installState = store.get('installState') ?? (await ComfyDesktopApp.migrateInstallState()); - const installed = store.get('installState') === 'installed'; - const basePath = installed ? store.get('basePath') : await this.install(appWindow); + // Fresh install + const basePath = + installState === undefined ? await ComfyDesktopApp.install(appWindow) : await ComfyDesktopApp.loadBasePath(); - if (!basePath) { - throw new Error(`Base path not found! ${ComfyServerConfig.configPath} is probably corrupted.`); - } return new ComfyDesktopApp(basePath, new ComfySettings(basePath), appWindow); } + /** + * Sets the ugpraded state if this is a version upgrade from <= 0.3.18 + * @returns 'upgraded' if this install has just been upgraded, or undefined for a fresh install + */ + static async migrateInstallState(): Promise { + // Fresh install + if (!ComfyServerConfig.exists()) return undefined; + + // Upgrade + const basePath = await ComfyDesktopApp.loadBasePath(); + + // Migrate config + const { store } = useDesktopStore(); + const upgraded = 'upgraded'; + store.set('installState', upgraded); + store.set('basePath', basePath); + return upgraded; + } + + /** Loads the base_path value from the YAML config. Quits in the event of failure. */ + static async loadBasePath(): Promise { + const basePath = await ComfyServerConfig.readBasePathFromConfig(ComfyServerConfig.configPath); + if (basePath) return basePath; + + log.error(`Base path not found! ${ComfyServerConfig.configPath} is probably corrupted.`); + await InstallationValidator.showInvalidFileAndQuit(ComfyServerConfig.configPath, { + message: `Base path not found! This file is probably corrupt:\n\n${ComfyServerConfig.configPath}`, + }); + throw new Error(/* Unreachable. */); + } + uninstall(): void { fs.rmSync(ComfyServerConfig.configPath); } diff --git a/src/store/index.ts b/src/store/index.ts index 7c83a149..14b13707 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -8,5 +8,5 @@ export type AppWindowSettings = { export type DesktopSettings = { basePath?: string; - installState?: 'started' | 'installed'; + installState?: 'started' | 'installed' | 'upgraded'; }; From ce2d921d0a1baec9b6bc63111ff94eb2b8958059 Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Thu, 28 Nov 2024 01:42:01 +1100 Subject: [PATCH 08/10] [Refactor] Desktop config store back to class --- src/main-process/comfyDesktopApp.ts | 10 ++++----- src/main.ts | 4 ++-- src/store/{store.ts => desktopConfig.ts} | 27 ++++++++++++------------ 3 files changed, 20 insertions(+), 21 deletions(-) rename src/store/{store.ts => desktopConfig.ts} (85%) diff --git a/src/main-process/comfyDesktopApp.ts b/src/main-process/comfyDesktopApp.ts index f3bef42c..8c94d9f7 100644 --- a/src/main-process/comfyDesktopApp.ts +++ b/src/main-process/comfyDesktopApp.ts @@ -16,7 +16,7 @@ import { DownloadManager } from '../models/DownloadManager'; import { VirtualEnvironment } from '../virtualEnvironment'; import { InstallWizard } from '../install/installWizard'; import { Terminal } from '../terminal'; -import { useDesktopStore } from '../store/store'; +import { DesktopConfig } from '../store/desktopConfig'; import { InstallationValidator } from '../install/installationValidator'; import { restoreCustomNodes } from '../services/backup'; @@ -146,7 +146,7 @@ export class ComfyDesktopApp { return new Promise((resolve) => { ipcMain.on(IPC_CHANNELS.INSTALL_COMFYUI, async (event, installOptions: InstallOptions) => { const installWizard = new InstallWizard(installOptions); - const { store } = useDesktopStore(); + const { store } = DesktopConfig; store.set('basePath', installWizard.basePath); await installWizard.install(); @@ -187,7 +187,7 @@ export class ComfyDesktopApp { this.appWindow.send(IPC_CHANNELS.LOG_MESSAGE, data); }, }); - const { store } = useDesktopStore(); + const { store } = DesktopConfig; if (!store.get('Comfy-Desktop.RestoredCustomNodes', false)) { try { await restoreCustomNodes(virtualEnvironment, this.appWindow); @@ -205,7 +205,7 @@ export class ComfyDesktopApp { } static async create(appWindow: AppWindow): Promise { - const { store } = useDesktopStore(); + const { store } = DesktopConfig; // Migrate settings from old version if required const installState = store.get('installState') ?? (await ComfyDesktopApp.migrateInstallState()); @@ -228,7 +228,7 @@ export class ComfyDesktopApp { const basePath = await ComfyDesktopApp.loadBasePath(); // Migrate config - const { store } = useDesktopStore(); + const { store } = DesktopConfig; const upgraded = 'upgraded'; store.set('installState', upgraded); store.set('basePath', basePath); diff --git a/src/main.ts b/src/main.ts index d804b37e..9c186198 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,7 @@ import { AppInfoHandlers } from './handlers/appInfoHandlers'; import { ComfyDesktopApp } from './main-process/comfyDesktopApp'; import { LevelOption } from 'electron-log'; import SentryLogging from './services/sentry'; -import { useDesktopStore } from './store/store'; +import { DesktopConfig } from './store/desktopConfig'; dotenv.config(); log.initialize(); @@ -41,7 +41,7 @@ if (!gotTheLock) { app.on('ready', async () => { log.debug('App ready'); - const store = await useDesktopStore().loadStore(); + const store = await DesktopConfig.load(); if (store) { startApp(); } else { diff --git a/src/store/store.ts b/src/store/desktopConfig.ts similarity index 85% rename from src/store/store.ts rename to src/store/desktopConfig.ts index 30f8e9af..51ca9e2d 100644 --- a/src/store/store.ts +++ b/src/store/desktopConfig.ts @@ -5,18 +5,22 @@ import path from 'node:path'; import fs from 'fs/promises'; import type { DesktopSettings } from '.'; -let currentStore: ElectronStore; - -/** Generic wrapper class to load electron stores and handle errors. */ -export function useDesktopStore() { - const store = currentStore; +/** Handles loading of electron-store config, pre-window errors, and provides a non-null interface for the store. */ +export class DesktopConfig { + static #store: ElectronStore | undefined; + static get store(): ElectronStore { + const store = this.#store; + if (!store) throw new Error('Cannot access store before initialization.'); + return store; + } - async function loadStore( + static async load( options?: ConstructorParameters>[0] ): Promise | undefined> { try { - currentStore = new ElectronStore(options); - return currentStore; + DesktopConfig.#store = new ElectronStore(options); + + return DesktopConfig.#store; } catch (error) { const configFilePath = path.join(getUserDataOrQuit(), `${options?.name ?? 'config'}.json`); @@ -39,7 +43,7 @@ export function useDesktopStore() { await tryDeleteConfigFile(configFilePath); // Causing a stack overflow from this recursion would take immense patience. - return loadStore(options); + return DesktopConfig.load(options); } } @@ -52,11 +56,6 @@ export function useDesktopStore() { } } } - - return { - store, - loadStore, - }; } function showResetPrompt(configFilePath: string): Promise { From 64fab8d63780bff18f0c093a7c84eacfcc18e0af Mon Sep 17 00:00:00 2001 From: filtered <176114999+webfiltered@users.noreply.github.com> Date: Sat, 30 Nov 2024 04:10:42 +1100 Subject: [PATCH 09/10] Add better handling for showing file on exit --- src/install/installationValidator.ts | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/install/installationValidator.ts b/src/install/installationValidator.ts index 45abfe52..b92ebabb 100644 --- a/src/install/installationValidator.ts +++ b/src/install/installationValidator.ts @@ -1,4 +1,7 @@ import { app, dialog, shell } from 'electron'; +import fs from 'fs/promises'; +import log from 'electron-log/main'; +import path from 'node:path'; export class InstallationValidator { /** @@ -21,7 +24,33 @@ export class InstallationValidator { const result = await dialog.showMessageBox(opt); - if (result.response === 0) shell.showItemInFolder(file); + // Try show the file in file manager + if (result.response === 0) { + try { + const parsed = path.parse(file); + log.debug(`Attempting to open containing directory: ${parsed.dir}`); + await fs.access(file); + shell.showItemInFolder(file); + } catch (error) { + log.warn(`Could not access file whilst attempting to exit gracefully after a critical error.`, file); + try { + // Failed - try the parent dir + const parsed = path.parse(file); + await fs.access(parsed.dir); + shell.openPath(parsed.dir); + } catch (error) { + // Nothing works. Log, notify, quit. + log.error( + `Could not read directory containing file, whilst attempting to exit gracefully after a critical error.` + ); + dialog.showErrorBox( + 'Unable to fine file', + `Unable to find the file. Please navigate to it manually:\n\n${file}` + ); + } + } + } + app.quit(); // Wait patiently for graceful termination. await new Promise(() => {}); From 4f27889919ebe7f89c482bbd3993ea8fafb091f5 Mon Sep 17 00:00:00 2001 From: huchenlei Date: Tue, 3 Dec 2024 18:43:50 -0500 Subject: [PATCH 10/10] Add documentation --- src/store/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/store/index.ts b/src/store/index.ts index 14b13707..f0cabae9 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -8,5 +8,12 @@ export type AppWindowSettings = { export type DesktopSettings = { basePath?: string; + /** + * The state of the installation. + * - `started`: The installation has started. + * - `installed`: A fresh installation. + * - `upgraded`: An upgrade from a previous version that stores the base path + * in the yaml config. + */ installState?: 'started' | 'installed' | 'upgraded'; };