diff options
Diffstat (limited to 'server/src/routes/ui/sort.rs')
-rw-r--r-- | server/src/routes/ui/sort.rs | 290 |
1 files changed, 0 insertions, 290 deletions
diff --git a/server/src/routes/ui/sort.rs b/server/src/routes/ui/sort.rs deleted file mode 100644 index 6d38e11..0000000 --- a/server/src/routes/ui/sort.rs +++ /dev/null @@ -1,290 +0,0 @@ -use jellybase::locale::Language; -use jellycommon::{helpers::SortAnyway, user::NodeUserData, Node, NodeKind, Rating}; -use markup::RenderAttributeValue; -use rocket::{ - http::uri::fmt::{Query, UriDisplay}, - FromForm, FromFormField, UriDisplayQuery, -}; -use std::sync::Arc; - -use crate::routes::ui::layout::trs; - -#[derive(FromForm, UriDisplayQuery, Default, Clone)] -pub struct NodeFilterSort { - pub sort_by: Option<SortProperty>, - pub filter_kind: Option<Vec<FilterProperty>>, - pub sort_order: Option<SortOrder>, -} - -macro_rules! form_enum { - (enum $i:ident { $($vi:ident = $vk:literal),*, }) => { - #[derive(Debug, FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)] - pub enum $i { $(#[field(value = $vk)] $vi),* } - impl $i { #[allow(unused)] const ALL: &'static [$i] = &[$($i::$vi),*]; } - }; -} - -form_enum!( - enum FilterProperty { - FederationLocal = "fed_local", - FederationRemote = "fed_remote", - Watched = "watched", - Unwatched = "unwatched", - WatchProgress = "watch_progress", - KindMovie = "kind_movie", - KindVideo = "kind_video", - KindShortFormVideo = "kind_short_form_video", - KindMusic = "kind_music", - KindCollection = "kind_collection", - KindChannel = "kind_channel", - KindShow = "kind_show", - KindSeries = "kind_series", - KindSeason = "kind_season", - KindEpisode = "kind_episode", - } -); - -form_enum!( - enum SortProperty { - ReleaseDate = "release_date", - Title = "title", - Index = "index", - Duration = "duration", - RatingRottenTomatoes = "rating_rt", - RatingMetacritic = "rating_mc", - RatingImdb = "rating_imdb", - RatingTmdb = "rating_tmdb", - RatingYoutubeViews = "rating_yt_views", - RatingYoutubeLikes = "rating_yt_likes", - RatingYoutubeFollowers = "rating_yt_followers", - RatingUser = "rating_user", - RatingLikesDivViews = "rating_loved", - } -); - -impl SortProperty { - const CATS: &'static [(&'static str, &'static [(SortProperty, &'static str)])] = { - use SortProperty::*; - &[ - ( - "filter_sort.sort.general", - &[(Title, "node.title"), (ReleaseDate, "node.release_date")], - ), - ("filter_sort.sort.media", &[(Duration, "media.runtime")]), - ( - "filter_sort.sort.rating", - &[ - (RatingImdb, "rating.imdb"), - (RatingTmdb, "rating.tmdb"), - (RatingMetacritic, "rating.metacritic"), - (RatingRottenTomatoes, "rating.rotten_tomatoes"), - (RatingYoutubeFollowers, "rating.youtube_followers"), - (RatingYoutubeLikes, "rating.youtube_likes"), - (RatingYoutubeViews, "rating.youtube_views"), - (RatingUser, "filter_sort.sort.rating.user"), - ( - RatingLikesDivViews, - "filter_sort.sort.rating.likes_div_views", - ), - ], - ), - ] - }; -} -impl FilterProperty { - const CATS: &'static [(&'static str, &'static [(FilterProperty, &'static str)])] = { - use FilterProperty::*; - &[ - ( - "filter_sort.filter.kind", - &[ - (KindMovie, "kind.movie"), - (KindVideo, "kind.video"), - (KindShortFormVideo, "kind.short_form_video"), - (KindMusic, "kind.music"), - (KindCollection, "kind.collection"), - (KindChannel, "kind.channel"), - (KindShow, "kind.show"), - (KindSeries, "kind.series"), - (KindSeason, "kind.season"), - (KindEpisode, "kind.episode"), - ], - ), - ( - "filter_sort.filter.federation", - &[(FederationLocal, "federation.local"), (FederationRemote, "federation.remote")], - ), - ( - "filter_sort.filter.watched", - &[ - (Watched, "watched.watched"), - (Unwatched, "watched.none"), - (WatchProgress, "watched.progress"), - ], - ), - ] - }; -} - -impl NodeFilterSort { - pub fn is_open(&self) -> bool { - self.filter_kind.is_some() || self.sort_by.is_some() - } -} - -#[rustfmt::skip] -#[derive(FromFormField, UriDisplayQuery, Clone, Copy, PartialEq, Eq)] -pub enum SortOrder { - #[field(value = "ascending")] Ascending, - #[field(value = "descending")] Descending, -} - -pub fn filter_and_sort_nodes( - f: &NodeFilterSort, - default_sort: (SortProperty, SortOrder), - nodes: &mut Vec<(Arc<Node>, NodeUserData)>, -) { - let sort_prop = f.sort_by.unwrap_or(default_sort.0); - nodes.retain(|(node, _udata)| { - let mut o = true; - if let Some(prop) = &f.filter_kind { - o = false; - for p in prop { - o |= match p { - // FilterProperty::FederationLocal => node.federated.is_none(), - // FilterProperty::FederationRemote => node.federated.is_some(), - FilterProperty::KindMovie => node.kind == NodeKind::Movie, - FilterProperty::KindVideo => node.kind == NodeKind::Video, - FilterProperty::KindShortFormVideo => node.kind == NodeKind::ShortFormVideo, - FilterProperty::KindMusic => node.kind == NodeKind::Music, - FilterProperty::KindCollection => node.kind == NodeKind::Collection, - FilterProperty::KindChannel => node.kind == NodeKind::Channel, - FilterProperty::KindShow => node.kind == NodeKind::Show, - FilterProperty::KindSeries => node.kind == NodeKind::Series, - FilterProperty::KindSeason => node.kind == NodeKind::Season, - FilterProperty::KindEpisode => node.kind == NodeKind::Episode, - // FilterProperty::Watched => udata.watched == WatchedState::Watched, - // FilterProperty::Unwatched => udata.watched == WatchedState::None, - // FilterProperty::WatchProgress => { - // matches!(udata.watched, WatchedState::Progress(_)) - // } - _ => false, // TODO - } - } - } - match sort_prop { - SortProperty::ReleaseDate => o &= node.release_date.is_some(), - SortProperty::Duration => o &= node.media.is_some(), - _ => (), - } - o - }); - match sort_prop { - SortProperty::Duration => { - nodes.sort_by_key(|(n, _)| (n.media.as_ref().unwrap().duration * 1000.) as i64) - } - SortProperty::ReleaseDate => { - nodes.sort_by_key(|(n, _)| n.release_date.expect("asserted above")) - } - SortProperty::Title => nodes.sort_by(|(a, _), (b, _)| a.title.cmp(&b.title)), - SortProperty::Index => nodes.sort_by(|(a, _), (b, _)| { - a.index - .unwrap_or(usize::MAX) - .cmp(&b.index.unwrap_or(usize::MAX)) - }), - SortProperty::RatingRottenTomatoes => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway(*n.ratings.get(&Rating::RottenTomatoes).unwrap_or(&0.)) - }), - SortProperty::RatingMetacritic => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway(*n.ratings.get(&Rating::Metacritic).unwrap_or(&0.)) - }), - SortProperty::RatingImdb => nodes - .sort_by_cached_key(|(n, _)| SortAnyway(*n.ratings.get(&Rating::Imdb).unwrap_or(&0.))), - SortProperty::RatingTmdb => nodes - .sort_by_cached_key(|(n, _)| SortAnyway(*n.ratings.get(&Rating::Tmdb).unwrap_or(&0.))), - SortProperty::RatingYoutubeViews => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway(*n.ratings.get(&Rating::YoutubeViews).unwrap_or(&0.)) - }), - SortProperty::RatingYoutubeLikes => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway(*n.ratings.get(&Rating::YoutubeLikes).unwrap_or(&0.)) - }), - SortProperty::RatingYoutubeFollowers => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway(*n.ratings.get(&Rating::YoutubeFollowers).unwrap_or(&0.)) - }), - SortProperty::RatingLikesDivViews => nodes.sort_by_cached_key(|(n, _)| { - SortAnyway( - *n.ratings.get(&Rating::YoutubeLikes).unwrap_or(&0.) - / (1. + *n.ratings.get(&Rating::YoutubeViews).unwrap_or(&0.)), - ) - }), - SortProperty::RatingUser => nodes.sort_by_cached_key(|(_, u)| u.rating), - } - - match f.sort_order.unwrap_or(default_sort.1) { - SortOrder::Ascending => (), - SortOrder::Descending => nodes.reverse(), - } -} - -markup::define! { - NodeFilterSortForm<'a>(f: &'a NodeFilterSort, lang: &'a Language) { - details.filtersort[open=f.is_open()] { - summary { "Filter and Sort" } - form[method="GET", action=""] { - fieldset.filter { - legend { "Filter" } - .categories { - @for (cname, cat) in FilterProperty::CATS { - .category { - h3 { @trs(lang, cname) } - @for (value, label) in *cat { - label { input[type="checkbox", name="filter_kind", value=value, checked=f.filter_kind.as_ref().map(|k|k.contains(value)).unwrap_or(true)]; @trs(lang, label) } br; - } - } - } - } - } - fieldset.sortby { - legend { "Sort" } - .categories { - @for (cname, cat) in SortProperty::CATS { - .category { - h3 { @trs(lang, cname) } - @for (value, label) in *cat { - label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by.as_ref()]; @trs(lang, label) } br; - } - } - } - } - } - fieldset.sortorder { - legend { "Sort Order" } - @use SortOrder::*; - @for (value, label) in [(Ascending, "filter_sort.order.asc"), (Descending, "filter_sort.order.desc")] { - label { input[type="radio", name="sort_order", value=value, checked=Some(value)==f.sort_order]; @trs(lang, label) } br; - } - } - input[type="submit", value="Apply"]; a[href="?"] { "Clear" } - } - } - } -} - -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>)) - } -} -impl markup::Render for FilterProperty { - fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result { - writer.write_fmt(format_args!("{}", self as &dyn UriDisplay<Query>)) - } -} -impl RenderAttributeValue for SortOrder {} -impl RenderAttributeValue for FilterProperty {} -impl RenderAttributeValue for SortProperty {} |