mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2026-06-22 04:11:37 +10:00
feat(download widget): download widget and queue fix
This commit is contained in:
+1
-1
@@ -26,4 +26,4 @@ dist-ssr
|
|||||||
.output
|
.output
|
||||||
|
|
||||||
src-tauri/flamegraph.svg
|
src-tauri/flamegraph.svg
|
||||||
src-tuair/perf*
|
src-tauri/perf*
|
||||||
+26
-20
@@ -1,53 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="h-16 bg-zinc-950 flex flex-row justify-between">
|
||||||
class="h-16 bg-zinc-950 flex flex-row justify-between"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row grow items-center pl-5 pr-2 py-3">
|
<div class="flex flex-row grow items-center pl-5 pr-2 py-3">
|
||||||
<div class="inline-flex items-center gap-x-10">
|
<div class="inline-flex items-center gap-x-10">
|
||||||
<NuxtLink to="/store">
|
<NuxtLink to="/store">
|
||||||
<Wordmark class="h-8 mb-0.5"/>
|
<Wordmark class="h-8 mb-0.5" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<nav class="inline-flex items-center mt-0.5">
|
<nav class="inline-flex items-center mt-0.5">
|
||||||
<ol class="inline-flex items-center gap-x-6">
|
<ol class="inline-flex items-center gap-x-6">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-for="(nav, navIdx) in navigation"
|
v-for="(nav, navIdx) in navigation"
|
||||||
:class="[
|
:class="[
|
||||||
'transition uppercase font-display font-semibold text-md',
|
'transition uppercase font-display font-semibold text-md',
|
||||||
navIdx === currentPageIndex
|
navIdx === currentPageIndex
|
||||||
? 'text-zinc-100'
|
? 'text-zinc-100'
|
||||||
: 'text-zinc-400 hover:text-zinc-200',
|
: 'text-zinc-400 hover:text-zinc-200',
|
||||||
]"
|
]"
|
||||||
:href="nav.route"
|
:href="nav.route"
|
||||||
>
|
>
|
||||||
{{ nav.label }}
|
{{ nav.label }}
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div @mousedown="() => window.startDragging()" class="flex cursor-pointer grow h-full" />
|
<div
|
||||||
|
@mousedown="() => window.startDragging()"
|
||||||
|
class="flex cursor-pointer grow h-full"
|
||||||
|
/>
|
||||||
<div class="inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
<ol class="inline-flex gap-3">
|
<ol class="inline-flex gap-3">
|
||||||
|
<HeaderQueueWidget
|
||||||
|
v-if="currentQueueObject"
|
||||||
|
:object="currentQueueObject"
|
||||||
|
/>
|
||||||
<li v-for="(item, itemIdx) in quickActions">
|
<li v-for="(item, itemIdx) in quickActions">
|
||||||
<HeaderWidget
|
<HeaderWidget
|
||||||
@click="item.action"
|
@click="item.action"
|
||||||
:notifications="item.notifications"
|
:notifications="item.notifications"
|
||||||
>
|
>
|
||||||
<component class="h-5" :is="item.icon"/>
|
<component class="h-5" :is="item.icon" />
|
||||||
</HeaderWidget>
|
</HeaderWidget>
|
||||||
</li>
|
</li>
|
||||||
<HeaderUserWidget/>
|
<HeaderUserWidget />
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<WindowControl class="h-16 w-16 p-4"/>
|
<WindowControl class="h-16 w-16 p-4" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {BellIcon, UserGroupIcon} from "@heroicons/vue/16/solid";
|
import { BellIcon, UserGroupIcon } from "@heroicons/vue/16/solid";
|
||||||
import type {NavigationItem, QuickActionNav} from "../types";
|
import type { NavigationItem, QuickActionNav } from "../types";
|
||||||
import HeaderWidget from "./HeaderWidget.vue";
|
import HeaderWidget from "./HeaderWidget.vue";
|
||||||
import {getCurrentWindow} from "@tauri-apps/api/window";
|
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||||
|
|
||||||
const window = getCurrentWindow();
|
const window = getCurrentWindow();
|
||||||
|
|
||||||
@@ -79,13 +84,14 @@ const currentPageIndex = useCurrentNavigationIndex(navigation);
|
|||||||
const quickActions: Array<QuickActionNav> = [
|
const quickActions: Array<QuickActionNav> = [
|
||||||
{
|
{
|
||||||
icon: UserGroupIcon,
|
icon: UserGroupIcon,
|
||||||
action: async () => {
|
action: async () => {},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: BellIcon,
|
icon: BellIcon,
|
||||||
action: async () => {
|
action: async () => {},
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const queue = useQueueState();
|
||||||
|
const currentQueueObject = computed(() => queue.value.queue.at(0));
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowDownTrayIcon } from "@heroicons/vue/20/solid";
|
||||||
|
|
||||||
|
const props = defineProps<{ object: QueueState["queue"][0] }>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="transition inline-flex items-center cursor-pointer rounded-sm px-4 py-1.5 bg-zinc-900 hover:bg-zinc-800 hover:text-zinc-300 relative"
|
||||||
|
>
|
||||||
|
<ArrowDownTrayIcon class="h-5 z-50 text-zinc-100 mix-blend-difference" />
|
||||||
|
<div
|
||||||
|
class="transition-all absolute left-0 top-0 bottom-0 bg-blue-600 z-10"
|
||||||
|
:style="{ width: `${props.object.progress * 100}%` }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { listen } from "@tauri-apps/api/event";
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
|
||||||
export type QueueState = {
|
export type QueueState = {
|
||||||
queue: Array<{ id: string; status: string }>;
|
queue: Array<{ id: string; status: string, progress: number }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useQueueState = () =>
|
export const useQueueState = () =>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
<div class="flex flex-col bg-zinc-900 overflow-hidden">
|
<div class="flex flex-col bg-zinc-900 overflow-hidden">
|
||||||
<Header class="select-none" />
|
<Header class="select-none" />
|
||||||
<div class="grow overflow-y-auto">
|
<div class="grow overflow-y-auto">
|
||||||
<span class="text-white">{{ queueState }}</span>
|
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -227,7 +227,7 @@
|
|||||||
: 'font-normal',
|
: 'font-normal',
|
||||||
'block truncate',
|
'block truncate',
|
||||||
]"
|
]"
|
||||||
>{{ dir }}}</span
|
>{{ dir }}</span
|
||||||
>
|
>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -41,17 +41,19 @@ impl DropWriter<File> {
|
|||||||
// Write automatically pushes to file and hasher
|
// Write automatically pushes to file and hasher
|
||||||
impl Write for DropWriter<File> {
|
impl Write for DropWriter<File> {
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
/*
|
||||||
self.hasher.write_all(buf).map_err(|e| {
|
self.hasher.write_all(buf).map_err(|e| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
ErrorKind::Other,
|
ErrorKind::Other,
|
||||||
format!("Unable to write to hasher: {}", e),
|
format!("Unable to write to hasher: {}", e),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
*/
|
||||||
self.destination.write(buf)
|
self.destination.write(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
self.hasher.flush()?;
|
// self.hasher.flush()?;
|
||||||
self.destination.flush()
|
self.destination.flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,6 +187,7 @@ pub fn download_game_chunk(
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
let checksum = pipeline
|
let checksum = pipeline
|
||||||
.finish()
|
.finish()
|
||||||
.map_err(|e| GameDownloadError::IoError(e))?;
|
.map_err(|e| GameDownloadError::IoError(e))?;
|
||||||
@@ -193,6 +196,7 @@ pub fn download_game_chunk(
|
|||||||
if res != ctx.checksum {
|
if res != ctx.checksum {
|
||||||
return Err(GameDownloadError::Checksum);
|
return Err(GameDownloadError::Checksum);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ pub struct DownloadManagerBuilder {
|
|||||||
status: Arc<Mutex<DownloadManagerStatus>>,
|
status: Arc<Mutex<DownloadManagerStatus>>,
|
||||||
app_handle: AppHandle,
|
app_handle: AppHandle,
|
||||||
|
|
||||||
current_game_interface: Option<Arc<GameDownloadAgentQueueStandin>>, // Should be the only game download agent in the map with the "Go" flag
|
current_download_agent: Option<Arc<GameDownloadAgentQueueStandin>>, // Should be the only game download agent in the map with the "Go" flag
|
||||||
active_control_flag: Option<DownloadThreadControl>,
|
active_control_flag: Option<DownloadThreadControl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ impl DownloadManagerBuilder {
|
|||||||
download_agent_registry: HashMap::new(),
|
download_agent_registry: HashMap::new(),
|
||||||
download_queue: queue.clone(),
|
download_queue: queue.clone(),
|
||||||
command_receiver,
|
command_receiver,
|
||||||
current_game_interface: None,
|
current_download_agent: None,
|
||||||
active_control_flag: None,
|
active_control_flag: None,
|
||||||
status: status.clone(),
|
status: status.clone(),
|
||||||
sender: command_sender.clone(),
|
sender: command_sender.clone(),
|
||||||
@@ -138,6 +138,16 @@ impl DownloadManagerBuilder {
|
|||||||
self.app_handle.emit("update_queue", event_data).unwrap();
|
self.app_handle.emit("update_queue", event_data).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cleanup_current_game(&mut self, game_id: &String) -> Arc<GameDownloadAgent> {
|
||||||
|
self.download_queue.pop_front();
|
||||||
|
let download_agent = self.download_agent_registry.remove(game_id).unwrap();
|
||||||
|
self.active_control_flag = None;
|
||||||
|
*self.progress.lock().unwrap() = None;
|
||||||
|
self.current_download_agent = None;
|
||||||
|
|
||||||
|
return download_agent;
|
||||||
|
}
|
||||||
|
|
||||||
fn manage_queue(mut self) -> Result<(), ()> {
|
fn manage_queue(mut self) -> Result<(), ()> {
|
||||||
loop {
|
loop {
|
||||||
let signal = match self.command_receiver.recv() {
|
let signal = match self.command_receiver.recv() {
|
||||||
@@ -186,14 +196,11 @@ impl DownloadManagerBuilder {
|
|||||||
|
|
||||||
fn manage_completed_signal(&mut self, game_id: String) {
|
fn manage_completed_signal(&mut self, game_id: String) {
|
||||||
info!("Got signal 'Completed'");
|
info!("Got signal 'Completed'");
|
||||||
if let Some(interface) = &self.current_game_interface {
|
if let Some(interface) = &self.current_download_agent {
|
||||||
// When if let chains are stabilised, combine these two statements
|
// When if let chains are stabilised, combine these two statements
|
||||||
if interface.id == game_id {
|
if interface.id == game_id {
|
||||||
info!("Popping consumed data");
|
info!("Popping consumed data");
|
||||||
self.download_queue.pop_front();
|
let download_agent = self.cleanup_current_game(&game_id);
|
||||||
let download_agent = self.download_agent_registry.remove(&game_id).unwrap();
|
|
||||||
self.active_control_flag = None;
|
|
||||||
*self.progress.lock().unwrap() = None;
|
|
||||||
|
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
on_game_complete(game_id, download_agent.version.clone(), &self.app_handle)
|
on_game_complete(game_id, download_agent.version.clone(), &self.app_handle)
|
||||||
@@ -240,6 +247,11 @@ impl DownloadManagerBuilder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.current_download_agent.is_some() {
|
||||||
|
info!("skipping go signal due to existing download job");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
info!("Starting download agent");
|
info!("Starting download agent");
|
||||||
let agent_data = self.download_queue.read().front().unwrap().clone();
|
let agent_data = self.download_queue.read().front().unwrap().clone();
|
||||||
let download_agent = self
|
let download_agent = self
|
||||||
@@ -247,9 +259,9 @@ impl DownloadManagerBuilder {
|
|||||||
.get(&agent_data.id)
|
.get(&agent_data.id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
self.current_game_interface = Some(agent_data);
|
self.current_download_agent = Some(agent_data);
|
||||||
// Cloning option should be okay because it only clones the Arc inside, not the AgentInterfaceData
|
// Cloning option should be okay because it only clones the Arc inside, not the AgentInterfaceData
|
||||||
let agent_data = self.current_game_interface.clone().unwrap();
|
let agent_data = self.current_download_agent.clone().unwrap();
|
||||||
|
|
||||||
let version_name = download_agent.version.clone();
|
let version_name = download_agent.version.clone();
|
||||||
|
|
||||||
@@ -284,31 +296,34 @@ impl DownloadManagerBuilder {
|
|||||||
active_control_flag.set(DownloadThreadControlFlag::Go);
|
active_control_flag.set(DownloadThreadControlFlag::Go);
|
||||||
self.set_status(DownloadManagerStatus::Downloading);
|
self.set_status(DownloadManagerStatus::Downloading);
|
||||||
self.set_game_status(
|
self.set_game_status(
|
||||||
self.current_game_interface.as_ref().unwrap().id.clone(),
|
self.current_download_agent.as_ref().unwrap().id.clone(),
|
||||||
DatabaseGameStatus::Downloading { version_name },
|
DatabaseGameStatus::Downloading { version_name },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
fn manage_error_signal(&self, error: GameDownloadError) {
|
fn manage_error_signal(&mut self, error: GameDownloadError) {
|
||||||
let current_status = self.current_game_interface.clone().unwrap();
|
let current_status = self.current_download_agent.clone().unwrap();
|
||||||
|
|
||||||
|
self.cleanup_current_game(¤t_status.id); // Remove all the locks and shit
|
||||||
|
|
||||||
let mut lock = current_status.status.lock().unwrap();
|
let mut lock = current_status.status.lock().unwrap();
|
||||||
*lock = GameDownloadStatus::Error;
|
*lock = GameDownloadStatus::Error;
|
||||||
self.set_status(DownloadManagerStatus::Error(error));
|
self.set_status(DownloadManagerStatus::Error(error));
|
||||||
|
|
||||||
self.set_game_status(
|
let game_id = self.current_download_agent.as_ref().unwrap().id.clone();
|
||||||
self.current_game_interface.as_ref().unwrap().id.clone(),
|
self.set_game_status(game_id, DatabaseGameStatus::Remote {});
|
||||||
DatabaseGameStatus::Remote {},
|
|
||||||
);
|
|
||||||
|
|
||||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||||
}
|
}
|
||||||
fn manage_cancel_signal(&mut self, game_id: String) {
|
fn manage_cancel_signal(&mut self, game_id: String) {
|
||||||
if let Some(current_flag) = &self.active_control_flag {
|
if let Some(current_flag) = &self.active_control_flag {
|
||||||
current_flag.set(DownloadThreadControlFlag::Stop);
|
current_flag.set(DownloadThreadControlFlag::Stop);
|
||||||
self.active_control_flag = None;
|
|
||||||
*self.progress.lock().unwrap() = None;
|
|
||||||
}
|
}
|
||||||
// TODO wait until current download exits
|
// TODO wait until current download exits
|
||||||
|
|
||||||
|
// This cleanup function might break things because it
|
||||||
|
// unsets the control flag
|
||||||
|
self.cleanup_current_game(&game_id);
|
||||||
|
|
||||||
self.download_agent_registry.remove(&game_id);
|
self.download_agent_registry.remove(&game_id);
|
||||||
let mut lock = self.download_queue.edit();
|
let mut lock = self.download_queue.edit();
|
||||||
let index = match lock.iter().position(|interface| interface.id == game_id) {
|
let index = match lock.iter().position(|interface| interface.id == game_id) {
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ static PROGRESS_UPDATES: usize = 100;
|
|||||||
impl ProgressObject {
|
impl ProgressObject {
|
||||||
pub fn new(max: usize, length: usize, sender: Sender<DownloadManagerSignal>) -> Self {
|
pub fn new(max: usize, length: usize, sender: Sender<DownloadManagerSignal>) -> Self {
|
||||||
let arr = Mutex::new((0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect());
|
let arr = Mutex::new((0..length).map(|_| Arc::new(AtomicUsize::new(0))).collect());
|
||||||
// TODO: consolidate this calculate with the set_max function below
|
// TODO: consolidate this calculation with the set_max function below
|
||||||
let points_to_push_update = max / PROGRESS_UPDATES;
|
let points_to_push_update = max / PROGRESS_UPDATES;
|
||||||
Self {
|
Self {
|
||||||
max: Arc::new(Mutex::new(max)),
|
max: Arc::new(Mutex::new(max)),
|
||||||
|
|||||||
Reference in New Issue
Block a user