From fc38a07b154062312c11c2605428d06ace29d1d9 Mon Sep 17 00:00:00 2001 From: Mubelotix Date: Sun, 16 Jun 2024 16:15:46 +0200 Subject: [PATCH] Display status on page --- daemon/src/index/index.rs | 8 +- daemon/src/index/status.rs | 4 +- webui/src/api.rs | 41 ++++---- webui/src/components/indexing_status/api.rs | 5 + .../indexing_status/indexing_status.html | 16 ++++ .../indexing_status/indexing_status.rs | 93 +++++++++++++++++++ webui/src/components/indexing_status/ty.rs | 1 + webui/src/main.rs | 2 + webui/src/pages/search/search.html | 1 + webui/src/pages/search/search.rs | 1 + webui/src/prelude.rs | 4 +- webui/src/util.rs | 4 +- 12 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 webui/src/components/indexing_status/api.rs create mode 100644 webui/src/components/indexing_status/indexing_status.html create mode 100644 webui/src/components/indexing_status/indexing_status.rs create mode 120000 webui/src/components/indexing_status/ty.rs diff --git a/daemon/src/index/index.rs b/daemon/src/index/index.rs index fa097a4..54ec473 100644 --- a/daemon/src/index/index.rs +++ b/daemon/src/index/index.rs @@ -101,6 +101,7 @@ impl DocumentIndex { } // Load documents + to_load_unprioritized.retain(|cid, _| !to_load.contains_key(cid)); if !to_load.is_empty() {debug!("{} documents to load ({:.02?}s)", to_load.len(), start.elapsed().as_secs_f32())} let (to_load_len, to_load_unprioritized_len) = (to_load.len(), to_load_unprioritized.len()); for (i, (cid, (name, parent_cid))) in to_load.drain().chain(to_load_unprioritized.drain()).enumerate() { @@ -108,7 +109,7 @@ impl DocumentIndex { let remaining_unprioritized = std::cmp::min(to_load_unprioritized_len, to_load_len + to_load_unprioritized_len - i); self.set_status(listed.len(), to_list.len(), loaded.len(), remaining_to_load, remaining_unprioritized).await; - if !loaded.insert(cid.clone()) {continue} + loaded.insert(cid.clone()); let Ok(document) = fetch_document(ipfs_rpc, &cid).await else {continue}; let Some(inspected) = inspect_document(document) else {continue}; self.add_document(&cid, inspected).await; @@ -116,7 +117,10 @@ impl DocumentIndex { } // Update filter + self.set_status(listed.len(), 0, loaded.len(), 0, 0).await; + self.set_status_updating_filter(true).await; self.update_filter().await; + self.set_status_updating_filter(false).await; let load = self.get_filter().await.load()*100.0; if load != previous_load { previous_load = load; @@ -165,9 +169,7 @@ impl DocumentIndex { } pub async fn update_filter(&self) { - self.set_status_updating_filter(true).await; self.inner.write().await.update_filter().await; - self.set_status_updating_filter(false).await; } } diff --git a/daemon/src/index/status.rs b/daemon/src/index/status.rs index 4ff62a9..ace6013 100644 --- a/daemon/src/index/status.rs +++ b/daemon/src/index/status.rs @@ -1,6 +1,6 @@ -use serde::Serialize; +use serde::{Serialize, Deserialize}; -#[derive(Default, Debug, Clone, Serialize)] +#[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct IndexingStatus { pub listed: usize, pub to_list: usize, diff --git a/webui/src/api.rs b/webui/src/api.rs index 3395f2c..a65cba3 100644 --- a/webui/src/api.rs +++ b/webui/src/api.rs @@ -13,14 +13,13 @@ pub enum ApiError { impl ApiError { pub fn to_format_parts(&self) -> (&'static str, Vec, String) { - let (title, recommandations, details) = match self { + let (title, recommandations) = match self { ApiError::InputJson(e) => ( "Failed to craft request", vec![ String::from("Open an issue on GitHub"), String::from("Try again"), - ], - format!("InputJson: {e}") + ] ), ApiError::OutputJson(e) => ( "Failed to read results", @@ -29,8 +28,7 @@ impl ApiError { String::from("Make sure the daemon address is correct"), String::from("Open an issue on GitHub"), String::from("Try again"), - ], - format!("OutputJson: {e}") + ] ), ApiError::Fetch(e) => ( "Failed to send query", @@ -39,8 +37,7 @@ impl ApiError { String::from("Make sure the daemon address is correct"), String::from("Make sure CORS is properly configured"), String::from("Try again"), - ], - format!("Fetch: {}", e.clone().dyn_into::().unwrap().message()) + ] ), ApiError::NotText(e) => ( "Invalid response", @@ -48,8 +45,7 @@ impl ApiError { String::from("Make sure the daemon address is correct"), String::from("Open an issue on GitHub"), String::from("Try again"), - ], - format!("NotText: {}", e.clone().dyn_into::().unwrap().message()) + ] ), ApiError::BadRequest(e) => ( "Failed to communicate with daemon", @@ -58,8 +54,7 @@ impl ApiError { String::from("Make sure the daemon address is correct"), String::from("Open an issue on GitHub"), String::from("Try again"), - ], - format!("BadRequest: {e}") + ] ), ApiError::Server(e) => ( "Daemon is having issues", @@ -67,19 +62,31 @@ impl ApiError { String::from("Make sure the daemon is up to date"), String::from("Open an issue on GitHub"), String::from("Try again"), - ], - format!("Server: {e}") + ] ), ApiError::Unknown(e) => ( "Unknown error", vec![ String::from("Make sure the daemon address is correct"), String::from("Try again"), - ], - format!("Unknown: {e}") + ] ), }; - (title, recommandations, details) + (title, recommandations, self.to_string()) + } +} + +impl std::fmt::Display for ApiError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + ApiError::InputJson(e) => write!(f, "InputJson: {}", e), + ApiError::OutputJson(e) => write!(f, "OutputJson: {}", e), + ApiError::Fetch(e) => write!(f, "Fetch: {}", e.clone().dyn_into::().unwrap().message()), + ApiError::NotText(e) => write!(f, "NotText: {}", e.clone().dyn_into::().unwrap().message()), + ApiError::BadRequest(e) => write!(f, "BadRequest: {}", e), + ApiError::Server(e) => write!(f, "Server: {}", e), + ApiError::Unknown(e) => write!(f, "Unknown: {}", e), + } } } @@ -89,7 +96,7 @@ impl From for ApiError { } } -async fn get(url: impl AsRef) -> Result { +pub async fn get(url: impl AsRef) -> Result { api_custom_method(url, "GET", ()).await } diff --git a/webui/src/components/indexing_status/api.rs b/webui/src/components/indexing_status/api.rs new file mode 100644 index 0000000..b1521e7 --- /dev/null +++ b/webui/src/components/indexing_status/api.rs @@ -0,0 +1,5 @@ +use crate::prelude::*; + +pub async fn get_indexing_status(rpc_addr: &str) -> Result { + get(format!("{rpc_addr}/indexing-status")).await +} diff --git a/webui/src/components/indexing_status/indexing_status.html b/webui/src/components/indexing_status/indexing_status.html new file mode 100644 index 0000000..f9b7a28 --- /dev/null +++ b/webui/src/components/indexing_status/indexing_status.html @@ -0,0 +1,16 @@ +
+ +
Your daemon is exploring your folders ({{progress_value}} / {{progress_max}}).
+ +
+ + +
Your daemon is indexing your files ({{progress_value}} / {{progress_max}}).
+ +
+ + +
Your daemon is compiling its index.
+ +
+
diff --git a/webui/src/components/indexing_status/indexing_status.rs b/webui/src/components/indexing_status/indexing_status.rs new file mode 100644 index 0000000..9b35045 --- /dev/null +++ b/webui/src/components/indexing_status/indexing_status.rs @@ -0,0 +1,93 @@ +use crate::prelude::*; + +mod ty; +pub use ty::*; +mod api; +pub use api::*; + +pub struct IndexingStatusComp { + status: Option, +} + +#[derive(PartialEq, Properties)] +pub struct IndexingStatusProps { + pub rpc_addr: String, +} + +pub enum IndexingStatusMsg { + SetStatus(IndexingStatus), +} + +impl Component for IndexingStatusComp { + type Message = IndexingStatusMsg; + type Properties = IndexingStatusProps; + + fn create(ctx: &Context) -> Self { + let link2 = ctx.link().clone(); + let rpc_addr2 = ctx.props().rpc_addr.clone(); + spawn_local(async move { + loop { + if link2.get_component().is_none() { + log!("IndexingStatusComp: Component dropped, stopping loop"); + break; + } + + let status = match get_indexing_status(&rpc_addr2).await { + Ok(status) => status, + Err(e) => { + log!("Failed to get indexing status: {}", e); + sleep(Duration::from_secs(1)).await; + continue; + } + }; + + let idle = status.to_list == 0 && status.to_load == 0 && status.to_load_unprioritized == 0; + let cooldown = if idle { 30 } else { 1 }; + + link2.send_message(IndexingStatusMsg::SetStatus(status)); + sleep(Duration::from_secs(cooldown)).await; + } + }); + + Self { + status: None, + } + } + + fn update(&mut self, _ctx: &Context, msg: IndexingStatusMsg) -> bool { + match msg { + IndexingStatusMsg::SetStatus(status) => { + self.status = Some(status); + true + } + } + } + + fn view(&self, _ctx: &Context) -> Html { + let (exploring, indexing, building_filter, progress_value, progress_max) = match &self.status { + Some(status) if status.to_list > 0 => ( + true, false, false, + Some(status.listed), + status.listed + status.to_list, + ), + Some(status) if status.to_load + status.to_load_unprioritized > 0 => ( + false, true, false, + Some(status.loaded), + status.loaded + status.to_load + status.to_load_unprioritized, + ), + Some(status) if status.updating_filter => ( + false, false, true, + None, + 1, + ), + _ => return html! {}, + }; + + template_html!( + "components/indexing_status/indexing_status.html", + progress_max = {progress_max.to_string()}, + progress_value = {progress_value.unwrap_or(0).to_string()}, + ... + ) + } +} diff --git a/webui/src/components/indexing_status/ty.rs b/webui/src/components/indexing_status/ty.rs new file mode 120000 index 0000000..3540769 --- /dev/null +++ b/webui/src/components/indexing_status/ty.rs @@ -0,0 +1 @@ +../../../../daemon/src/index/status.rs \ No newline at end of file diff --git a/webui/src/main.rs b/webui/src/main.rs index dcd45f3..02fc4c1 100644 --- a/webui/src/main.rs +++ b/webui/src/main.rs @@ -19,6 +19,8 @@ mod search_bar; mod connection_status; #[path = "components/result/result.rs"] mod result_comp; +#[path = "components/indexing_status/indexing_status.rs"] +mod indexing_status; mod query; diff --git a/webui/src/pages/search/search.html b/webui/src/pages/search/search.html index f0bbb98..56afb84 100644 --- a/webui/src/pages/search/search.html +++ b/webui/src/pages/search/search.html @@ -19,6 +19,7 @@

