diff --git a/src/app/components/update-notification/UpdateNotification.tsx b/src/app/components/update-notification/UpdateNotification.tsx index c9e4626..47a5bf9 100644 --- a/src/app/components/update-notification/UpdateNotification.tsx +++ b/src/app/components/update-notification/UpdateNotification.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { Box, IconButton, Spinner, Text, Menu, PopOut, Button, config } from 'folds'; +import { Box, Spinner, Text, Menu, PopOut, Button, config } from 'folds'; import * as css from './UpdateNotification.css'; interface UpdateInfo { @@ -14,6 +14,207 @@ interface DownloadProgress { total: number; } +export function UpdateNotification() { + const [updateAvailable, setUpdateAvailable] = useState(false); + const [updateInfo, setUpdateInfo] = useState(null); + const [downloading, setDownloading] = useState(false); + const [downloadProgress, setDownloadProgress] = useState(0); + const [checking, setChecking] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [menuAnchor, setMenuAnchor] = useState<{ x: number; y: number; width: number; height: number } | undefined>(undefined); + + useEffect(() => { + if (typeof window === 'undefined' || !(window as any).electron?.updater) { + return; + } + + const { updater } = (window as any).electron; + + updater.onUpdateAvailable((info: UpdateInfo) => { + console.log('Update available:', info.version); + setUpdateAvailable(true); + setUpdateInfo(info); + setDownloading(false); + setChecking(false); + }); + + updater.onUpdateDownloadProgress((progress: DownloadProgress) => { + setDownloadProgress(Math.round(progress.percent)); + }); + }, []); + + const handleCheckForUpdates = async () => { + setChecking(true); + try { + const result = await (window as any).electron.updater.checkForUpdates(); + console.log('Update check result:', result); + if (!result.success) { + console.warn('Update check failed:', result.error); + setChecking(false); + return; + } + setTimeout(() => { + if (!updateAvailable) { + setChecking(false); + } + }, 2000); + } catch (error) { + console.error('Failed to check for updates:', error); + setChecking(false); + } + }; + + const handleUpdateAndRestart = async () => { + setDownloading(true); + setDownloadProgress(0); + setMenuAnchor(undefined); + try { + await (window as any).electron.updater.downloadAndInstallUpdate(); + } catch (error) { + console.error('Failed to update:', error); + setDownloading(false); + } + }; + + const handleMenuToggle = (event: React.MouseEvent) => { + if (menuAnchor) { + setMenuAnchor(undefined); + } else { + const rect = event.currentTarget.getBoundingClientRect(); + setMenuAnchor({ x: rect.x, y: rect.y, width: rect.width, height: rect.height }); + } + }; + + if (typeof window === 'undefined' || !(window as any).electron?.updater) { + return null; + } + + if (!updateAvailable && !checking) { + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + +
+ ); + } + + if (checking) { + return ( + + ); + } + + if (!updateAvailable) { + return null; + } + + return ( + <> + + + + + + Update Available + {updateInfo && ( + + Version {updateInfo.version} + + )} + + + + + } + > + {null} + + + ); +} + +interface UpdateInfo { + version: string; + releaseNotes?: string; + releaseDate?: string; +} + +interface DownloadProgress { + percent: number; + transferred: number; + total: number; +} + export function UpdateNotification() { const [updateAvailable, setUpdateAvailable] = useState(false); const [updateInfo, setUpdateInfo] = useState(null);