diff options
author | metamuffin <metamuffin@disroot.org> | 2023-08-07 11:10:10 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-08-07 11:59:59 +0200 |
commit | dc7ed1ccaa5e727b3ab0569fd7fe56a2d0084bd5 (patch) | |
tree | eb724ec5c0de92abef03549365eba01e07c06d90 /server/src/routes/ui | |
parent | 55eba6fdc6742d2850d003059220284bace059fc (diff) | |
download | jellything-dc7ed1ccaa5e727b3ab0569fd7fe56a2d0084bd5.tar jellything-dc7ed1ccaa5e727b3ab0569fd7fe56a2d0084bd5.tar.bz2 jellything-dc7ed1ccaa5e727b3ab0569fd7fe56a2d0084bd5.tar.zst |
node sort ui
Diffstat (limited to 'server/src/routes/ui')
-rw-r--r-- | server/src/routes/ui/mod.rs | 1 | ||||
-rw-r--r-- | server/src/routes/ui/node.rs | 25 | ||||
-rw-r--r-- | server/src/routes/ui/sort.rs | 95 |
3 files changed, 116 insertions, 5 deletions
diff --git a/server/src/routes/ui/mod.rs b/server/src/routes/ui/mod.rs index d561627..07098c8 100644 --- a/server/src/routes/ui/mod.rs +++ b/server/src/routes/ui/mod.rs @@ -31,6 +31,7 @@ pub mod layout; pub mod node; pub mod player; pub mod style; +pub mod sort; pub struct HtmlTemplate<'a>(pub markup::DynRender<'a>); diff --git a/server/src/routes/ui/node.rs b/server/src/routes/ui/node.rs index 301f89c..ba16c73 100644 --- a/server/src/routes/ui/node.rs +++ b/server/src/routes/ui/node.rs @@ -3,7 +3,12 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin <metamuffin.org> */ -use super::{assets::rocket_uri_macro_r_item_assets, error::MyError, player::player_uri}; +use super::{ + assets::rocket_uri_macro_r_item_assets, + error::MyError, + player::player_uri, + sort::{filter_and_sort_nodes, NodeFilterSort, NodeFilterSortForm}, +}; use crate::{ database::Database, routes::{ @@ -20,12 +25,19 @@ use anyhow::{anyhow, Context}; use jellycommon::{MediaInfo, NodeKind, NodePublic, Rating, SourceTrackKind}; use rocket::{get, serde::json::Json, Either, State}; +/// This function is a stub and only useful for use in the uri! macro. #[get("/n/<id>")] -pub async fn r_library_node( +pub fn r_library_node(id: String) -> () { + drop(id) +} + +#[get("/n/<id>?<filter..>")] +pub async fn r_library_node_filter( _sess: Session, id: String, db: &State<Database>, aj: AcceptJson, + filter: NodeFilterSort, ) -> Result<Either<DynLayoutPage<'_>, Json<NodePublic>>, MyError> { let node = db .node @@ -38,7 +50,7 @@ pub async fn r_library_node( return Ok(Either::Right(Json(node))); } - let children = node + let mut children = node .children .iter() .map(|c| { @@ -54,11 +66,13 @@ pub async fn r_library_node( .into_iter() .collect(); + filter_and_sort_nodes(&filter, &mut children); + Ok(Either::Left(LayoutPage { title: node.title.to_string(), show_back: true, //- !matches!(node.kind, NodeKind::Collection), content: markup::new! { - @NodePage { node: &node, id: &id, children: &children } + @NodePage { node: &node, id: &id, children: &children, filter: &filter } }, ..Default::default() })) @@ -88,7 +102,7 @@ markup::define! { } } } - NodePage<'a>(id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>) { + NodePage<'a>(id: &'a str, node: &'a NodePublic, children: &'a Vec<(String, NodePublic)>, filter: &'a NodeFilterSort) { @if !matches!(node.kind, NodeKind::Collection) { img.backdrop[src=uri!(r_item_assets(id, AssetRole::Backdrop, Some(2048)))]; } @@ -110,6 +124,7 @@ markup::define! { @if let NodeKind::Collection = node.kind { @if let Some(parent) = &node.parent { a.dirup[href=uri!(r_library_node(parent))] { "Go up" } + @NodeFilterSortForm { f: filter } } } @match node.kind { diff --git a/server/src/routes/ui/sort.rs b/server/src/routes/ui/sort.rs new file mode 100644 index 0000000..3ab6cef --- /dev/null +++ b/server/src/routes/ui/sort.rs @@ -0,0 +1,95 @@ +use jellycommon::{NodeKind, NodePublic}; +use rocket::{ + http::uri::fmt::{Query, UriDisplay}, + FromForm, FromFormField, UriDisplayQuery, +}; + +#[derive(FromForm, UriDisplayQuery)] +pub struct NodeFilterSort { + sort_by: Option<SortProperty>, + filter_kind: Option<Vec<NodeKind>>, + sort_order: Option<SortOrder>, +} + +#[rustfmt::skip] +#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)] +enum SortProperty { + #[field(value = "release_date")] ReleaseDate, + #[field(value = "title")] Title, +} + +#[rustfmt::skip] +#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)] +enum SortOrder { + #[field(value = "ascending")] Ascending, + #[field(value = "descending")] Descending, +} + +pub fn filter_and_sort_nodes(f: &NodeFilterSort, nodes: &mut Vec<(String, NodePublic)>) { + nodes.retain(|(_id, node)| { + let mut o = true; + if let Some(kind) = &f.filter_kind { + o &= kind.contains(&node.kind) + } + o + }); + if let Some(sort_prop) = f.sort_by { + match sort_prop { + SortProperty::ReleaseDate => nodes.sort_by_key(|(_, _n)| 0), // TODO + SortProperty::Title => nodes.sort_by(|(_, a), (_, b)| a.title.cmp(&b.title)), + } + } + match f.sort_order.unwrap_or(SortOrder::Ascending) { + SortOrder::Ascending => (), + SortOrder::Descending => nodes.reverse(), + } +} + +markup::define! { + NodeFilterSortForm<'a>(f: &'a NodeFilterSort) { + details { + summary { "Filter and Sort" } + form[method="GET", action=""] { + fieldset { + legend { "Filter" } + @use NodeKind::*; + @for (value, label) in [(Movie, "Movie"), (Episode, "Episode"), (Video, "Video"), (Channel, "Channel")] { + label { input[type="checkbox", name="filter_kind", value=A(value), checked=f.filter_kind.as_ref().map(|k|k.contains(&value)).unwrap_or(true)]; @label } br; + } + } + fieldset { + legend { "Sort By" } + @use SortProperty::*; + @for (value, label) in [(Title, "Title"), (ReleaseDate, "Release Date")] { + label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by]; @label } br; + } + } + fieldset { + legend { "Sort Order" } + @use SortOrder::*; + @for (value, label) in [(Ascending, "Ascending"), (Descending, "Descending")] { + label { input[type="radio", name="sort_order", value=value, checked=Some(value)==f.sort_order]; @label } br; + } + } + input[type="submit", value="Apply"]; + } + } + } +} + +impl markup::Render for SortProperty { + fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result { + writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>)) + } +} +impl markup::Render for SortOrder { + fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result { + writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>)) + } +} +struct A(NodeKind); +impl markup::Render for A { + fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result { + writer.write_fmt(format_args!("{}", &self.0 as &dyn UriDisplay<Query>)) + } +} |