Skip to content

Commit 1dd34e2

Browse files
committed
fix: refactor app updater to handle background downloads and prevent concurrent installation attempts
1 parent 1619d3d commit 1dd34e2

3 files changed

Lines changed: 187 additions & 144 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1818
- Fixed the issue on destination paths not load correctly on remote config modal.
1919
- UI not load the ordered remote list Fixed.
2020
- Fixed the issue on when tray menu updated in main thread, UI thread was freezed for a long time. Not anymore.
21+
- Some update issues fixed.
2122

2223
## [v0.2.5] - 2026-05-16
2324

src-tauri/src/utils/app/updater.rs

Lines changed: 122 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[cfg(desktop)]
22
pub mod app_updates {
3-
use crate::core::lifecycle::shutdown::shutdown_app;
3+
use crate::core::lifecycle::shutdown::handle_shutdown;
44
use crate::utils::app::platform::relaunch_app;
55
use crate::utils::types::updater::{
66
AppUpdaterState, DownloadState, DownloadStatus, Result, UpdateInfo, UpdateMetadata,
@@ -99,9 +99,9 @@ pub mod app_updates {
9999
.on_before_exit(move || {
100100
let app = app_exit.clone();
101101
warn!("Shutting down for update installation...");
102-
tauri::async_runtime::spawn(async move {
102+
tauri::async_runtime::block_on(async move {
103103
app.state::<RcloneState>().set_shutting_down();
104-
let _ = shutdown_app(app).await;
104+
handle_shutdown(app).await;
105105
});
106106
})
107107
.build()?
@@ -237,12 +237,13 @@ pub mod app_updates {
237237
pub async fn install_update(app: AppHandle) -> Result<()> {
238238
let updater_state = app.state::<AppUpdaterState>();
239239

240-
let update = updater_state
241-
.data
242-
.lock()
243-
.pending_action
244-
.take()
245-
.ok_or(Error::NoPendingUpdate)?;
240+
let update = {
241+
let mut data = updater_state.data.lock();
242+
if data.state == UpdateState::Downloading {
243+
return Ok(());
244+
}
245+
data.pending_action.take().ok_or(Error::NoPendingUpdate)?
246+
};
246247

247248
// Reset progress and set updating flag
248249
{
@@ -262,117 +263,121 @@ pub mod app_updates {
262263
}),
263264
);
264265

265-
let download_app = app.clone();
266-
let res: std::result::Result<Vec<u8>, tauri_plugin_updater::Error> = update
267-
.download(
268-
{
269-
let app = download_app.clone();
270-
let mut last_emit = std::time::Instant::now();
271-
move |chunk_length, content_length| {
272-
let st = app.state::<AppUpdaterState>();
273-
let (downloaded, total) = {
274-
let mut data = st.data.lock();
275-
data.downloaded_bytes += chunk_length as u64;
276-
if let Some(total) = content_length {
277-
data.total_bytes = total;
266+
let app_clone = app.clone();
267+
let update_clone = update.clone();
268+
269+
tauri::async_runtime::spawn(async move {
270+
let res = update
271+
.download(
272+
{
273+
let app = app_clone.clone();
274+
let mut last_emit = std::time::Instant::now();
275+
move |chunk_length, content_length| {
276+
let st = app.state::<AppUpdaterState>();
277+
let (downloaded, total) = {
278+
let mut data = st.data.lock();
279+
data.downloaded_bytes += chunk_length as u64;
280+
if let Some(total) = content_length {
281+
data.total_bytes = total;
282+
}
283+
(data.downloaded_bytes, data.total_bytes)
284+
};
285+
286+
let percentage = if total > 0 {
287+
(downloaded as f64 / total as f64) * 100.0
288+
} else {
289+
0.0
290+
};
291+
292+
let now = std::time::Instant::now();
293+
if now.duration_since(last_emit).as_millis() >= 200 {
294+
let _ = app.emit(
295+
crate::utils::types::events::APP_EVENT,
296+
serde_json::json!({
297+
"status": "download_progress",
298+
"data": DownloadStatus {
299+
downloaded_bytes: downloaded,
300+
total_bytes: total,
301+
percentage,
302+
state: DownloadState::InProgress,
303+
}
304+
}),
305+
);
306+
last_emit = now;
278307
}
279-
(data.downloaded_bytes, data.total_bytes)
280-
};
281-
282-
let percentage = if total > 0 {
283-
(downloaded as f64 / total as f64) * 100.0
284-
} else {
285-
0.0
286-
};
287-
288-
let now = std::time::Instant::now();
289-
if now.duration_since(last_emit).as_millis() >= 200 {
290-
let _ = app.emit(
291-
crate::utils::types::events::APP_EVENT,
292-
serde_json::json!({
293-
"status": "download_progress",
294-
"data": DownloadStatus {
295-
downloaded_bytes: downloaded,
296-
total_bytes: total,
297-
percentage,
298-
state: DownloadState::InProgress,
299-
}
300-
}),
301-
);
302-
last_emit = now;
303-
}
304-
}
305-
},
306-
|| {
307-
info!("App update download finished successfully");
308-
},
309-
)
310-
.await;
311-
312-
match res {
313-
Ok(signature) => {
314-
let (downloaded, total) = {
315-
let mut data = updater_state.data.lock();
316-
data.state = UpdateState::ReadyToRestart;
317-
data.signature = Some(signature);
318-
data.pending_action = Some(update.clone());
319-
(data.downloaded_bytes, data.total_bytes)
320-
};
321-
322-
notify(
323-
&app,
324-
NotificationEvent::AppUpdate(UpdateStage::Downloaded {
325-
version: update.version.clone(),
326-
}),
327-
);
328-
329-
let _ = app.emit(
330-
crate::utils::types::events::APP_EVENT,
331-
serde_json::json!({
332-
"status": "download_progress",
333-
"data": DownloadStatus {
334-
downloaded_bytes: downloaded,
335-
total_bytes: total,
336-
percentage: 100.0,
337-
state: DownloadState::Complete,
338308
}
339-
}),
340-
);
341-
Ok(())
309+
},
310+
|| {
311+
info!("App update download finished successfully");
312+
},
313+
)
314+
.await;
315+
316+
let st = app_clone.state::<AppUpdaterState>();
317+
match res {
318+
Ok(signature) => {
319+
let (downloaded, total) = {
320+
let mut data = st.data.lock();
321+
data.state = UpdateState::ReadyToRestart;
322+
data.signature = Some(signature);
323+
data.pending_action = Some(update_clone.clone());
324+
(data.downloaded_bytes, data.total_bytes)
325+
};
326+
327+
notify(
328+
&app_clone,
329+
NotificationEvent::AppUpdate(UpdateStage::Downloaded {
330+
version: update_clone.version.clone(),
331+
}),
332+
);
333+
334+
let _ = app_clone.emit(
335+
crate::utils::types::events::APP_EVENT,
336+
serde_json::json!({
337+
"status": "download_progress",
338+
"data": DownloadStatus {
339+
downloaded_bytes: downloaded,
340+
total_bytes: total,
341+
percentage: 100.0,
342+
state: DownloadState::Complete,
343+
}
344+
}),
345+
);
346+
}
347+
Err(e) => {
348+
warn!("App update download failed: {e}");
349+
let (downloaded, total) = {
350+
let mut data = st.data.lock();
351+
data.state = UpdateState::Available;
352+
data.failure_message = Some(e.to_string());
353+
data.pending_action = Some(update_clone.clone());
354+
(data.downloaded_bytes, data.total_bytes)
355+
};
356+
357+
notify(
358+
&app_clone,
359+
NotificationEvent::AppUpdate(UpdateStage::Failed {
360+
error: e.to_string(),
361+
}),
362+
);
363+
364+
let _ = app_clone.emit(
365+
crate::utils::types::events::APP_EVENT,
366+
serde_json::json!({
367+
"status": "download_progress",
368+
"data": DownloadStatus {
369+
downloaded_bytes: downloaded,
370+
total_bytes: total,
371+
percentage: 0.0,
372+
state: DownloadState::Failed(e.to_string()),
373+
}
374+
}),
375+
);
376+
}
342377
}
343-
Err(e) => {
344-
warn!("App update download failed: {e}");
345-
let (downloaded, total) = {
346-
let mut data = updater_state.data.lock();
347-
data.state = UpdateState::Available;
348-
data.failure_message = Some(e.to_string());
349-
data.pending_action = Some(update.clone());
350-
(data.downloaded_bytes, data.total_bytes)
351-
};
352-
353-
notify(
354-
&app,
355-
NotificationEvent::AppUpdate(UpdateStage::Failed {
356-
error: e.to_string(),
357-
}),
358-
);
359-
360-
let _ = app.emit(
361-
crate::utils::types::events::APP_EVENT,
362-
serde_json::json!({
363-
"status": "download_progress",
364-
"data": DownloadStatus {
365-
downloaded_bytes: downloaded,
366-
total_bytes: total,
367-
percentage: 0.0,
368-
state: DownloadState::Failed(e.to_string()),
369-
}
370-
}),
371-
);
378+
});
372379

373-
Err(Error::Tauri(e))
374-
}
375-
}
380+
Ok(())
376381
}
377382

378383
#[tauri::command]
@@ -382,10 +387,7 @@ pub mod app_updates {
382387
let (update, signature) = {
383388
let mut data = updater_state.data.lock();
384389
match (data.pending_action.take(), data.signature.take()) {
385-
(Some(u), Some(s)) => {
386-
data.state = UpdateState::Downloading;
387-
Some((u, s))
388-
}
390+
(Some(u), Some(s)) => Some((u, s)),
389391
_ => None,
390392
}
391393
}

0 commit comments

Comments
 (0)