rewrite components such that data always flows downwards

This commit is contained in:
2026-04-05 17:03:19 -04:00
parent 44425723c6
commit ea9ad2f8a7
6 changed files with 95 additions and 79 deletions
+26 -27
View File
@@ -9,18 +9,22 @@ use crate::{
};
#[derive(PartialEq, Clone, Props)]
pub struct MediaGridProps
{
pub struct MediaGridProps {
pub query: Signal<String>,
pub max_page: Signal<i32>,
pub total_items: Signal<i32>,
pub page: Signal<i32>,
pub page_size: Signal<i32>,
pub on_page_loaded: Option<EventHandler<PaginationInfo>>,
}
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<MediaModel>) -> Element
{
fn MediaList(items: Vec<MediaModel>) -> Element {
rsx! {
{items.iter().map(|itm| rsx!{
{items.iter().enumerate().map(|(index, itm)| rsx!{
MediaItem {
item: itm.clone()
item: itm.clone(),
index
}
})}
}
+16 -11
View File
@@ -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<EventHandler<MediaClassChangeEvent>>,
pub on_deleted: Option<EventHandler<usize>>,
}
#[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<Response<()>, Status>
{
async fn set_class(id: String, class: i32) -> Result<Response<()>, Status> {
let mut client = get_rpc_client();
return client
.set_media_class(SetMediaClassRequest {
+15 -12
View File
@@ -2,8 +2,12 @@ use dioxus::prelude::*;
use web_sys::window;
#[component]
pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i32>) -> Element
{
pub fn Pagination(
page: Signal<i32>,
max_page: Signal<i32>,
item_count: Signal<i32>,
on_page_change: EventHandler<i32>,
) -> 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<i32>, max_page: Signal<i32>, item_count: Signal<i
class: "pagination",
a {
onclick: move|_| {
page.set(1);
on_page_change();
on_page_change.call(1);
scroll_document();
},
"First"
}
a {
onclick: move|_| {
let p = (cur_page_val - 1).max(1);
page.set(p);
on_page_change();
on_page_change.call(p);
scroll_document();
},
"Prev"
}
@@ -29,15 +33,15 @@ pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i
a {
onclick: move|_| {
let p = (cur_page_val + 1).min(max_page_val);
page.set(p);
on_page_change();
on_page_change.call(p);
scroll_document();
},
"Next"
}
a {
onclick: move|_| {
page.set(max_page_val);
on_page_change();
on_page_change.call(max_page_val);
scroll_document();
},
"Last"
}
@@ -45,8 +49,7 @@ pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i
}
}
fn on_page_change()
{
fn scroll_document() {
let window = window().expect("Failed to get window");
let document = window.document().expect("Failed to get document");
document
+6 -2
View File
@@ -1,14 +1,18 @@
use dioxus::prelude::*;
#[component]
pub fn Search(query: Signal<String>, page: Signal<i32>) -> Element {
pub fn Search(query: String, oninput: Option<EventHandler<String>>) -> 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());
}
},
}
}
}
+25 -10
View File
@@ -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<i32>, q: Option<String>) -> Element
{
let query = use_signal(|| q.unwrap_or("".to_string()));
let page = use_signal(|| page.unwrap_or(1));
pub fn Home(page: Option<i32>, q: Option<String>) -> 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::<i32>(|| 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 }
}
}
+7 -17
View File
@@ -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",