aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/src/routes/ui/sort.rs151
-rw-r--r--web/style/forms.css59
-rw-r--r--web/style/player.css26
3 files changed, 158 insertions, 78 deletions
diff --git a/server/src/routes/ui/sort.rs b/server/src/routes/ui/sort.rs
index d8a44b2..dc017a2 100644
--- a/server/src/routes/ui/sort.rs
+++ b/server/src/routes/ui/sort.rs
@@ -11,67 +11,97 @@ pub struct NodeFilterSort {
sort_order: Option<SortOrder>,
}
-#[rustfmt::skip]
-#[derive(FromFormField, UriDisplayQuery, Clone, PartialEq, Eq)]
-enum FilterProperty {
- #[field(value = "fed_local")] FederationLocal,
- #[field(value = "fed_remote")] FederationRemote,
- #[field(value = "kind_movie")] KindMovie,
- #[field(value = "kind_video")] KindVideo,
- #[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,
+macro_rules! form_enum {
+ (enum $i:ident { $($vi:ident = $vk:literal),*, }) => {
+ #[derive(FromFormField, UriDisplayQuery, Clone, PartialEq, Eq)]
+ enum $i { $(#[field(value = $vk)] $vi),* }
+ impl $i { #[allow(unused)] const ALL: &'static [$i] = &[$($i::$vi),*]; }
+ };
}
-#[rustfmt::skip]
-#[derive(FromFormField, UriDisplayQuery, Clone, PartialEq, Eq)]
-enum SortProperty {
- #[field(value = "release_date")] ReleaseDate,
- #[field(value = "title")] Title,
- #[field(value = "rating_rt")] RatingRottenTomatoes,
- #[field(value = "rating_mc")] RatingMetacritic,
- #[field(value = "rating_imdb")] RatingImdb,
- #[field(value = "rating_yt_views")] RatingYoutubeViews,
- #[field(value = "rating_yt_likes")] RatingYoutubeLikes,
- #[field(value = "rating_yt_followers")] RatingYoutubeFollowers,
-}
+form_enum!(
+ enum FilterProperty {
+ FederationLocal = "fed_local",
+ FederationRemote = "fed_remote",
+ KindMovie = "kind_movie",
+ KindVideo = "kind_video",
+ 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",
+ RatingRottenTomatoes = "rating_rt",
+ RatingMetacritic = "rating_mc",
+ RatingImdb = "rating_imdb",
+ RatingTmdb = "rating_tmdb",
+ RatingYoutubeViews = "rating_yt_views",
+ RatingYoutubeLikes = "rating_yt_likes",
+ RatingYoutubeFollowers = "rating_yt_followers",
+ }
+);
impl SortProperty {
- const ALL: &'static [(SortProperty, &'static str)] = {
+ const CATS: &'static [(&'static str, &'static [(SortProperty, &'static str)])] = {
use SortProperty::*;
&[
- (Title, "Title"),
- (ReleaseDate, "Release Date"),
- (RatingImdb, "IMDb Rating"),
- (RatingMetacritic, "Metacritic Rating"),
- (RatingRottenTomatoes, "Rotten Tomatoes"),
- (RatingYoutubeFollowers, "Youtube Subscribers"),
- (RatingYoutubeLikes, "Youtube Likes"),
- (RatingYoutubeViews, "Youtube Views"),
+ (
+ "General",
+ &[(Title, "Title"), (ReleaseDate, "Release Date")],
+ ),
+ (
+ "By Rating",
+ &[
+ (RatingImdb, "IMDb Rating"),
+ (RatingTmdb, "TMDB Rating"),
+ (RatingMetacritic, "Metacritic Rating"),
+ (RatingRottenTomatoes, "Rotten Tomatoes"),
+ (RatingYoutubeFollowers, "Youtube Subscribers"),
+ (RatingYoutubeLikes, "Youtube Likes"),
+ (RatingYoutubeViews, "Youtube Views"),
+ ],
+ ),
]
};
}
impl FilterProperty {
- const ALL: &'static [(FilterProperty, &'static str)] = {
+ const CATS: &'static [(&'static str, &'static [(FilterProperty, &'static str)])] = {
use FilterProperty::*;
&[
- (FederationLocal, "Local"),
- (FederationRemote, "Remote"),
- (KindMovie, "Movie"),
- (KindVideo, "Video"),
- (KindCollection, "Collection"),
- (KindChannel, "Channel"),
- (KindShow, "Show"),
- (KindSeries, "Series"),
- (KindSeason, "Season"),
- (KindEpisode, "Episode"),
+ (
+ "By Kind",
+ &[
+ (KindMovie, "Movie"),
+ (KindVideo, "Video"),
+ (KindCollection, "Collection"),
+ (KindChannel, "Channel"),
+ (KindShow, "Show"),
+ (KindSeries, "Series"),
+ (KindSeason, "Season"),
+ (KindEpisode, "Episode"),
+ ],
+ ),
+ (
+ "By Federation",
+ &[(FederationLocal, "Local"), (FederationRemote, "Remote")],
+ ),
]
};
}
+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)]
enum SortOrder {
@@ -86,7 +116,7 @@ pub fn filter_and_sort_nodes(
nodes.retain(|(_id, node, _udata)| {
let mut o = true;
if let Some(prop) = &f.filter_kind {
- for (p, _) in FilterProperty::ALL {
+ for p in FilterProperty::ALL {
if prop.contains(p) {
continue;
}
@@ -124,6 +154,9 @@ pub fn filter_and_sort_nodes(
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.))
}),
@@ -143,19 +176,33 @@ pub fn filter_and_sort_nodes(
markup::define! {
NodeFilterSortForm<'a>(f: &'a NodeFilterSort) {
- details.filtersort {
+ details.filtersort[open=f.is_open()] {
summary { "Filter and Sort" }
form[method="GET", action=""] {
fieldset.filter {
legend { "Filter" }
- @for (value, label) in FilterProperty::ALL {
- label { input[type="checkbox", name="filter_kind", value=value, checked=f.filter_kind.as_ref().map(|k|k.contains(&value)).unwrap_or(true)]; @label } br;
+ .categories {
+ @for (cname, cat) in FilterProperty::CATS {
+ .category {
+ h3 { @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)]; @label } br;
+ }
+ }
+ }
}
}
fieldset.sortby {
- legend { "Sort By" }
- @for (value, label) in SortProperty::ALL {
- label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by.as_ref()]; @label } br;
+ legend { "Sort" }
+ .categories {
+ @for (cname, cat) in SortProperty::CATS {
+ .category {
+ h3 { @cname }
+ @for (value, label) in *cat {
+ label { input[type="radio", name="sort_by", value=value, checked=Some(value)==f.sort_by.as_ref()]; @label } br;
+ }
+ }
+ }
}
}
fieldset.sortorder {
diff --git a/web/style/forms.css b/web/style/forms.css
index 5e5319d..48a7dba 100644
--- a/web/style/forms.css
+++ b/web/style/forms.css
@@ -83,3 +83,62 @@ form.account h1 {
form.account p {
color: var(--font-dark);
}
+
+legend {
+ font-size: 1.5em;
+}
+input[type="radio"] {
+ appearance: none;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border-radius: 8px;
+ padding: 2px;
+ background-clip: content-box;
+ border: 2px solid var(--font);
+ background-color: transparent;
+ transition: background-color 0.3s;
+}
+input[type="radio"]:checked {
+ background-color: var(--accent-light);
+}
+
+input[type="checkbox"] {
+ appearance: none;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border-radius: 3px;
+ padding: 2px;
+ background-clip: content-box;
+ border: 2px solid var(--font);
+ background-color: transparent;
+ transition: background-color 0.3s;
+}
+input[type="checkbox"]:checked {
+ background-color: var(--accent-light);
+}
+
+fieldset label {
+ transition: color 0.2s;
+}
+fieldset label:hover {
+ color: var(--accent-light);
+}
+
+fieldset .categories {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+fieldset .categories .category {
+ margin-right: 2em;
+ margin-left: 2em;
+ min-width: max-content;
+ flex-basis: auto;
+ display: inline-flex;
+ flex-direction: column;
+}
+fieldset .categories .category h3 {
+ margin: 0px;
+}
diff --git a/web/style/player.css b/web/style/player.css
index e01cbc3..1683573 100644
--- a/web/style/player.css
+++ b/web/style/player.css
@@ -16,32 +16,6 @@ form.playerconf {
grid-template-rows: 3em auto 5em;
}
-legend {
- font-size: 1.5em;
-}
-input[type="radio"] {
- appearance: none;
- display: inline-block;
- width: 1.2em;
- height: 1.2em;
- border-radius: 8px;
- padding: 2px;
- background-clip: content-box;
- border: 2px solid var(--font);
- background-color: transparent;
- transition: background-color 0.3s;
-}
-input[type="radio"]:checked {
- background-color: var(--accent-light);
-}
-
-fieldset label {
- transition: color 0.2s;
-}
-fieldset label:hover {
- color: var(--accent-light);
-}
-
.playerconf {
margin: 2em;
}