From a1e581632df11c9c4bf1007909e196edfeafc12a Mon Sep 17 00:00:00 2001 From: Shreyas Date: Fri, 28 Feb 2025 14:59:01 +0530 Subject: [PATCH] fix(desktop): change updater endpoint and some UX improvements (#4794) fix: change updater endpoint and better UX --- .../hoppscotch-desktop/src-tauri/src/lib.rs | 10 +-- .../src-tauri/src/updater.rs | 46 ++++++++--- .../src-tauri/tauri.conf.json | 2 +- packages/hoppscotch-desktop/src/App.vue | 6 -- .../hoppscotch-desktop/src/components.d.ts | 2 +- .../hoppscotch-desktop/src/views/Home.vue | 76 +++++++++++++------ 6 files changed, 92 insertions(+), 50 deletions(-) diff --git a/packages/hoppscotch-desktop/src-tauri/src/lib.rs b/packages/hoppscotch-desktop/src-tauri/src/lib.rs index e0e861bf..3492790d 100644 --- a/packages/hoppscotch-desktop/src-tauri/src/lib.rs +++ b/packages/hoppscotch-desktop/src-tauri/src/lib.rs @@ -29,13 +29,6 @@ pub fn run() { .plugin(tauri_plugin_updater::Builder::new().build())?; Ok(()) }) - .setup(|app| { - let handle = app.handle().clone(); - tauri::async_runtime::spawn(async move { - updater::check_and_install_updates(&handle).await; - }); - Ok(()) - }) .setup(|app| { let handle = app.handle().clone(); tracing::info!(app_name = %app.package_info().name, "Configuring deep link handler"); @@ -78,7 +71,8 @@ pub fn run() { .plugin(tauri_plugin_relay::init()) .invoke_handler(tauri::generate_handler![ hopp_auth_port, - updater::check_updates_and_wait + updater::check_updates_available, + updater::install_updates_and_restart ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/packages/hoppscotch-desktop/src-tauri/src/updater.rs b/packages/hoppscotch-desktop/src-tauri/src/updater.rs index a13a2c33..c6d16d6c 100644 --- a/packages/hoppscotch-desktop/src-tauri/src/updater.rs +++ b/packages/hoppscotch-desktop/src-tauri/src/updater.rs @@ -2,19 +2,40 @@ use tauri_plugin_dialog::DialogExt; use tauri_plugin_updater::UpdaterExt; #[tauri::command] -pub async fn check_updates_and_wait(app: tauri::AppHandle) -> Result { - check_and_install_updates(&app).await; - Ok("Update check completed".to_string()) -} - -pub async fn check_and_install_updates(app: &tauri::AppHandle) { +pub async fn check_updates_available(app: tauri::AppHandle) -> Result { tracing::info!("Checking for updates..."); - let updater = match app.updater() { Ok(updater) => updater, Err(e) => { tracing::error!(error = %e, "Failed to initialize updater"); - return; + return Ok(false); + } + }; + + match updater.check().await { + Ok(Some(_update)) => { + tracing::info!("Update available"); + Ok(true) + } + Ok(None) => { + tracing::info!("No updates available"); + Ok(false) + } + Err(e) => { + tracing::error!(error = %e, "Failed to check for updates"); + Err(format!("Failed to check for updates: {}", e)) + } + } +} + +#[tauri::command] +pub async fn install_updates_and_restart(app: tauri::AppHandle) -> Result<(), String> { + tracing::info!("Installing updates..."); + let updater = match app.updater() { + Ok(updater) => updater, + Err(e) => { + tracing::error!(error = %e, "Failed to initialize updater"); + return Err(format!("Failed to initialize updater: {}", e)); } }; @@ -23,7 +44,7 @@ pub async fn check_and_install_updates(app: &tauri::AppHandle) { tracing::info!( current_version = app.package_info().version.to_string(), update_version = update.version.to_string(), - "Update available" + "Installing update" ); let dialog = app.dialog(); @@ -40,32 +61,35 @@ pub async fn check_and_install_updates(app: &tauri::AppHandle) { if should_update { tracing::info!("User agreed to update, starting download..."); - match update.download_and_install(|_, _| {}, || {}).await { Ok(_) => { tracing::info!("Update installed successfully, restarting app"); app.restart(); + Err("Unreachable - app should have restarted".to_string()) } Err(e) => { tracing::error!(error = %e, "Failed to download or install update"); - let _ = app .dialog() .message(format!("Failed to install update: {}", e)) .title("Update Error") .kind(tauri_plugin_dialog::MessageDialogKind::Error) .blocking_show(); + Err(format!("Failed to download or install update: {}", e)) } } } else { tracing::info!("User declined the update"); + Ok(()) } } Ok(None) => { tracing::info!("No updates available"); + Ok(()) } Err(e) => { tracing::error!(error = %e, "Failed to check for updates"); + Err(format!("Failed to check for updates: {}", e)) } } } diff --git a/packages/hoppscotch-desktop/src-tauri/tauri.conf.json b/packages/hoppscotch-desktop/src-tauri/tauri.conf.json index 11c77d43..e7507987 100644 --- a/packages/hoppscotch-desktop/src-tauri/tauri.conf.json +++ b/packages/hoppscotch-desktop/src-tauri/tauri.conf.json @@ -56,7 +56,7 @@ "plugins": { "updater": { "active": true, - "endpoints": ["https://releases.hoppscotch.com/hoppscotch-desktop.json"], + "endpoints": ["https://releases.hoppscotch.com/hoppscotch-selfhost-desktop.json"], "dialog": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDYwOURFNEY4RDRGMDQxNTgKUldSWVFmRFUrT1NkWUc1RDM0Z2ZjTHE2dG52Q3ZlYzg3ZXVpZU9KaENPWTBMd3MwY0hWa1lreDcK" } diff --git a/packages/hoppscotch-desktop/src/App.vue b/packages/hoppscotch-desktop/src/App.vue index 7f5f247d..5d98afa7 100644 --- a/packages/hoppscotch-desktop/src/App.vue +++ b/packages/hoppscotch-desktop/src/App.vue @@ -1,6 +1,5 @@ diff --git a/packages/hoppscotch-desktop/src/components.d.ts b/packages/hoppscotch-desktop/src/components.d.ts index 55fdda69..cf602dd2 100644 --- a/packages/hoppscotch-desktop/src/components.d.ts +++ b/packages/hoppscotch-desktop/src/components.d.ts @@ -8,7 +8,7 @@ export {} declare module 'vue' { export interface GlobalComponents { HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary'] - HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem'] + HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] LayoutHeader: typeof import('./components/layout/LayoutHeader.vue')['default'] LayoutSidebar: typeof import('./components/layout/LayoutSidebar.vue')['default'] Tippy: typeof import('vue-tippy')['Tippy'] diff --git a/packages/hoppscotch-desktop/src/views/Home.vue b/packages/hoppscotch-desktop/src/views/Home.vue index fedbf060..14bd721c 100644 --- a/packages/hoppscotch-desktop/src/views/Home.vue +++ b/packages/hoppscotch-desktop/src/views/Home.vue @@ -9,8 +9,7 @@
-
-

Loading Hoppscotch...

+
@@ -153,13 +152,17 @@ const migrateFromLocalStorage = async () => { console.log(`Migration complete. Migrated ${migratedCount} items.`); }; +interface UpdateCheckResult { + status: "completed" | "timeout"; + hasUpdates?: boolean; +} + const loadVendored = async () => { isLoading.value = true; error.value = ""; try { console.log("Initializing home_store and starting migration process"); - await home_store.init(); await app_store.init(); @@ -173,36 +176,63 @@ const loadVendored = async () => { console.error("Migration error:", migrationError); } + let shouldProceedWithLoad = true; + try { console.log("Checking for updates before loading app..."); - await invoke('check_updates_and_wait'); - console.log("Update check completed"); + + const timeoutPromise: Promise = new Promise((resolve) => { + setTimeout(() => { + console.log("Update check timeout reached, proceeding with app load"); + resolve({ status: "timeout" }); + }, 2000); // TODO: 2s shoud be good? + }); + + const result = await Promise.race([ + invoke('check_updates_available').then(hasUpdates => ({ status: "completed", hasUpdates })), + timeoutPromise + ]); + + console.log("Update check result:", result); + + if (result.status === "completed" && result.hasUpdates) { + console.log("Updates available, handling before loading app"); + shouldProceedWithLoad = false; + + await invoke('install_updates_and_restart'); + // This point would only be reached if install_updates_and_restart + // doesn't actually restart the app + return; + } } catch (updateError) { console.error("Update check error:", updateError); + // Continue with loading the app despite update check errors } - const vendoredInstance: VendoredInstance = { - type: "vendored", - displayName: "Vendored", - version: "vendored" - }; + if (shouldProceedWithLoad) { + const vendoredInstance: VendoredInstance = { + type: "vendored", + displayName: "Vendored", + version: "vendored" + }; - await saveConnectionState({ - status: "connected", - instance: vendoredInstance - }); + await saveConnectionState({ + status: "connected", + instance: vendoredInstance + }); - console.log("Loading vendored app..."); - const loadResp = await load({ - bundleName: "Hoppscotch", - window: { title: "Hoppscotch" } - }); + console.log("Loading vendored app..."); + const loadResp = await load({ + bundleName: "Hoppscotch", + window: { title: "Hoppscotch" } + }); - if (!loadResp.success) { - throw new Error("Failed to load Hoppscotch Vendored"); + if (!loadResp.success) { + throw new Error("Failed to load Hoppscotch Vendored"); + } + + console.log("Vendored app loaded successfully"); } - - console.log("Vendored app loaded successfully"); } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); console.error("Error loading vendored app:", errorMessage);