From 1a8e26a8470fddbf4ab7260f6517b49e025ef4ae Mon Sep 17 00:00:00 2001 From: Nexmoe <16796652+nexmoe@users.noreply.github.com> Date: Sun, 2 Nov 2025 19:35:49 +0800 Subject: [PATCH] feat(update): add download action and guide users to download page when (#12) * feat(update): add download action and guide users to download page when * fix(updater): always initialize auto-updater in non-prod builds for testing --- src/main/index.ts | 27 ++++++++++------ src/renderer/src/App.tsx | 43 +++++++++---------------- src/renderer/src/locales/en.json | 3 ++ src/renderer/src/pages/About.tsx | 55 +++++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 39 deletions(-) diff --git a/src/main/index.ts b/src/main/index.ts index 3010655..81b9104 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -107,11 +107,6 @@ function setupDownloadEvents(): void { } function initAutoUpdater(): void { - if (process.env.NODE_ENV !== 'production') { - log.info('Skipping auto-updater initialization in development mode') - return - } - try { log.info('Initializing auto-updater...') @@ -123,6 +118,12 @@ function initAutoUpdater(): void { autoUpdater.on('update-available', (info) => { log.info('Update available:', info.version) mainWindow?.webContents.send('update:available', info) + + // If auto-update is enabled, the update will be downloaded automatically + // because autoDownload is set to true + if (settingsManager.get('autoUpdate')) { + log.info('Auto-update is enabled, update will be downloaded automatically') + } }) autoUpdater.on('update-not-available', (info) => { @@ -153,12 +154,18 @@ function initAutoUpdater(): void { } }) - if (settingsManager.get('autoUpdate')) { - log.info('Auto-update is enabled, checking for updates...') - void autoUpdater.checkForUpdatesAndNotify() - } - log.info('Auto-updater initialized successfully') + + // Check for updates immediately if auto-update is enabled + const autoUpdateEnabled = settingsManager.get('autoUpdate') + if (autoUpdateEnabled) { + log.info('Auto-update is enabled, checking for updates immediately...') + // Use checkForUpdates instead of checkForUpdatesAndNotify + // because we have our own notification system and want to ensure immediate download + void autoUpdater.checkForUpdates() + } else { + log.info('Auto-update is disabled, skipping automatic update check') + } } catch (error) { log.error('Failed to initialize auto-updater:', error) } diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 93d66cb..c0aba93 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -61,43 +61,30 @@ function AppContent() { } } - const startUpdateDownload = async () => { - if (updateDownloadInProgressRef.current) { - return - } - - updateDownloadInProgressRef.current = true - toast.info(t('about.notifications.downloadStarted')) - - try { - const result = await ipcServices.update.downloadUpdate() - if (!result.success) { - throw new Error(result.error ?? 'Unknown error') - } - } catch (error) { - updateDownloadInProgressRef.current = false - const message = - error instanceof Error && error.message - ? error.message - : t('about.notifications.unknownErrorFallback') - toast.error(t('about.notifications.updateError', { error: message })) + const handleGoToDownloadPage = () => { + if (typeof window !== 'undefined') { + window.open('https://vidbee.org/download/', '_blank', 'noopener,noreferrer') } } const handleUpdateAvailable = (rawInfo: unknown) => { const info = (rawInfo ?? {}) as { version?: string } const versionLabel = info.version ?? '' - toast.success(t('about.notifications.updateAvailable', { version: versionLabel })) if (autoUpdateEnabled) { - void startUpdateDownload() - } else { - toast(t('about.notifications.downloadUpdate', { version: versionLabel }), { + // Update will be downloaded automatically because autoDownload is enabled in main process + toast.success(t('about.notifications.updateAvailable', { version: versionLabel }), { action: { - label: t('about.notifications.manualDownloadAction'), - onClick: () => { - void startUpdateDownload() - } + label: t('about.actions.goToDownload'), + onClick: handleGoToDownloadPage + } + }) + // No need to manually call downloadUpdate() because autoDownload is true + } else { + toast.success(t('about.notifications.updateAvailable', { version: versionLabel }), { + action: { + label: t('about.actions.goToDownload'), + onClick: handleGoToDownloadPage } }) } diff --git a/src/renderer/src/locales/en.json b/src/renderer/src/locales/en.json index 53987a1..b811731 100644 --- a/src/renderer/src/locales/en.json +++ b/src/renderer/src/locales/en.json @@ -2,8 +2,10 @@ "about": { "actions": { "checkUpdates": "Check updates", + "download": "Download", "email": "Email", "feedback": "Feedback", + "goToDownload": "Go to download page", "openRepo": "Open GitHub repository", "view": "View", "visit": "Visit" @@ -32,6 +34,7 @@ "restartToUpdate": "Restart now to install update?", "restartNowAction": "Restart now", "updateAvailable": "Update available: {{version}}", + "updateAvailableMessage": "A new version {{version}} is available. Please download it from the official website.", "updateDownloaded": "Update downloaded, restart to install", "updateDownloadedVersion": "Update {{version}} downloaded, restart to install", "updateError": "Failed to check for updates: {{error}}", diff --git a/src/renderer/src/pages/About.tsx b/src/renderer/src/pages/About.tsx index ae93303..3ec15eb 100644 --- a/src/renderer/src/pages/About.tsx +++ b/src/renderer/src/pages/About.tsx @@ -11,6 +11,7 @@ import { Switch } from '@renderer/components/ui/switch' import { useAtom, useSetAtom } from 'jotai' import type { LucideIcon } from 'lucide-react' import { + Download, Facebook, FileText, Github, @@ -77,6 +78,47 @@ export function About() { ) => { await saveSetting({ key, value }) toast.success(t('notifications.settingsSaved')) + + // If auto-update is enabled, check for updates immediately + if (key === 'autoUpdate' && value === true) { + try { + toast.info(t('about.notifications.checkingUpdates')) + const result = await ipcServices.update.checkForUpdates() + + if (result.available) { + // The update will be downloaded automatically because autoDownload is enabled + toast.success(t('about.notifications.updateAvailable', { version: result.version }), { + action: { + label: t('about.actions.goToDownload'), + onClick: handleGoToDownload + } + }) + setLatestVersionState({ + status: 'available', + version: result.version ?? '' + }) + } else if (result.error) { + toast.error(t('about.notifications.updateError', { error: result.error })) + setLatestVersionState({ + status: 'error', + error: result.error + }) + } else { + toast.success(t('about.notifications.noUpdatesAvailable')) + setLatestVersionState({ + status: 'uptodate', + version: result.version ?? appVersion + }) + } + } catch (error) { + console.error('Failed to check for updates:', error) + toast.error(t('about.notifications.updateError', { error: 'Unknown error' })) + } + } + } + + const handleGoToDownload = () => { + openShareUrl('https://vidbee.org/download/') } const handleCheckForUpdates = async () => { @@ -85,7 +127,12 @@ export function About() { const result = await ipcServices.update.checkForUpdates() if (result.available) { - toast.success(t('about.notifications.updateAvailable', { version: result.version })) + toast.success(t('about.notifications.updateAvailable', { version: result.version }), { + action: { + label: t('about.actions.goToDownload'), + onClick: handleGoToDownload + } + }) setLatestVersionState({ status: 'available', version: result.version ?? '' @@ -247,6 +294,12 @@ export function About() { + {latestVersionState?.status === 'available' ? ( + + ) : null}