From d9099095916eab249442a676c3d8f1121e94a935 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 8 Mar 2026 16:27:29 +0100 Subject: [PATCH] Auto-Updater: Doppelstart verhindern + Download-Status sichtbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - State-Tracking im Main-Prozess (idle/checking/downloading/ready) - Manueller Check gibt aktuellen Status zurück statt Squirrel-Doppelstart - Auto-Check nur wenn idle (kein Konflikt mit laufendem Download) - Frontend synchronisiert Status beim Mount und Modal-Öffnen - getUpdateStatus IPC für synchrone Status-Abfrage Co-Authored-By: Claude Opus 4.6 --- electron/main.js | 56 +++++++++++++++++++++++++++++++++++---------- electron/preload.js | 1 + web/src/App.tsx | 17 +++++++++++++- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/electron/main.js b/electron/main.js index 222d873..21f33fb 100644 --- a/electron/main.js +++ b/electron/main.js @@ -33,43 +33,75 @@ function setupAutoUpdater() { return; } + // State-Tracking: verhindert Doppelstarts und bewahrt Status + let updateState = 'idle'; // idle | checking | downloading | ready + + autoUpdater.on('checking-for-update', () => { + console.log('[AutoUpdater] Checking for updates...'); + updateState = 'checking'; + }); + autoUpdater.on('update-available', () => { console.log('[AutoUpdater] Update available, downloading...'); + updateState = 'downloading'; if (mainWindow) mainWindow.webContents.send('update-available'); }); autoUpdater.on('update-downloaded', (_event, releaseNotes, releaseName) => { console.log('[AutoUpdater] Update downloaded:', releaseName || 'new version'); + updateState = 'ready'; if (mainWindow) mainWindow.webContents.send('update-ready'); }); + autoUpdater.on('update-not-available', () => { + console.log('[AutoUpdater] App is up to date.'); + updateState = 'idle'; + if (mainWindow) mainWindow.webContents.send('update-not-available'); + }); + + autoUpdater.on('error', (err) => { + console.error('[AutoUpdater] Error:', err.message); + updateState = 'idle'; + if (mainWindow) mainWindow.webContents.send('update-error', err.message); + }); + // Handle install-update request from renderer ipcMain.on('install-update', () => { autoUpdater.quitAndInstall(); }); - autoUpdater.on('update-not-available', () => { - console.log('[AutoUpdater] App is up to date.'); - if (mainWindow) mainWindow.webContents.send('update-not-available'); - }); - - // Manual check from renderer + // Manual check from renderer — gibt aktuellen Status zurück statt Doppelstart ipcMain.on('check-for-updates', () => { + if (updateState === 'downloading') { + if (mainWindow) mainWindow.webContents.send('update-available'); + return; + } + if (updateState === 'ready') { + if (mainWindow) mainWindow.webContents.send('update-ready'); + return; + } + if (updateState === 'checking') { + return; // bereits am Prüfen + } try { autoUpdater.checkForUpdates(); } catch (e) { console.error('[AutoUpdater]', e.message); } }); - autoUpdater.on('error', (err) => { - console.error('[AutoUpdater] Error:', err.message); - if (mainWindow) mainWindow.webContents.send('update-error', err.message); + // Sync-Abfrage: Frontend kann aktuellen Status beim Modal-Öffnen abfragen + ipcMain.on('get-update-status', (event) => { + event.returnValue = updateState; }); - // Check for updates after 5 seconds, then every 30 minutes + // Auto-Check nach 5 Sek, dann alle 30 Min (nur wenn idle) setTimeout(() => { - try { autoUpdater.checkForUpdates(); } catch (e) { console.error('[AutoUpdater]', e.message); } + if (updateState === 'idle') { + try { autoUpdater.checkForUpdates(); } catch (e) { console.error('[AutoUpdater]', e.message); } + } }, 5000); setInterval(() => { - try { autoUpdater.checkForUpdates(); } catch (e) { console.error('[AutoUpdater]', e.message); } + if (updateState === 'idle') { + try { autoUpdater.checkForUpdates(); } catch (e) { console.error('[AutoUpdater]', e.message); } + } }, 30 * 60 * 1000); } diff --git a/electron/preload.js b/electron/preload.js index ff2fc1b..967a2cb 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -9,6 +9,7 @@ contextBridge.exposeInMainWorld('electronAPI', { onUpdateNotAvailable: (callback) => ipcRenderer.on('update-not-available', callback), onUpdateError: (callback) => ipcRenderer.on('update-error', (_e, msg) => callback(msg)), checkForUpdates: () => ipcRenderer.send('check-for-updates'), + getUpdateStatus: () => ipcRenderer.sendSync('get-update-status'), installUpdate: () => ipcRenderer.send('install-update'), setStreaming: (active) => ipcRenderer.send('streaming-status', active), showNotification: (title, body) => ipcRenderer.send('show-notification', title, body), diff --git a/web/src/App.tsx b/web/src/App.tsx index d864dd1..4cf908f 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -65,6 +65,11 @@ export default function App() { setUpdateStatus('error'); setUpdateError(msg || 'Unbekannter Fehler'); }); + // Sync initial status (Hintergrund-Download könnte bereits laufen) + const currentState = api.getUpdateStatus?.(); + if (currentState === 'downloading') setUpdateStatus('downloading'); + else if (currentState === 'ready') setUpdateStatus('ready'); + else if (currentState === 'checking') setUpdateStatus('checking'); }, [isElectron]); // Fetch plugin list @@ -185,7 +190,17 @@ export default function App() { )} setShowVersionModal(true)} + onClick={() => { + // Status vom Main-Prozess synchronisieren bevor Modal öffnet + if (isElectron) { + const api = (window as any).electronAPI; + const s = api.getUpdateStatus?.(); + if (s === 'downloading') setUpdateStatus('downloading'); + else if (s === 'ready') setUpdateStatus('ready'); + else if (s === 'checking') setUpdateStatus('checking'); + } + setShowVersionModal(true); + }} title="Versionsinformationen" > v{version}