UI & error fixes & QoL (#116)

* fix: use Arc<Error> instead of just ErrorKind

* fix: game status updates for UI

* fix: missing game version on push_game_update calls

* feat: wait if library load takes <300ms

* fix: clippy
This commit is contained in:
DecDuck
2025-08-15 22:56:49 +10:00
committed by GitHub
parent cb55ac2bf5
commit 17c375bcab
7 changed files with 90 additions and 107 deletions

View File

@ -124,7 +124,12 @@ impl DownloadManagerBuilder {
self.current_download_agent = None;
let mut download_thread_lock = self.current_download_thread.lock().unwrap();
*download_thread_lock = None;
if let Some(unfinished_thread) = download_thread_lock.take()
&& !unfinished_thread.is_finished()
{
unfinished_thread.join().unwrap();
}
drop(download_thread_lock);
}
@ -215,10 +220,6 @@ impl DownloadManagerBuilder {
&& self.download_queue.read().front().unwrap()
== &self.current_download_agent.as_ref().unwrap().metadata()
{
debug!(
"Current download agent: {:?}",
self.current_download_agent.as_ref().unwrap().metadata()
);
return;
}
@ -325,8 +326,8 @@ impl DownloadManagerBuilder {
self.stop_and_wait_current_download();
self.remove_and_cleanup_front_download(&current_agent.metadata());
self.push_ui_queue_update();
}
self.push_ui_queue_update();
self.set_status(DownloadManagerStatus::Error);
}
fn manage_cancel_signal(&mut self, meta: &DownloadableMetadata) {

View File

@ -1,6 +1,6 @@
use std::{
fmt::{Display, Formatter},
io,
io, sync::Arc,
};
use serde_with::SerializeDisplay;
@ -14,9 +14,10 @@ pub enum ApplicationDownloadError {
NotInitialized,
Communication(RemoteAccessError),
DiskFull(u64, u64),
#[allow(dead_code)]
Checksum,
Lock,
IoError(io::ErrorKind),
IoError(Arc<io::Error>),
DownloadError,
}

View File

@ -484,19 +484,16 @@ impl GameDownloadAgent {
self.control_flag.set(DownloadThreadControlFlag::Go);
let status = ApplicationTransientStatus::Validating {
version_name: self.version.clone(),
};
let mut db_lock = borrow_db_mut_checked();
db_lock.applications.transient_statuses.insert(
self.metadata(),
ApplicationTransientStatus::Validating {
version_name: self.version.clone(),
},
);
push_game_update(
app_handle,
&self.metadata().id,
None,
GameStatusManager::fetch_state(&self.metadata().id, &db_lock),
);
db_lock
.applications
.transient_statuses
.insert(self.metadata(), status.clone());
push_game_update(app_handle, &self.metadata().id, None, (None, Some(status)));
}
pub fn validate(&self, app_handle: &AppHandle) -> Result<bool, ApplicationDownloadError> {

View File

@ -17,6 +17,7 @@ use std::fs::{Permissions, set_permissions};
use std::io::Read;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::sync::Arc;
use std::{
fs::{File, OpenOptions},
io::{self, BufWriter, Seek, SeekFrom, Write},
@ -191,12 +192,13 @@ pub fn download_game_bucket(
.to_str()
.unwrap();
for (i, raw_length) in lengths.split(",").enumerate() {
let length = raw_length.parse::<usize>().unwrap_or(0);
let Some(drop) = bucket.drops.get(i) else {
warn!("invalid number of Content-Lengths recieved: {}, {}", i, lengths);
warn!(
"invalid number of Content-Lengths recieved: {}, {}",
i, lengths
);
return Err(ApplicationDownloadError::DownloadError);
};
if drop.length != length {
@ -210,11 +212,11 @@ pub fn download_game_bucket(
let mut pipeline =
DropDownloadPipeline::new(response, bucket.drops.clone(), control_flag, progress)
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
let completed = pipeline
.copy()
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
if !completed {
return Ok(false);
}
@ -225,18 +227,20 @@ pub fn download_game_bucket(
for drop in bucket.drops.iter() {
let permissions = Permissions::from_mode(drop.permissions);
set_permissions(drop.path.clone(), permissions)
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
}
}
let checksums = pipeline
.finish()
.map_err(|e| ApplicationDownloadError::IoError(e.kind()))?;
.map_err(|e| ApplicationDownloadError::IoError(Arc::new(e)))?;
for (index, drop) in bucket.drops.iter().enumerate() {
let res = hex::encode(**checksums.get(index).unwrap());
if res != drop.checksum {
return Err(ApplicationDownloadError::Checksum);
warn!("context didn't match... doing nothing because we will validate later.");
// return Ok(false);
// return Err(ApplicationDownloadError::Checksum);
}
}

View File

@ -14,6 +14,7 @@ use crate::database::models::data::{
ApplicationTransientStatus, DownloadableMetadata, GameDownloadStatus, GameVersion,
};
use crate::download_manager::download_manager_frontend::DownloadStatus;
use crate::error::drop_server_error::DropServerError;
use crate::error::library_error::LibraryError;
use crate::error::remote_access_error::RemoteAccessError;
use crate::games::state::{GameStatusManager, GameStatusWithTransient};
@ -89,7 +90,10 @@ pub async fn fetch_library_logic(
.await?;
if response.status() != 200 {
let err = response.json().await.unwrap();
let err = response.json().await.unwrap_or(DropServerError {
status_code: 500,
status_message: "Invalid response from server.".to_owned(),
});
warn!("{err:?}");
return Err(RemoteAccessError::InvalidResponse(err));
}
@ -358,8 +362,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.transient_statuses
.entry(meta.clone())
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
.insert(meta.clone(), ApplicationTransientStatus::Uninstalling {});
push_game_update(
app_handle,
@ -393,8 +396,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.transient_statuses
.entry(meta.clone())
.and_modify(|v| *v = ApplicationTransientStatus::Uninstalling {});
.insert(meta.clone(), ApplicationTransientStatus::Uninstalling {});
drop(db_handle);
@ -412,8 +414,7 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
db_handle
.applications
.game_statuses
.entry(meta.id.clone())
.and_modify(|e| *e = GameDownloadStatus::Remote {});
.insert(meta.id.clone(), GameDownloadStatus::Remote {});
let _ = db_handle.applications.transient_statuses.remove(&meta);
push_game_update(
@ -425,8 +426,6 @@ pub fn uninstall_game_logic(meta: DownloadableMetadata, app_handle: &AppHandle)
debug!("uninstalled game id {}", &meta.id);
app_handle.emit("update_library", ()).unwrap();
drop(db_handle);
}
});
} else {
@ -499,6 +498,7 @@ pub fn on_game_complete(
.game_statuses
.insert(meta.id.clone(), status.clone());
drop(db_handle);
app_handle
.emit(
&format!("update_game/{}", meta.id),
@ -519,6 +519,12 @@ pub fn push_game_update(
version: Option<GameVersion>,
status: GameStatusWithTransient,
) {
if let Some(GameDownloadStatus::Installed { .. } | GameDownloadStatus::SetupRequired { .. }) =
&status.0
&& version.is_none() {
panic!("pushed game for installed game that doesn't have version information");
}
app_handle
.emit(
&format!("update_game/{game_id}"),

View File

@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{path::PathBuf, sync::Arc};
use futures_lite::io;
use sysinfo::{Disk, DiskRefreshKind, Disks};
@ -21,7 +21,7 @@ pub fn get_disk_available(mount_point: PathBuf) -> Result<u64, ApplicationDownlo
return Ok(disk.available_space());
}
}
Err(ApplicationDownloadError::IoError(io::Error::other(
Err(ApplicationDownloadError::IoError(Arc::new(io::Error::other(
"could not find disk of path",
).kind()))
))))
}