From 130f8b86d10419784a211abd2d6318bf138cb070 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Tue, 17 Jun 2025 06:55:52 +0400 Subject: [PATCH] refactor: browser auto-update --- src-tauri/src/auto_updater.rs | 56 ++++++++++++++++ src-tauri/src/browser_runner.rs | 5 +- src-tauri/src/lib.rs | 7 ++ src-tauri/src/version_updater.rs | 7 +- src/app/page.tsx | 8 +-- src/hooks/use-version-updater.ts | 112 ++++++++++++++++++++++++++++++- 6 files changed, 187 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/auto_updater.rs b/src-tauri/src/auto_updater.rs index 16ff638..ee06d0b 100644 --- a/src-tauri/src/auto_updater.rs +++ b/src-tauri/src/auto_updater.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fs; use std::path::PathBuf; +use tauri::Emitter; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct UpdateNotification { @@ -120,6 +121,53 @@ impl AutoUpdater { Ok(notifications) } + + pub async fn check_for_updates_with_progress( + &self, + app_handle: &tauri::AppHandle, + ) { + // Check for browser updates and trigger auto-downloads + match self.check_for_updates().await { + Ok(update_notifications) => { + if !update_notifications.is_empty() { + println!( + "Found {} browser updates to auto-download", + update_notifications.len() + ); + + // Trigger automatic downloads for each update + for notification in update_notifications { + println!( + "Auto-downloading {} version {}", + notification.browser, notification.new_version + ); + + // Emit a custom event to trigger auto-download + let auto_update_event = serde_json::json!({ + "browser": notification.browser, + "new_version": notification.new_version, + "notification_id": notification.id, + "affected_profiles": notification.affected_profiles + }); + + if let Err(e) = app_handle.emit("browser-auto-update-available", &auto_update_event) { + eprintln!( + "Failed to emit auto-update event for {}: {e}", + notification.browser + ); + } else { + println!("Emitted auto-update event for {}", notification.browser); + } + } + } else { + println!("No browser updates needed"); + } + } + Err(e) => { + eprintln!("Failed to check for browser updates: {e}"); + } + } + } /// Check if a specific profile has an available update fn check_profile_update( @@ -426,6 +474,14 @@ pub async fn complete_browser_update_with_auto_update( .map_err(|e| format!("Failed to complete browser update: {e}")) } +#[tauri::command] +pub async fn check_for_updates_with_progress( + app_handle: tauri::AppHandle, +) { + let updater = AutoUpdater::new(); + updater.check_for_updates_with_progress(&app_handle).await; +} + #[cfg(test)] mod tests { use super::*; diff --git a/src-tauri/src/browser_runner.rs b/src-tauri/src/browser_runner.rs index 124192a..532234c 100644 --- a/src-tauri/src/browser_runner.rs +++ b/src-tauri/src/browser_runner.rs @@ -1913,7 +1913,10 @@ impl BrowserRunner { if let Ok(settings) = settings_manager.load_settings() { if settings.auto_delete_unused_binaries { // Perform cleanup in the background after profile deletion - let _ = self.cleanup_unused_binaries_internal(); + // Ignore errors since this is not critical for profile deletion + if let Err(e) = self.cleanup_unused_binaries_internal() { + println!("Warning: Failed to cleanup unused binaries: {e}"); + } } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5cda46a..c6dd4ad 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -297,6 +297,13 @@ pub fn run() { version_updater::VersionUpdater::run_background_task().await; }); + let app_handle_auto_updater = app.handle().clone(); + + // Start the auto-update check task separately + tauri::async_runtime::spawn(async move { + auto_updater::check_for_updates_with_progress(app_handle_auto_updater).await; + }); + let app_handle_update = app.handle().clone(); tauri::async_runtime::spawn(async move { println!("Starting app update check at startup..."); diff --git a/src-tauri/src/version_updater.rs b/src-tauri/src/version_updater.rs index 8402c27..d2a04b6 100644 --- a/src-tauri/src/version_updater.rs +++ b/src-tauri/src/version_updater.rs @@ -9,6 +9,7 @@ use tauri::Emitter; use tokio::sync::Mutex; use tokio::time::interval; +use crate::auto_updater::AutoUpdater; use crate::browser_version_service::BrowserVersionService; #[derive(Debug, Serialize, Deserialize, Clone)] @@ -47,6 +48,7 @@ impl Default for BackgroundUpdateState { pub struct VersionUpdater { version_service: BrowserVersionService, + auto_updater: AutoUpdater, app_handle: Option, } @@ -54,6 +56,7 @@ impl VersionUpdater { pub fn new() -> Self { Self { version_service: BrowserVersionService::new(), + auto_updater: AutoUpdater::new(), app_handle: None, } } @@ -379,9 +382,11 @@ impl VersionUpdater { } // Small delay between browsers to avoid overwhelming APIs - tokio::time::sleep(Duration::from_millis(500)).await; + tokio::time::sleep(Duration::from_millis(200)).await; } + self.auto_updater.check_for_updates_with_progress(app_handle).await; + // Emit completion event let progress = VersionUpdateProgress { current_browser: "".to_string(), diff --git a/src/app/page.tsx b/src/app/page.tsx index c6e2c21..c1bd0a6 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -82,13 +82,13 @@ export default function Home() { } }, []); - // Auto-update functionality - pass loadProfiles to refresh profiles after updates + // Version updater for handling version fetching progress events and auto-updates + useVersionUpdater(); + + // Auto-update functionality - use the existing hook for compatibility const updateNotifications = useUpdateNotifications(loadProfiles); const { checkForUpdates, isUpdating } = updateNotifications; - // Version updater for handling version fetching progress events - useVersionUpdater(); - // Profiles loader with update check (for initial load and manual refresh) const loadProfilesWithUpdateCheck = useCallback(async () => { try { diff --git a/src/hooks/use-version-updater.ts b/src/hooks/use-version-updater.ts index 2b0b5aa..706dd70 100644 --- a/src/hooks/use-version-updater.ts +++ b/src/hooks/use-version-updater.ts @@ -1,6 +1,7 @@ import { getBrowserDisplayName } from "@/lib/browser-utils"; import { dismissToast, + showAutoUpdateToast, showErrorToast, showSuccessToast, showUnifiedVersionUpdateToast, @@ -32,6 +33,13 @@ interface BrowserVersionsResult { total_versions_count: number; } +interface AutoUpdateEvent { + browser: string; + new_version: string; + notification_id: string; + affected_profiles: string[]; +} + export function useVersionUpdater() { const [isUpdating, setIsUpdating] = useState(false); const [lastUpdateTime, setLastUpdateTime] = useState(null); @@ -86,7 +94,8 @@ export function useVersionUpdater() { if (progress.new_versions_found > 0) { showSuccessToast("Browser versions updated successfully", { duration: 5000, - description: "Updates will start automatically.", + description: + "Auto-downloads will start shortly for available updates.", }); } else { showSuccessToast("No new browser versions found", { @@ -117,6 +126,105 @@ export function useVersionUpdater() { }; }, [loadUpdateStatus]); + // Listen for browser auto-update events + useEffect(() => { + const unlisten = listen( + "browser-auto-update-available", + (event) => { + const handleAutoUpdate = async () => { + const { browser, new_version, notification_id } = event.payload; + console.log("Browser auto-update event received:", event.payload); + + const browserDisplayName = getBrowserDisplayName(browser); + + try { + // Show auto-update start notification + showAutoUpdateToast(browserDisplayName, new_version, { + description: `Downloading ${browserDisplayName} ${new_version} automatically. Progress will be shown below.`, + }); + + // Dismiss the update notification in the backend + await invoke("dismiss_update_notification", { + notificationId: notification_id, + }); + + // Check if browser already exists before downloading + const isDownloaded = await invoke("check_browser_exists", { + browserStr: browser, + version: new_version, + }); + + if (isDownloaded) { + // Browser already exists, skip download and go straight to profile update + console.log( + `${browserDisplayName} ${new_version} already exists, skipping download`, + ); + + showSuccessToast( + `${browserDisplayName} ${new_version} already available`, + { + description: "Updating profile configurations...", + duration: 3000, + }, + ); + } else { + // Download the browser - this will trigger download progress events automatically + await invoke("download_browser", { + browserStr: browser, + version: new_version, + }); + } + + // Complete the update with auto-update of profile versions + const updatedProfiles = await invoke( + "complete_browser_update_with_auto_update", + { + browser, + newVersion: new_version, + }, + ); + + // Show success message based on whether profiles were updated + if (updatedProfiles.length > 0) { + const profileText = + updatedProfiles.length === 1 + ? `Profile "${updatedProfiles[0]}" has been updated` + : `${updatedProfiles.length} profiles have been updated`; + + showSuccessToast(`${browserDisplayName} update completed`, { + description: `${profileText} to version ${new_version}. You can now launch your browsers with the latest version.`, + duration: 6000, + }); + } else { + showSuccessToast(`${browserDisplayName} update completed`, { + description: `Version ${new_version} is now available. Running profiles will use the new version when restarted.`, + duration: 6000, + }); + } + } catch (error) { + console.error("Failed to handle browser auto-update:", error); + showErrorToast(`Failed to auto-update ${browserDisplayName}`, { + description: + error instanceof Error + ? error.message + : "Unknown error occurred", + duration: 8000, + }); + } + }; + + // Call the async handler + void handleAutoUpdate(); + }, + ); + + return () => { + void unlisten.then((fn) => { + fn(); + }); + }; + }, []); + // Load update status on mount and periodically useEffect(() => { void loadUpdateStatus(); @@ -156,7 +264,7 @@ export function useVersionUpdater() { }); } else if (totalNewVersions > 0) { showSuccessToast("Browser versions updated successfully", { - description: `Found ${totalNewVersions} new versions across ${successfulUpdates} browsers. Updates will start automatically.`, + description: `Found ${totalNewVersions} new versions across ${successfulUpdates} browsers. Auto-downloads will start shortly.`, duration: 4000, }); } else {