From 31cce6db5373ee99ef4c4c17ddf27b81040017eb Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sat, 31 May 2025 14:37:07 +0200 Subject: Fix nodefiltersort fromform instance very badly --- locale/en.ini | 3 + server/src/helper/filter_sort.rs | 136 ++++++++++++++++++++++++++++++++++++--- server/src/ui/items.rs | 10 +-- server/src/ui/node.rs | 9 +-- ui/src/admin/user.rs | 14 ++-- ui/src/lib.rs | 1 - 6 files changed, 148 insertions(+), 25 deletions(-) diff --git a/locale/en.ini b/locale/en.ini index 6594832..25db0ca 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -112,6 +112,9 @@ admin.import_success=Import finished admin.update_search_success=Search index updated admin.users.remove_success=User removed admin.users.permission_update_success=Permissions updated +admin.users.title=User Management +admin.users.user_list=All Users +admin.users.return_to_list=Back to user list page.curr=Page {cur} of {max} page.prev=Previous page diff --git a/server/src/helper/filter_sort.rs b/server/src/helper/filter_sort.rs index b30ff18..10d397a 100644 --- a/server/src/helper/filter_sort.rs +++ b/server/src/helper/filter_sort.rs @@ -6,22 +6,59 @@ use super::A; use jellycommon::{ - api::NodeFilterSort, + api::{FilterProperty, NodeFilterSort, SortOrder, SortProperty}, user::{PlayerKind, Theme}, }; use rocket::{ async_trait, - form::{DataField, FromFormField, Result, ValueField}, + form::{DataField, FromForm, FromFormField, Result, ValueField}, + UriDisplayQuery, }; -#[async_trait] -impl<'v> FromFormField<'v> for A { - fn from_value(field: ValueField<'v>) -> Result<'v, Self> { - // TODO - Ok(A(NodeFilterSort::default())) - } - async fn from_data(field: DataField<'v, '_>) -> Result<'v, Self> { - Ok(A(NodeFilterSort::default())) +impl Into for ANodeFilterSort { + fn into(self) -> NodeFilterSort { + NodeFilterSort { + sort_by: self.sort_by.map(|e| match e { + ASortProperty::ReleaseDate => SortProperty::ReleaseDate, + ASortProperty::Title => SortProperty::Title, + ASortProperty::Index => SortProperty::Index, + ASortProperty::Duration => SortProperty::Duration, + ASortProperty::RatingRottenTomatoes => SortProperty::RatingRottenTomatoes, + ASortProperty::RatingMetacritic => SortProperty::RatingMetacritic, + ASortProperty::RatingImdb => SortProperty::RatingImdb, + ASortProperty::RatingTmdb => SortProperty::RatingTmdb, + ASortProperty::RatingYoutubeViews => SortProperty::RatingYoutubeViews, + ASortProperty::RatingYoutubeLikes => SortProperty::RatingYoutubeLikes, + ASortProperty::RatingYoutubeFollowers => SortProperty::RatingYoutubeFollowers, + ASortProperty::RatingUser => SortProperty::RatingUser, + ASortProperty::RatingLikesDivViews => SortProperty::RatingLikesDivViews, + }), + filter_kind: self.filter_kind.map(|l| { + l.into_iter() + .map(|e| match e { + AFilterProperty::FederationLocal => FilterProperty::FederationLocal, + AFilterProperty::FederationRemote => FilterProperty::FederationRemote, + AFilterProperty::Watched => FilterProperty::Watched, + AFilterProperty::Unwatched => FilterProperty::Unwatched, + AFilterProperty::WatchProgress => FilterProperty::WatchProgress, + AFilterProperty::KindMovie => FilterProperty::KindMovie, + AFilterProperty::KindVideo => FilterProperty::KindVideo, + AFilterProperty::KindShortFormVideo => FilterProperty::KindShortFormVideo, + AFilterProperty::KindMusic => FilterProperty::KindMusic, + AFilterProperty::KindCollection => FilterProperty::KindCollection, + AFilterProperty::KindChannel => FilterProperty::KindChannel, + AFilterProperty::KindShow => FilterProperty::KindShow, + AFilterProperty::KindSeries => FilterProperty::KindSeries, + AFilterProperty::KindSeason => FilterProperty::KindSeason, + AFilterProperty::KindEpisode => FilterProperty::KindEpisode, + }) + .collect() + }), + sort_order: self.sort_order.map(|e| match e { + ASortOrder::Ascending => SortOrder::Ascending, + ASortOrder::Descending => SortOrder::Descending, + }), + } } } @@ -44,3 +81,82 @@ impl<'v> FromFormField<'v> for A { Err(field.unexpected())? } } + +#[derive(FromForm, UriDisplayQuery, Clone)] +pub struct ANodeFilterSort { + sort_by: Option, + filter_kind: Option>, + sort_order: Option, +} + +#[derive(FromFormField, UriDisplayQuery, Clone)] +enum AFilterProperty { + #[field(value = "fed_local")] + FederationLocal, + #[field(value = "fed_remote")] + FederationRemote, + #[field(value = "watched")] + Watched, + #[field(value = "unwatched")] + Unwatched, + #[field(value = "watch_progress")] + WatchProgress, + #[field(value = "kind_movie")] + KindMovie, + #[field(value = "kind_video")] + KindVideo, + #[field(value = "kind_short_form_video")] + KindShortFormVideo, + #[field(value = "kind_music")] + KindMusic, + #[field(value = "kind_collection")] + KindCollection, + #[field(value = "kind_channel")] + KindChannel, + #[field(value = "kind_show")] + KindShow, + #[field(value = "kind_series")] + KindSeries, + #[field(value = "kind_season")] + KindSeason, + #[field(value = "kind_episode")] + KindEpisode, +} + +#[derive(FromFormField, UriDisplayQuery, Clone)] +enum ASortProperty { + #[field(value = "release_date")] + ReleaseDate, + #[field(value = "title")] + Title, + #[field(value = "index")] + Index, + #[field(value = "duration")] + Duration, + #[field(value = "rating_rt")] + RatingRottenTomatoes, + #[field(value = "rating_mc")] + RatingMetacritic, + #[field(value = "rating_imdb")] + RatingImdb, + #[field(value = "rating_tmdb")] + RatingTmdb, + #[field(value = "rating_yt_views")] + RatingYoutubeViews, + #[field(value = "rating_yt_likes")] + RatingYoutubeLikes, + #[field(value = "rating_yt_followers")] + RatingYoutubeFollowers, + #[field(value = "rating_user")] + RatingUser, + #[field(value = "rating_loved")] + RatingLikesDivViews, +} + +#[derive(FromFormField, UriDisplayQuery, Clone)] +enum ASortOrder { + #[field(value = "ascending")] + Ascending, + #[field(value = "descending")] + Descending, +} diff --git a/server/src/ui/items.rs b/server/src/ui/items.rs index 915963c..ace948f 100644 --- a/server/src/ui/items.rs +++ b/server/src/ui/items.rs @@ -4,8 +4,8 @@ Copyright (C) 2025 metamuffin */ use super::error::MyError; -use crate::helper::{accept::Accept, RequestInfo, A}; -use jellycommon::api::{ApiItemsResponse, NodeFilterSort}; +use crate::helper::{accept::Accept, filter_sort::ANodeFilterSort, RequestInfo}; +use jellycommon::api::ApiItemsResponse; use jellylogic::items::all_items; use jellyui::{items::ItemsPage, render_page}; use rocket::{get, response::content::RawHtml, serde::json::Json, Either}; @@ -14,9 +14,9 @@ use rocket::{get, response::content::RawHtml, serde::json::Json, Either}; pub fn r_items( ri: RequestInfo, page: Option, - filter: A, + filter: ANodeFilterSort, ) -> Result, Json>, MyError> { - let r = all_items(&ri.session, page, filter.0.clone())?; + let r = all_items(&ri.session, page, filter.clone().into())?; Ok(if matches!(ri.accept, Accept::Json) { Either::Right(Json(r)) } else { @@ -24,7 +24,7 @@ pub fn r_items( &ItemsPage { lang: &ri.lang, r, - filter: &filter.0, + filter: &filter.clone().into(), page: page.unwrap_or(0), }, ri.render_info(), diff --git a/server/src/ui/node.rs b/server/src/ui/node.rs index 7085a5a..5004b7b 100644 --- a/server/src/ui/node.rs +++ b/server/src/ui/node.rs @@ -4,7 +4,7 @@ Copyright (C) 2025 metamuffin */ use super::error::MyResult; -use crate::helper::{RequestInfo, A}; +use crate::helper::{filter_sort::ANodeFilterSort, RequestInfo, A}; use jellycommon::{ api::{ApiNodeResponse, NodeFilterSort}, NodeID, @@ -17,10 +17,11 @@ use rocket::{get, response::content::RawHtml, serde::json::Json, Either}; pub async fn r_node<'a>( ri: RequestInfo, id: A, - filter: Option>, + filter: Option, parents: bool, children: bool, ) -> MyResult, Json>> { + let filter: Option = filter.map(Into::into); let filter = filter.unwrap_or_default(); let r = get_node( @@ -28,7 +29,7 @@ pub async fn r_node<'a>( id.0, !ri.accept.is_json() || children, !ri.accept.is_json() || parents, - filter.0.clone(), + filter.clone(), )?; Ok(if ri.accept.is_json() { @@ -41,7 +42,7 @@ pub async fn r_node<'a>( children: &r.children, parents: &r.parents, similar: &[], - filter: &filter.0, + filter: &filter, lang: &ri.lang, player: false, }, diff --git a/ui/src/admin/user.rs b/ui/src/admin/user.rs index 225482e..919d045 100644 --- a/ui/src/admin/user.rs +++ b/ui/src/admin/user.rs @@ -4,7 +4,11 @@ Copyright (C) 2025 metamuffin */ -use crate::{FlashM, Page, locale::Language, scaffold::FlashDisplay}; +use crate::{ + FlashM, Page, + locale::{Language, tr, trs}, + scaffold::FlashDisplay, +}; use jellycommon::{ routes::{u_admin_user, u_admin_user_permission, u_admin_user_remove, u_admin_users}, user::{PermissionSet, User, UserPermission}, @@ -29,9 +33,9 @@ impl Page for AdminUsersPage<'_> { markup::define! { AdminUsersPage<'a>(lang: &'a Language, users: &'a [User], flash: &'a FlashM) { - h1 { "User Management" } + h1 { @trs(lang, "admin.users.title") } @FlashDisplay { flash } - h2 { "All Users" } + h2 { @trs(lang, "admin.users.user_list") } ul { @for u in *users { li { a[href=u_admin_user(&u.name)] { @format!("{:?}", u.display_name) " (" @u.name ")" } @@ -40,7 +44,7 @@ markup::define! { } AdminUserPage<'a>(lang: &'a Language, user: &'a User, flash: &'a FlashM) { h1 { @format!("{:?}", user.display_name) " (" @user.name ")" } - a[href=u_admin_users()] "Back to the User List" + a[href=u_admin_users()] { @trs(lang, "admin.users.return_to_list") } @FlashDisplay { flash } form[method="POST", action=u_admin_user_remove(&user.name)] { // input[type="text", name="name", value=&user.name, hidden]; @@ -62,7 +66,7 @@ markup::define! { } } fieldset.perms { - legend { "Permission" } + legend { "State" } label { input[type="radio", name="action", value="unset"]; "Unset" } br; label { input[type="radio", name="action", value="grant"]; "Grant" } br; label { input[type="radio", name="action", value="revoke"]; "Revoke" } br; diff --git a/ui/src/lib.rs b/ui/src/lib.rs index cbf15bf..786646d 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -19,7 +19,6 @@ pub mod search; pub mod stats; use jellycommon::user::Theme; -use locale::Language; use markup::DynRender; use scaffold::{RenderInfo, Scaffold}; use serde::{Deserialize, Serialize}; -- cgit v1.2.3-70-g09d2