fix(updater): update function support alpha (#45)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Displays update status and allows manual update checking/installation
|
||||
*/
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import { Download, RefreshCw, CheckCircle2, AlertCircle, Loader2, Rocket } from 'lucide-react';
|
||||
import { Download, RefreshCw, Loader2, Rocket } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { useUpdateStore } from '@/stores/update';
|
||||
@@ -41,25 +41,6 @@ export function UpdateSettings() {
|
||||
await checkForUpdates();
|
||||
}, [checkForUpdates, clearError]);
|
||||
|
||||
const renderStatusIcon = () => {
|
||||
switch (status) {
|
||||
case 'checking':
|
||||
return <Loader2 className="h-5 w-5 animate-spin text-blue-500" />;
|
||||
case 'downloading':
|
||||
return <Download className="h-5 w-5 text-blue-500 animate-pulse" />;
|
||||
case 'available':
|
||||
return <Download className="h-5 w-5 text-green-500" />;
|
||||
case 'downloaded':
|
||||
return <CheckCircle2 className="h-5 w-5 text-green-500" />;
|
||||
case 'error':
|
||||
return <AlertCircle className="h-5 w-5 text-red-500" />;
|
||||
case 'not-available':
|
||||
return <CheckCircle2 className="h-5 w-5 text-green-500" />;
|
||||
default:
|
||||
return <RefreshCw className="h-5 w-5 text-muted-foreground" />;
|
||||
}
|
||||
};
|
||||
|
||||
const renderStatusText = () => {
|
||||
switch (status) {
|
||||
case 'checking':
|
||||
@@ -71,7 +52,7 @@ export function UpdateSettings() {
|
||||
case 'downloaded':
|
||||
return `Ready to install: v${updateInfo?.version}`;
|
||||
case 'error':
|
||||
return error || 'Update check failed';
|
||||
return 'Update check failed';
|
||||
case 'not-available':
|
||||
return 'You have the latest version';
|
||||
default:
|
||||
@@ -138,12 +119,9 @@ export function UpdateSettings() {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Current Version */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">Current Version</p>
|
||||
<p className="text-2xl font-bold">v{currentVersion}</p>
|
||||
</div>
|
||||
{renderStatusIcon()}
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">Current Version</p>
|
||||
<p className="text-2xl font-bold">v{currentVersion}</p>
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
|
||||
@@ -50,6 +50,7 @@ export function Settings() {
|
||||
|
||||
const { status: gatewayStatus, restart: restartGateway } = useGatewayStore();
|
||||
const currentVersion = useUpdateStore((state) => state.currentVersion);
|
||||
const updateSetAutoDownload = useUpdateStore((state) => state.setAutoDownload);
|
||||
const [controlUiInfo, setControlUiInfo] = useState<ControlUiInfo | null>(null);
|
||||
const [openclawCliCommand, setOpenclawCliCommand] = useState('');
|
||||
const [openclawCliError, setOpenclawCliError] = useState<string | null>(null);
|
||||
@@ -380,7 +381,10 @@ export function Settings() {
|
||||
</div>
|
||||
<Switch
|
||||
checked={autoDownloadUpdate}
|
||||
onCheckedChange={setAutoDownloadUpdate}
|
||||
onCheckedChange={(value) => {
|
||||
setAutoDownloadUpdate(value);
|
||||
updateSetAutoDownload(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Manages application update state
|
||||
*/
|
||||
import { create } from 'zustand';
|
||||
import { useSettingsStore } from './settings';
|
||||
|
||||
export interface UpdateInfo {
|
||||
version: string;
|
||||
@@ -83,6 +84,8 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
|
||||
}
|
||||
|
||||
// Listen for update events
|
||||
// Single source of truth: listen only to update:status-changed
|
||||
// (sent by AppUpdater.updateStatus() in the main process)
|
||||
window.electron.ipcRenderer.on('update:status-changed', (data) => {
|
||||
const status = data as {
|
||||
status: UpdateStatus;
|
||||
@@ -98,31 +101,22 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
|
||||
});
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:checking', () => {
|
||||
set({ status: 'checking', error: null });
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:available', (info) => {
|
||||
set({ status: 'available', updateInfo: info as UpdateInfo });
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:not-available', () => {
|
||||
set({ status: 'not-available' });
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:progress', (progress) => {
|
||||
set({ status: 'downloading', progress: progress as ProgressInfo });
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:downloaded', (info) => {
|
||||
set({ status: 'downloaded', updateInfo: info as UpdateInfo, progress: null });
|
||||
});
|
||||
|
||||
window.electron.ipcRenderer.on('update:error', (error) => {
|
||||
set({ status: 'error', error: error as string, progress: null });
|
||||
});
|
||||
|
||||
set({ isInitialized: true });
|
||||
|
||||
// Apply persisted settings from the settings store
|
||||
const { autoCheckUpdate, autoDownloadUpdate } = useSettingsStore.getState();
|
||||
|
||||
// Sync auto-download preference to the main process
|
||||
if (autoDownloadUpdate) {
|
||||
window.electron.ipcRenderer.invoke('update:setAutoDownload', true).catch(() => {});
|
||||
}
|
||||
|
||||
// Auto-check for updates on startup (respects user toggle)
|
||||
if (autoCheckUpdate) {
|
||||
setTimeout(() => {
|
||||
get().checkForUpdates().catch(() => {});
|
||||
}, 10000);
|
||||
}
|
||||
},
|
||||
|
||||
checkForUpdates: async () => {
|
||||
@@ -134,15 +128,34 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('Update check timed out')), 30000))
|
||||
]) as {
|
||||
success: boolean;
|
||||
info?: UpdateInfo;
|
||||
error?: string;
|
||||
status?: {
|
||||
status: UpdateStatus;
|
||||
info?: UpdateInfo;
|
||||
progress?: ProgressInfo;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
|
||||
if (!result.success) {
|
||||
if (result.status) {
|
||||
set({
|
||||
status: result.status.status,
|
||||
updateInfo: result.status.info || null,
|
||||
progress: result.status.progress || null,
|
||||
error: result.status.error || null,
|
||||
});
|
||||
} else if (!result.success) {
|
||||
set({ status: 'error', error: result.error || 'Failed to check for updates' });
|
||||
}
|
||||
} catch (error) {
|
||||
set({ status: 'error', error: String(error) });
|
||||
} finally {
|
||||
// In dev mode autoUpdater skips without emitting events, so the
|
||||
// status may still be 'checking' or even 'idle'. Catch both.
|
||||
const currentStatus = get().status;
|
||||
if (currentStatus === 'checking' || currentStatus === 'idle') {
|
||||
set({ status: 'error', error: 'Update check completed without a result. This usually means the app is running in dev mode.' });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user