Admarus

Join the network + diff --git a/webui/src/pages/search/search.rs b/webui/src/pages/search/search.rs index 6a1b26a..5e5075e 100644 --- a/webui/src/pages/search/search.rs +++ b/webui/src/pages/search/search.rs @@ -30,6 +30,7 @@ impl Component for SearchPage { let onclick_lucky = ctx.props().app_link.callback(|_| AppMsg::ChangePage(Page::lucky(None))); let conn_status = Rc::clone(&ctx.props().conn_status); let onchange_conn_status = ctx.props().onchange_conn_status.clone(); + let rpc_addr = conn_status.admarus_addr(); template_html!( "pages/search/search.html", diff --git a/webui/src/prelude.rs b/webui/src/prelude.rs index f06d298..35ec38f 100644 --- a/webui/src/prelude.rs +++ b/webui/src/prelude.rs @@ -1,8 +1,8 @@ pub use crate::{ app::*, search::*, settings::*, util::*, results::*, result::*, api_bodies::*, api::*, lang::*, - search_bar::*, lucky::*, query::*, connection_status::*, result_comp::*, * + search_bar::*, lucky::*, query::*, connection_status::*, indexing_status::*, result_comp::*, * }; -pub use js_sys::{Array, Function, Promise, Reflect::*}; +pub use js_sys::{Array, Function, Promise, Reflect}; pub use std::{time::Duration, rc::Rc, cmp::Ordering, collections::{HashMap, HashSet}, ops::Deref}; pub use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; pub use wasm_bindgen_futures::{spawn_local, JsFuture}; diff --git a/webui/src/util.rs b/webui/src/util.rs index 85a0778..ef6bcc9 100644 --- a/webui/src/util.rs +++ b/webui/src/util.rs @@ -33,7 +33,7 @@ impl HackTraitAnimation for Scope { let document = unsafe { web_sys::window().unwrap_unchecked().document().unwrap_unchecked() }; - let start_view_transition = get(&document, &"startViewTransition".into()).unwrap(); + let start_view_transition = Reflect::get(&document, &"startViewTransition".into()).unwrap(); let start_view_transition = match start_view_transition.dyn_ref::() { Some(f) => f.clone(), None => { @@ -47,7 +47,7 @@ impl HackTraitAnimation for Scope { }) as Box); let args = Array::new_with_length(1); args.set(0, callback.as_ref().clone()); - apply(&start_view_transition, &document, &args).unwrap(); + Reflect::apply(&start_view_transition, &document, &args).unwrap(); if !LEAK_MEMORY { spawn_local(async move {