From 1d0566fce7d723a5913fae9be7664634183366b4 Mon Sep 17 00:00:00 2001 From: AndyMik90 Date: Thu, 18 Dec 2025 10:59:25 +0100 Subject: [PATCH] feat: Auto-open settings to updates section when app update is ready When an app update is downloaded and ready to install, automatically opens the Settings dialog to the Updates section so users can easily install the update. Changes: - App.tsx: Listen for onAppUpdateDownloaded event and open settings - AdvancedSettings.tsx: Add Electron app update UI with download/install buttons - ipc.ts: Add app update methods to ElectronAPI interface - settings-mock.ts: Add browser mocks for app update methods --- auto-claude-ui/src/renderer/App.tsx | 14 ++ .../components/settings/AdvancedSettings.tsx | 169 +++++++++++++++++- .../src/renderer/lib/mocks/settings-mock.ts | 12 +- auto-claude-ui/src/shared/types/ipc.ts | 17 ++ 4 files changed, 208 insertions(+), 4 deletions(-) diff --git a/auto-claude-ui/src/renderer/App.tsx b/auto-claude-ui/src/renderer/App.tsx index 4cec2f30..d1d0a727 100644 --- a/auto-claude-ui/src/renderer/App.tsx +++ b/auto-claude-ui/src/renderer/App.tsx @@ -114,6 +114,20 @@ export function App() { }; }, []); + // Listen for app updates - auto-open settings to 'updates' section when update is ready + useEffect(() => { + // When an update is downloaded and ready to install, open settings to updates section + const cleanupDownloaded = window.electronAPI.onAppUpdateDownloaded(() => { + console.log('[App] Update downloaded, opening settings to updates section'); + setSettingsInitialSection('updates'); + setIsSettingsDialogOpen(true); + }); + + return () => { + cleanupDownloaded(); + }; + }, []); + // Check if selected project needs initialization (e.g., .auto-claude folder was deleted) useEffect(() => { if (selectedProject && !selectedProject.autoBuildPath && skippedInitProjectId !== selectedProject.id) { diff --git a/auto-claude-ui/src/renderer/components/settings/AdvancedSettings.tsx b/auto-claude-ui/src/renderer/components/settings/AdvancedSettings.tsx index 2c5722f3..ae08027c 100644 --- a/auto-claude-ui/src/renderer/components/settings/AdvancedSettings.tsx +++ b/auto-claude-ui/src/renderer/components/settings/AdvancedSettings.tsx @@ -5,7 +5,9 @@ import { AlertCircle, CloudDownload, Loader2, - ExternalLink + ExternalLink, + Download, + Sparkles } from 'lucide-react'; import { Button } from '../ui/button'; import { Label } from '../ui/label'; @@ -13,7 +15,13 @@ import { Switch } from '../ui/switch'; import { Progress } from '../ui/progress'; import { cn } from '../../lib/utils'; import { SettingsSection } from './SettingsSection'; -import type { AppSettings, AutoBuildSourceUpdateCheck, AutoBuildSourceUpdateProgress } from '../../../shared/types'; +import type { + AppSettings, + AutoBuildSourceUpdateCheck, + AutoBuildSourceUpdateProgress, + AppUpdateAvailableEvent, + AppUpdateProgress +} from '../../../shared/types'; /** * Simple markdown renderer for release notes @@ -69,14 +77,22 @@ export function AdvancedSettings({ settings, onSettingsChange, section, version const [isDownloadingUpdate, setIsDownloadingUpdate] = useState(false); const [downloadProgress, setDownloadProgress] = useState(null); + // Electron app update state + const [appUpdateInfo, setAppUpdateInfo] = useState(null); + const [isCheckingAppUpdate, setIsCheckingAppUpdate] = useState(false); + const [isDownloadingAppUpdate, setIsDownloadingAppUpdate] = useState(false); + const [appDownloadProgress, setAppDownloadProgress] = useState(null); + const [isAppUpdateDownloaded, setIsAppUpdateDownloaded] = useState(false); + // Check for updates on mount useEffect(() => { if (section === 'updates') { checkForSourceUpdates(); + checkForAppUpdates(); } }, [section]); - // Listen for download progress + // Listen for source download progress useEffect(() => { const cleanup = window.electronAPI.onAutoBuildSourceUpdateProgress((progress) => { setDownloadProgress(progress); @@ -91,6 +107,62 @@ export function AdvancedSettings({ settings, onSettingsChange, section, version return cleanup; }, []); + // Listen for app update events + useEffect(() => { + const cleanupAvailable = window.electronAPI.onAppUpdateAvailable((info) => { + setAppUpdateInfo(info); + setIsCheckingAppUpdate(false); + }); + + const cleanupDownloaded = window.electronAPI.onAppUpdateDownloaded((info) => { + setAppUpdateInfo(info); + setIsDownloadingAppUpdate(false); + setIsAppUpdateDownloaded(true); + setAppDownloadProgress(null); + }); + + const cleanupProgress = window.electronAPI.onAppUpdateProgress((progress) => { + setAppDownloadProgress(progress); + }); + + return () => { + cleanupAvailable(); + cleanupDownloaded(); + cleanupProgress(); + }; + }, []); + + const checkForAppUpdates = async () => { + setIsCheckingAppUpdate(true); + try { + const result = await window.electronAPI.checkAppUpdate(); + if (result.success && result.data) { + setAppUpdateInfo(result.data); + } else { + // No update available + setAppUpdateInfo(null); + } + } catch (err) { + console.error('Failed to check for app updates:', err); + } finally { + setIsCheckingAppUpdate(false); + } + }; + + const handleDownloadAppUpdate = async () => { + setIsDownloadingAppUpdate(true); + try { + await window.electronAPI.downloadAppUpdate(); + } catch (err) { + console.error('Failed to download app update:', err); + setIsDownloadingAppUpdate(false); + } + }; + + const handleInstallAppUpdate = () => { + window.electronAPI.installAppUpdate(); + }; + const checkForSourceUpdates = async () => { setIsCheckingSourceUpdate(true); try { @@ -118,6 +190,97 @@ export function AdvancedSettings({ settings, onSettingsChange, section, version description="Manage Auto Claude updates" >
+ {/* Electron App Update Section */} + {(appUpdateInfo || isAppUpdateDownloaded) && ( +
+
+ +

App Update Ready

+
+ +
+
+

+ New Version +

+

+ {appUpdateInfo?.version || 'Unknown'} +

+ {appUpdateInfo?.releaseDate && ( +

+ Released {new Date(appUpdateInfo.releaseDate).toLocaleDateString()} +

+ )} +
+ {isAppUpdateDownloaded ? ( + + ) : isDownloadingAppUpdate ? ( + + ) : ( + + )} +
+ + {/* Release Notes */} + {appUpdateInfo?.releaseNotes && ( +
+ +
+ )} + + {/* Download Progress */} + {isDownloadingAppUpdate && appDownloadProgress && ( +
+
+ Downloading... + + {Math.round(appDownloadProgress.percent)}% + +
+ +

+ {(appDownloadProgress.transferred / 1024 / 1024).toFixed(2)} MB / {(appDownloadProgress.total / 1024 / 1024).toFixed(2)} MB +

+
+ )} + + {/* Downloaded Success */} + {isAppUpdateDownloaded && ( +
+ + Update downloaded! Click Install to restart and apply the update. +
+ )} + + {/* Action Buttons */} +
+ {isAppUpdateDownloaded ? ( + + ) : ( + + )} +
+
+ )} + {/* Unified Version Display with Update Check */}
diff --git a/auto-claude-ui/src/renderer/lib/mocks/settings-mock.ts b/auto-claude-ui/src/renderer/lib/mocks/settings-mock.ts index d22cca9f..6bc890d0 100644 --- a/auto-claude-ui/src/renderer/lib/mocks/settings-mock.ts +++ b/auto-claude-ui/src/renderer/lib/mocks/settings-mock.ts @@ -14,5 +14,15 @@ export const settingsMock = { saveSettings: async () => ({ success: true }), // App Info - getAppVersion: async () => '0.1.0-browser' + getAppVersion: async () => '0.1.0-browser', + + // App Update Operations (mock - no updates in browser mode) + checkAppUpdate: async () => ({ success: true, data: null }), + downloadAppUpdate: async () => ({ success: true }), + installAppUpdate: () => { console.log('[browser-mock] installAppUpdate called'); }, + + // App Update Event Listeners (no-op in browser mode) + onAppUpdateAvailable: () => () => {}, + onAppUpdateDownloaded: () => () => {}, + onAppUpdateProgress: () => () => {} }; diff --git a/auto-claude-ui/src/shared/types/ipc.ts b/auto-claude-ui/src/shared/types/ipc.ts index 3d218f3f..2c82e633 100644 --- a/auto-claude-ui/src/shared/types/ipc.ts +++ b/auto-claude-ui/src/shared/types/ipc.ts @@ -55,6 +55,7 @@ import type { ClaudeUsageSnapshot } from './agent'; import type { AppSettings, SourceEnvConfig, SourceEnvCheckResult, AutoBuildSourceUpdateCheck, AutoBuildSourceUpdateProgress } from './settings'; +import type { AppUpdateInfo, AppUpdateProgress, AppUpdateAvailableEvent, AppUpdateDownloadedEvent } from './app-update'; import type { ChangelogTask, TaskSpecContent, @@ -388,6 +389,22 @@ export interface ElectronAPI { callback: (progress: AutoBuildSourceUpdateProgress) => void ) => () => void; + // Electron app update operations + checkAppUpdate: () => Promise>; + downloadAppUpdate: () => Promise; + installAppUpdate: () => void; + + // Electron app update event listeners + onAppUpdateAvailable: ( + callback: (info: AppUpdateAvailableEvent) => void + ) => () => void; + onAppUpdateDownloaded: ( + callback: (info: AppUpdateDownloadedEvent) => void + ) => () => void; + onAppUpdateProgress: ( + callback: (progress: AppUpdateProgress) => void + ) => () => void; + // Shell operations openExternal: (url: string) => Promise;