feat: add auto-update functionality with settings UI
- Implement electron-updater integration with IPC handlers for update operations - Add Pinia store for managing update state and user preferences - Create settings UI with version display, update controls, and auto-update toggles - Update IPC constants and preload API for update-related communication - Refactor preload invoke methods to use async IPC consistently - Add rounded corners to settings page layout for better visual consistency
This commit is contained in:
@@ -6,6 +6,10 @@ import configManager from '@electron/service/config-service'
|
||||
import { runTaskOperationService } from '@electron/process/runTaskOperationService'
|
||||
import log from 'electron-log';
|
||||
import 'bytenode'; // Ensure bytenode is bundled/externalized correctly
|
||||
import { appUpdater } from '@electron/service/updater';
|
||||
|
||||
// 初始化 updater,确保在 app ready 之前或者之中注册好 IPC
|
||||
appUpdater.init();
|
||||
|
||||
// import logManager from '@electron/service/logger'
|
||||
|
||||
|
||||
@@ -23,17 +23,11 @@ const api: WindowApi = {
|
||||
// 通过 IPC 调用主进程
|
||||
readFile: (filePath: string) => ipcRenderer.invoke(IPC_EVENTS.READ_FILE, filePath),
|
||||
|
||||
// 同步调用
|
||||
invoke: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.sendSync(IPC_EVENTS.INVOKE, channel, ...args),
|
||||
// 异步调用(映射为 electron 的 invoke)
|
||||
invoke: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.invoke(channel, ...args),
|
||||
|
||||
// 异步调用
|
||||
invokeAsync: (channel: IPC_EVENTS, ...args: any[]) => {
|
||||
try {
|
||||
ipcRenderer.invoke(IPC_EVENTS.INVOKE_ASYNC, channel, ...args)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
// 异步调用(为了兼容老代码)
|
||||
invokeAsync: (channel: IPC_EVENTS, ...args: any[]) => ipcRenderer.invoke(channel, ...args),
|
||||
|
||||
// 监听主进程消息
|
||||
on: (event: IPC_EVENTS, callback: (...args: any[]) => void) => {
|
||||
|
||||
83
electron/service/updater/index.ts
Normal file
83
electron/service/updater/index.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
import { BrowserWindow, ipcMain, app } from 'electron';
|
||||
import { IPC_EVENTS } from '@lib/constants';
|
||||
|
||||
export class AppUpdater {
|
||||
private mainWindow: BrowserWindow | null = null;
|
||||
private static _instance: AppUpdater;
|
||||
private _initialized: boolean = false;
|
||||
|
||||
private constructor() {
|
||||
autoUpdater.autoDownload = false;
|
||||
}
|
||||
|
||||
public init() {
|
||||
if (this._initialized) return;
|
||||
this._initialized = true;
|
||||
this.setupListeners();
|
||||
this.registerHandlers();
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!this._instance) {
|
||||
this._instance = new AppUpdater();
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
setMainWindow(window: BrowserWindow) {
|
||||
this.mainWindow = window;
|
||||
}
|
||||
|
||||
private setupListeners() {
|
||||
autoUpdater.on('checking-for-update', () => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'checking' }));
|
||||
autoUpdater.on('update-available', (info) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'available', info }));
|
||||
autoUpdater.on('update-not-available', () => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'not-available' }));
|
||||
autoUpdater.on('download-progress', (progress) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'downloading', progress }));
|
||||
autoUpdater.on('update-downloaded', (info) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'downloaded', info }));
|
||||
autoUpdater.on('error', (error) => this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'error', error: error.message }));
|
||||
}
|
||||
|
||||
private sendToRenderer(channel: string, data: any) {
|
||||
BrowserWindow.getAllWindows().forEach(win => {
|
||||
if (!win.isDestroyed()) {
|
||||
win.webContents.send(channel, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private registerHandlers() {
|
||||
ipcMain.handle(IPC_EVENTS.UPDATE_CHECK, () => {
|
||||
if (app.isPackaged) {
|
||||
return autoUpdater.checkForUpdates();
|
||||
} else {
|
||||
// 在开发环境下模拟
|
||||
this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'checking' });
|
||||
setTimeout(() => {
|
||||
this.sendToRenderer(IPC_EVENTS.UPDATE_STATUS_CHANGED, { status: 'not-available' });
|
||||
}, 1500);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_EVENTS.UPDATE_DOWNLOAD, () => {
|
||||
if (app.isPackaged) {
|
||||
return autoUpdater.downloadUpdate();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_EVENTS.UPDATE_INSTALL, () => {
|
||||
if (app.isPackaged) {
|
||||
return autoUpdater.quitAndInstall();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
ipcMain.handle(IPC_EVENTS.UPDATE_VERSION, () => {
|
||||
return app.getVersion();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const appUpdater = AppUpdater.getInstance();
|
||||
Reference in New Issue
Block a user