From 41aa78b67228cd67183a3adc61e56229280982e9 Mon Sep 17 00:00:00 2001 From: Amatsugu Date: Sun, 28 Dec 2025 14:29:46 -0500 Subject: [PATCH] added pagination controls --- AobaClient/assets/style/main.scss | 21 +++++++++++++++ AobaClient/src/components/media_grid.rs | 36 ++++++++++++++++--------- AobaClient/src/components/mod.rs | 2 ++ AobaClient/src/components/navbar.rs | 2 +- AobaClient/src/components/pagination.rs | 30 +++++++++++++++++++++ AobaClient/src/components/search.rs | 4 +-- AobaClient/src/route.rs | 7 +++-- AobaClient/src/views/home.rs | 11 +++++--- AobaClient/src/views/media.rs | 8 ++++++ AobaClient/src/views/mod.rs | 2 ++ AobaCore/Models/PagedResult.cs | 6 ++--- AobaCore/Services/AobaService.cs | 2 +- AobaServer/Proto/Types.proto | 4 +-- 13 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 AobaClient/src/components/pagination.rs create mode 100644 AobaClient/src/views/media.rs diff --git a/AobaClient/assets/style/main.scss b/AobaClient/assets/style/main.scss index 96d1289..ce731cf 100644 --- a/AobaClient/assets/style/main.scss +++ b/AobaClient/assets/style/main.scss @@ -195,3 +195,24 @@ form { } } } + +.pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 5; + padding: 2px; + + a { + transition: all 0.25s ease-out; + cursor: pointer; + user-select: none; + padding: 5px 10px; + background-color: $featureColor; + border-radius: 4px; + + &:hover { + background-color: $focusColor; + } + } +} diff --git a/AobaClient/src/components/media_grid.rs b/AobaClient/src/components/media_grid.rs index 4d947c8..4281a55 100644 --- a/AobaClient/src/components/media_grid.rs +++ b/AobaClient/src/components/media_grid.rs @@ -9,6 +9,8 @@ use crate::{ #[derive(PartialEq, Clone, Props)] pub struct MediaGridProps { pub query: Option, + pub max_page: Signal, + pub total_items: Signal, #[props(default = Some(1))] pub page: Option, #[props(default = Some(100))] @@ -33,12 +35,13 @@ impl Into for MediaGridProps { } #[component] -pub fn MediaGrid(props: MediaGridProps) -> Element { +pub fn MediaGrid(mut props: MediaGridProps) -> Element { let media_result = use_resource(use_reactive!(|(props)| async move { let mut client = get_rpc_client(); let result = client.list_media(props.into_request()).await; if let Ok(items) = result { - return Ok(items.into_inner()); + let res = items.into_inner(); + return Ok(res); } else { let err = result.err().unwrap(); let message = err.message(); @@ -48,17 +51,24 @@ pub fn MediaGrid(props: MediaGridProps) -> Element { match media_result.cloned() { Some(value) => match value { - Ok(result) => rsx! { - div { - class: "mediaGrid", - // oncontextmenu: oncontext, - {result.items.iter().map(|itm| rsx!{ - MediaItem { - item: itm.clone() - } - })}, - } - }, + Ok(result) => { + let pagination = result.pagination.unwrap(); + 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)); + return rsx! { + div { + class: "mediaGrid", + // oncontextmenu: oncontext, + {result.items.iter().map(|itm| rsx!{ + MediaItem { + item: itm.clone() + } + })}, + } + }; + } Err(msg) => rsx! { div { class: "mediaGrid", diff --git a/AobaClient/src/components/mod.rs b/AobaClient/src/components/mod.rs index ad908bf..23e479f 100644 --- a/AobaClient/src/components/mod.rs +++ b/AobaClient/src/components/mod.rs @@ -5,6 +5,7 @@ mod media_item; mod metrics_token; mod navbar; mod notif; +mod pagination; mod search; pub use context_menu::*; pub use media_grid::*; @@ -12,5 +13,6 @@ pub use media_item::*; pub use metrics_token::*; pub use navbar::*; pub use notif::*; +pub use pagination::*; pub use search::*; mod icons; diff --git a/AobaClient/src/components/navbar.rs b/AobaClient/src/components/navbar.rs index f55063e..90855fa 100644 --- a/AobaClient/src/components/navbar.rs +++ b/AobaClient/src/components/navbar.rs @@ -22,7 +22,7 @@ pub fn Navbar() -> Element { pub fn MainNaviagation() -> Element { rsx! { div { class: "mainNav", - Link { class: "navItem", to: Route::Home {}, "Home" } + Link { class: "navItem", to: Route::Home { }, "Home" } Link { class: "navItem", to: Route::Settings {}, "Settings" } } } diff --git a/AobaClient/src/components/pagination.rs b/AobaClient/src/components/pagination.rs new file mode 100644 index 0000000..a66638e --- /dev/null +++ b/AobaClient/src/components/pagination.rs @@ -0,0 +1,30 @@ +use dioxus::prelude::*; + +#[component] +pub fn Pagination(page: Signal, max_page: Signal, item_count: Signal) -> Element { + let cur_page_val = page.cloned(); + let max_page_val = max_page.cloned(); + let item_count_val = item_count.cloned(); + rsx! { + div { + class: "pagination", + a { + onclick: move|_| page.set(1), + "First" + } + a { + onclick: move|_| page.set((cur_page_val - 1).max(1)), + "Prev" + } + div { "Page {cur_page_val} of {max_page_val} ({item_count_val} Media Items)" } + a { + onclick: move|_| page.set((cur_page_val + 1).min(max_page_val)), + "Next" + } + a { + onclick: move|_| page.set(max_page_val), + "Last" + } + } + } +} diff --git a/AobaClient/src/components/search.rs b/AobaClient/src/components/search.rs index d4745ac..9c9310b 100644 --- a/AobaClient/src/components/search.rs +++ b/AobaClient/src/components/search.rs @@ -1,14 +1,14 @@ use dioxus::prelude::*; #[component] -pub fn Search(query: Signal) -> Element { +pub fn Search(query: Signal, page: Signal) -> Element { rsx! { div { class: "searchBar stickyTop", input { r#type: "search", placeholder: "Search Files", value: query, - oninput: move |event| query.set(event.value()), + oninput: move |event| {query.set(event.value()); page.set(1);}, } } } diff --git a/AobaClient/src/route.rs b/AobaClient/src/route.rs index 7c0fd0a..f018fe5 100644 --- a/AobaClient/src/route.rs +++ b/AobaClient/src/route.rs @@ -1,6 +1,6 @@ use crate::{ layouts::MainLayout, - views::{Home, Settings}, + views::{Home, Media, Settings}, }; use dioxus::prelude::*; @@ -8,8 +8,11 @@ use dioxus::prelude::*; #[rustfmt::skip] pub enum Route { #[layout(MainLayout)] + #[route("/")] - Home {}, + Home { }, + #[route("/media/:id")] + Media { id: String }, #[route("/settings")] Settings {}, // #[end_layout] diff --git a/AobaClient/src/views/home.rs b/AobaClient/src/views/home.rs index d0b976c..284aea5 100644 --- a/AobaClient/src/views/home.rs +++ b/AobaClient/src/views/home.rs @@ -1,12 +1,15 @@ -use crate::components::{MediaGrid, Search}; +use crate::components::{MediaGrid, Pagination, Search}; use dioxus::prelude::*; #[component] pub fn Home() -> Element { let query = use_signal(|| "".to_string()); - + let page = use_signal(|| 1 as i32); + let max_page = use_signal(|| 1 as i32); + let item_count = use_signal(|| 0 as i32); rsx! { - Search { query } - MediaGrid { query: query.cloned() } + Search { query, page }, + Pagination { page, max_page, item_count }, + MediaGrid { query: query.cloned(), page: page.cloned(), max_page, total_items: item_count } } } diff --git a/AobaClient/src/views/media.rs b/AobaClient/src/views/media.rs new file mode 100644 index 0000000..cc4d27a --- /dev/null +++ b/AobaClient/src/views/media.rs @@ -0,0 +1,8 @@ +use dioxus::prelude::*; + +#[component] +pub fn Media(id: String) -> Element { + rsx! { + {id} + } +} diff --git a/AobaClient/src/views/mod.rs b/AobaClient/src/views/mod.rs index 02615f2..4d2a211 100644 --- a/AobaClient/src/views/mod.rs +++ b/AobaClient/src/views/mod.rs @@ -1,7 +1,9 @@ mod home; mod login; +mod media; pub use home::*; pub use login::*; +pub use media::*; mod settings; pub use settings::Settings; diff --git a/AobaCore/Models/PagedResult.cs b/AobaCore/Models/PagedResult.cs index ca6b80c..c33ddd7 100644 --- a/AobaCore/Models/PagedResult.cs +++ b/AobaCore/Models/PagedResult.cs @@ -5,13 +5,13 @@ using System.Text; using System.Threading.Tasks; namespace AobaCore.Models; -public class PagedResult(List items, int page, int pageSize, long totalItems) +public class PagedResult(List items, int page, int pageSize, int totalItems) { public List Items { get; set; } = items; public int Page { get; set; } = page; public int PageSize { get; set; } = pageSize; - public long TotalItems { get; set; } = totalItems; - public long TotalPages { get; set; } = totalItems / pageSize; + public int TotalItems { get; set; } = totalItems; + public int TotalPages { get; set; } = (totalItems / pageSize) + 1; public string? Query { get; set; } } diff --git a/AobaCore/Services/AobaService.cs b/AobaCore/Services/AobaService.cs index a88c46a..66591cf 100644 --- a/AobaCore/Services/AobaService.cs +++ b/AobaCore/Services/AobaService.cs @@ -37,7 +37,7 @@ public class AobaService(IMongoDatabase db) var total = await find.CountDocumentsAsync(); page -= 1; var items = await find.Sort(sort).Skip(page * pageSize).Limit(pageSize).ToListAsync(); - return new PagedResult(items, page, pageSize, total); + return new PagedResult(items, page, pageSize, (int)total); } public async Task> FindMediaWithExtAsync(string ext, CancellationToken cancellationToken = default) diff --git a/AobaServer/Proto/Types.proto b/AobaServer/Proto/Types.proto index 4caeb13..0f97c26 100644 --- a/AobaServer/Proto/Types.proto +++ b/AobaServer/Proto/Types.proto @@ -54,8 +54,8 @@ message ListResponse { message Pagination { int32 page = 1; int32 pageSize = 2; - int64 totalPages = 3; - int64 totalItems = 4; + int32 totalPages = 3; + int32 totalItems = 4; optional string query = 5; }