From ea9ad2f8a79c53ff211675be7a4e2eaa3e12fe67 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 5 Apr 2026 17:03:19 -0400 Subject: [PATCH] rewrite components such that data always flows downwards --- AobaClient/src/components/media_grid.rs | 53 ++++++++++++------------- AobaClient/src/components/media_item.rs | 27 ++++++++----- AobaClient/src/components/pagination.rs | 27 +++++++------ AobaClient/src/components/search.rs | 8 +++- AobaClient/src/views/home.rs | 35 +++++++++++----- AobaClient/src/views/media.rs | 24 ++++------- 6 files changed, 95 insertions(+), 79 deletions(-) diff --git a/AobaClient/src/components/media_grid.rs b/AobaClient/src/components/media_grid.rs index b28169d..fc1d522 100644 --- a/AobaClient/src/components/media_grid.rs +++ b/AobaClient/src/components/media_grid.rs @@ -9,18 +9,22 @@ use crate::{ }; #[derive(PartialEq, Clone, Props)] -pub struct MediaGridProps -{ +pub struct MediaGridProps { pub query: Signal, pub max_page: Signal, pub total_items: Signal, pub page: Signal, pub page_size: Signal, + pub on_page_loaded: Option>, +} + +pub struct PaginationInfo { + pub total_pages: i32, + pub total_items: i32, } #[component] -pub fn MediaGrid(mut props: MediaGridProps) -> Element -{ +pub fn MediaGrid(props: MediaGridProps) -> Element { let mut error_display = use_signal(|| { rsx! {} }); @@ -34,32 +38,29 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element query: Some(props.query.cloned()), }; let result = client.list_media(request).await; - if let Ok(items) = result - { + if let Ok(items) = result { let res = items.into_inner(); return Ok(res); - } - else - { + } else { let err = result.err().unwrap(); let message = err.message(); return Err(format!("Failed to load results: {message}")); } })); - use_effect(move || match media_result() - { - Some(value) => match value - { - Ok(result) => - { - if let Some(pagination) = result.pagination - { + use_effect(move || match media_result() { + Some(value) => match value { + Ok(result) => { + if let Some(pagination) = result.pagination { let total_pages = pagination.total_pages; let total_items = pagination.total_items; - props.max_page.set(total_pages.max(1)); - props.total_items.set(total_items.max(1)); + if let Some(handler) = props.on_page_loaded { + handler.call(PaginationInfo { + total_pages, + total_items, + }); + } } items.set(Some(result.items)); error_display.set(rsx! {}); @@ -70,8 +71,7 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element } }), }, - _ => - {} + _ => {} }); rsx! { @@ -87,8 +87,7 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element } #[component] -fn PlaceholderGrid(count: usize) -> Element -{ +fn PlaceholderGrid(count: usize) -> Element { rsx! { div{ class: "mediaGrid", @@ -100,12 +99,12 @@ fn PlaceholderGrid(count: usize) -> Element } #[component] -fn MediaList(items: Vec) -> Element -{ +fn MediaList(items: Vec) -> Element { rsx! { - {items.iter().map(|itm| rsx!{ + {items.iter().enumerate().map(|(index, itm)| rsx!{ MediaItem { - item: itm.clone() + item: itm.clone(), + index } })} } diff --git a/AobaClient/src/components/media_item.rs b/AobaClient/src/components/media_item.rs index c913ab5..404a964 100644 --- a/AobaClient/src/components/media_item.rs +++ b/AobaClient/src/components/media_item.rs @@ -1,5 +1,7 @@ use dioxus::prelude::*; -use dioxus_primitives::context_menu::{ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger}; +use dioxus_primitives::context_menu::{ + ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger, +}; use tonic::{Response, Status}; use web_sys::window; @@ -11,23 +13,28 @@ use crate::{ }, }; +pub struct MediaClassChangeEvent { + pub index: usize, + pub class: String, +} + #[derive(PartialEq, Clone, Props)] -pub struct MediaItemProps -{ +pub struct MediaItemProps { pub item: MediaModel, + pub index: usize, + pub on_class_changed: Option>, + pub on_deleted: Option>, } #[component] -pub fn MediaItem(props: MediaItemProps) -> Element -{ +pub fn MediaItem(props: MediaItemProps) -> Element { let item = props.item; let mtype = item.media_type().as_str_name(); let filename = item.file_name; let id = item.id.unwrap().value; let thumb = item.thumb_url; let class = item.class; - let mut class_signal = use_signal(|| match class - { + let mut class_signal = use_signal(|| match class { 1 => "blur", 2 => "secret", _ => "", @@ -174,8 +181,7 @@ pub fn MediaItem(props: MediaItemProps) -> Element } #[component] -pub fn MediaItemPlaceHolder() -> Element -{ +pub fn MediaItemPlaceHolder() -> Element { return rsx! { div { class: "mediaItem placeholder", img { }, @@ -190,8 +196,7 @@ pub fn MediaItemPlaceHolder() -> Element }; } -async fn set_class(id: String, class: i32) -> Result, Status> -{ +async fn set_class(id: String, class: i32) -> Result, Status> { let mut client = get_rpc_client(); return client .set_media_class(SetMediaClassRequest { diff --git a/AobaClient/src/components/pagination.rs b/AobaClient/src/components/pagination.rs index c00f17a..23d81c4 100644 --- a/AobaClient/src/components/pagination.rs +++ b/AobaClient/src/components/pagination.rs @@ -2,8 +2,12 @@ use dioxus::prelude::*; use web_sys::window; #[component] -pub fn Pagination(page: Signal, max_page: Signal, item_count: Signal) -> Element -{ +pub fn Pagination( + page: Signal, + max_page: Signal, + item_count: Signal, + on_page_change: EventHandler, +) -> Element { let cur_page_val = page.cloned(); let max_page_val = max_page.cloned(); let item_count_val = item_count.cloned(); @@ -12,16 +16,16 @@ pub fn Pagination(page: Signal, max_page: Signal, item_count: Signal, max_page: Signal, item_count: Signal, max_page: Signal, item_count: Signal, page: Signal) -> Element { +pub fn Search(query: String, oninput: Option>) -> Element { rsx! { div { class: "searchBar", input { r#type: "search", placeholder: "Search Files", value: query, - oninput: move |event| {query.set(event.value()); page.set(1);}, + oninput: move |event| { + if let Some(handler) = oninput { + handler.call(event.value()); + } + }, } } } diff --git a/AobaClient/src/views/home.rs b/AobaClient/src/views/home.rs index adf21c6..c865d0b 100644 --- a/AobaClient/src/views/home.rs +++ b/AobaClient/src/views/home.rs @@ -1,4 +1,4 @@ -use crate::components::{MediaGrid, Pagination, Search}; +use crate::components::{MediaGrid, Pagination, PaginationInfo, Search}; use dioxus::prelude::*; // #[component] @@ -19,19 +19,34 @@ use dioxus::prelude::*; // } #[component] -pub fn Home(page: Option, q: Option) -> Element -{ - let query = use_signal(|| q.unwrap_or("".to_string())); - let page = use_signal(|| page.unwrap_or(1)); +pub fn Home(page: Option, q: Option) -> Element { + let mut query = use_signal(|| q.unwrap_or("".to_string())); + let mut page = use_signal(|| page.unwrap_or(1)); let page_size = use_signal::(|| 100); - let max_page = use_signal(|| 1 as i32); - let item_count = use_signal(|| 0 as i32); + let mut max_page = use_signal(|| 1 as i32); + let mut item_count = use_signal(|| 0 as i32); rsx! { div { class: "stickyTop", - Search { query, page }, - Pagination { page, max_page, item_count }, + Search { + query: query(), + oninput: move |q| { + query.set(q); + page.set(1); + } + }, + Pagination { + page, max_page, item_count, + on_page_change: move |p|{ + page.set(p); + } + }, + } + MediaGrid { query: query, page: page, max_page, total_items: item_count, page_size, + on_page_loaded: move |p: PaginationInfo| { + max_page.set(p.total_pages); + item_count.set(p.total_items); + } } - MediaGrid { query: query, page: page, max_page, total_items: item_count, page_size } } } diff --git a/AobaClient/src/views/media.rs b/AobaClient/src/views/media.rs index d7aaa61..713285c 100644 --- a/AobaClient/src/views/media.rs +++ b/AobaClient/src/views/media.rs @@ -1,6 +1,4 @@ use crate::HOST; -use crate::components::radio_group::{RadioGroup, RadioItem}; -use crate::rpc::aoba::SetMediaClassRequest; use crate::rpc::{ aoba::{Id, MediaModel}, get_rpc_client, @@ -8,26 +6,20 @@ use crate::rpc::{ use dioxus::prelude::*; #[component] -pub fn Media(id: String) -> Element -{ +pub fn Media(id: String) -> Element { let media_result = use_resource(use_reactive!(|(id)| async move { let mut client = get_rpc_client(); let result = client.get_media(Id { value: id.clone() }).await; - if let Ok(item) = result - { + if let Ok(item) = result { let res = item.into_inner(); return res.value; - } - else - { + } else { return None; } })); - return match media_result.cloned().unwrap_or(None) - { - Some(media) => - { + return match media_result.cloned().unwrap_or(None) { + Some(media) => { return rsx! {MediaPage{media: media}}; } None => rsx! {"Not Found"}, @@ -35,12 +27,10 @@ pub fn Media(id: String) -> Element } #[component] -fn MediaPage(media: MediaModel) -> Element -{ +fn MediaPage(media: MediaModel) -> Element { let url = media.thumb_url; let id = media.id.expect("Media has no id").value.clone(); - let cur_class = use_signal(|| match media.class - { + let cur_class = use_signal(|| match media.class { 0 => "Standard", 1 => "NSFW", 2 => "Secret",