feat(download ui): add speed and time remaining information

closes #7

Co-authored-by: AdenMGB <140392385+AdenMGB@users.noreply.github.com>
This commit is contained in:
DecDuck
2024-12-27 11:50:13 +11:00
parent 6a8d0af87d
commit 1f899ec349
9 changed files with 188 additions and 56 deletions

View File

@ -41,7 +41,8 @@ pub enum DownloadManagerSignal {
/// Any error which occurs in the agent
Error(GameDownloadError),
/// Pushes UI update
Update,
UpdateUIQueue,
UpdateUIStats(usize, usize), //kb/s and seconds
/// Uninstall game
/// Takes game ID
Uninstall(String),
@ -156,7 +157,7 @@ impl DownloadManager {
let to_move = queue.remove(current_index).unwrap();
queue.insert(new_index, to_move);
self.command_sender
.send(DownloadManagerSignal::Update)
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
pub fn cancel(&self, game_id: String) {
@ -188,7 +189,7 @@ impl DownloadManager {
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
}
self.command_sender
.send(DownloadManagerSignal::Update)
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
pub fn pause_downloads(&self) {

View File

@ -16,7 +16,7 @@ use crate::{
db::{Database, GameStatus, GameTransientStatus},
library::{
on_game_complete, push_game_update, GameUpdateEvent, QueueUpdateEvent,
QueueUpdateEventQueueData,
QueueUpdateEventQueueData, StatsUpdateEvent,
},
state::{GameStatusManager, GameStatusWithTransient},
DB,
@ -128,7 +128,13 @@ impl DownloadManagerBuilder {
push_game_update(&self.app_handle, id, status);
}
fn push_manager_update(&self) {
fn push_ui_stats_update(&self, kbs: usize, time: usize) {
let event_data = StatsUpdateEvent { speed: kbs, time };
self.app_handle.emit("update_stats", event_data).unwrap();
}
fn push_ui_queue_update(&self) {
let queue = self.download_queue.read();
let queue_objs: Vec<QueueUpdateEventQueueData> = queue
.iter()
@ -208,8 +214,11 @@ impl DownloadManagerBuilder {
DownloadManagerSignal::Cancel => {
self.manage_cancel_signal();
}
DownloadManagerSignal::Update => {
self.push_manager_update();
DownloadManagerSignal::UpdateUIQueue => {
self.push_ui_queue_update();
}
DownloadManagerSignal::UpdateUIStats(kbs, time) => {
self.push_ui_stats_update(kbs, time);
}
DownloadManagerSignal::Finish => {
self.stop_and_wait_current_download();
@ -315,7 +324,7 @@ impl DownloadManagerBuilder {
self.manage_go_signal();
}
self.push_manager_update();
self.push_ui_queue_update();
}
fn manage_stop_signal(&mut self) {
@ -356,7 +365,9 @@ impl DownloadManagerBuilder {
}
}
}
self.sender.send(DownloadManagerSignal::Update).unwrap();
self.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
self.sender.send(DownloadManagerSignal::Go).unwrap();
}
@ -406,7 +417,9 @@ impl DownloadManagerBuilder {
GameTransientStatus::Downloading { version_name },
);
});
self.sender.send(DownloadManagerSignal::Update).unwrap();
self.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
fn manage_go_signal(&mut self) {
@ -483,7 +496,9 @@ impl DownloadManagerBuilder {
);
});
self.sender.send(DownloadManagerSignal::Update).unwrap();
self.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
fn manage_error_signal(&mut self, error: GameDownloadError) {
let current_status = self.current_download_agent.clone().unwrap();
@ -504,7 +519,9 @@ impl DownloadManagerBuilder {
db_handle.games.transient_statuses.remove(id);
});
self.sender.send(DownloadManagerSignal::Update).unwrap();
self.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
fn manage_cancel_signal(&mut self) {
self.stop_and_wait_current_download();

View File

@ -2,9 +2,9 @@ use std::{
sync::{
atomic::{AtomicUsize, Ordering},
mpsc::Sender,
Arc, Mutex,
Arc, Mutex, RwLock,
},
time::Instant,
time::{Duration, Instant},
};
use log::info;
@ -20,6 +20,8 @@ pub struct ProgressObject {
points_towards_update: Arc<AtomicUsize>,
points_to_push_update: Arc<AtomicUsize>,
last_update: Arc<RwLock<Instant>>,
amount_last_update: Arc<AtomicUsize>,
}
pub struct ProgressHandle {
@ -59,6 +61,8 @@ impl ProgressObject {
points_towards_update: Arc::new(AtomicUsize::new(0)),
points_to_push_update: Arc::new(AtomicUsize::new(points_to_push_update)),
last_update: Arc::new(RwLock::new(Instant::now())),
amount_last_update: Arc::new(AtomicUsize::new(0)),
}
}
@ -69,12 +73,42 @@ impl ProgressObject {
let to_update = self.points_to_push_update.fetch_add(0, Ordering::Relaxed);
if current_amount < to_update {
return;
if current_amount >= to_update {
self.points_towards_update
.fetch_sub(to_update, Ordering::Relaxed);
self.sender
.send(DownloadManagerSignal::UpdateUIQueue)
.unwrap();
}
let last_update = self.last_update.read().unwrap();
let last_update_difference = Instant::now().duration_since(*last_update).as_millis();
if last_update_difference > 1000 {
// push update
drop(last_update);
let mut last_update = self.last_update.write().unwrap();
*last_update = Instant::now();
drop(last_update);
let current_amount = self.sum();
let max = self.get_max();
let amount_at_last_update = self.amount_last_update.fetch_add(0, Ordering::Relaxed);
self.amount_last_update
.store(current_amount, Ordering::Relaxed);
let amount_since_last_update = current_amount - amount_at_last_update;
let kilobytes_per_second = amount_since_last_update / (last_update_difference as usize).max(1);
let remaining = max - current_amount; // bytes
let time_remaining = (remaining / 1000) / kilobytes_per_second.max(1);
self.sender
.send(DownloadManagerSignal::UpdateUIStats(
kilobytes_per_second,
time_remaining,
))
.unwrap();
}
self.points_towards_update
.fetch_sub(to_update, Ordering::Relaxed);
self.sender.send(DownloadManagerSignal::Update).unwrap();
}
pub fn set_time_now(&self) {

View File

@ -53,6 +53,12 @@ pub struct QueueUpdateEvent {
pub status: DownloadManagerStatus,
}
#[derive(serde::Serialize, Clone)]
pub struct StatsUpdateEvent {
pub speed: usize,
pub time: usize,
}
// Game version with some fields missing and size information
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]