aboutsummaryrefslogtreecommitdiff
path: root/ui/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-20 18:39:51 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-20 18:39:51 +0100
commitda27cc2f457f362f11f65b4e06e3d9eca09d1344 (patch)
tree2580b47c22ff1af68e4c98eb200869cb13eb0272 /ui/src
parent03f38fdc3bd45962be8555e50f18fd7761c17989 (diff)
downloadjellything-da27cc2f457f362f11f65b4e06e3d9eca09d1344.tar
jellything-da27cc2f457f362f11f65b4e06e3d9eca09d1344.tar.bz2
jellything-da27cc2f457f362f11f65b4e06e3d9eca09d1344.tar.zst
nom nom nom
Diffstat (limited to 'ui/src')
-rw-r--r--ui/src/node_page.rs31
-rw-r--r--ui/src/props.rs67
-rw-r--r--ui/src/scaffold.rs5
-rw-r--r--ui/src/search.rs12
-rw-r--r--ui/src/stats.rs35
5 files changed, 79 insertions, 71 deletions
diff --git a/ui/src/node_page.rs b/ui/src/node_page.rs
index fa5c93b..f52ea5b 100644
--- a/ui/src/node_page.rs
+++ b/ui/src/node_page.rs
@@ -4,11 +4,12 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::Page;
+use crate::{Page, scaffold::RenderInfo};
use jellycommon::{
- jellyobject::{Object, Tag},
+ jellyobject::{Object, Tag, TypedTag},
*,
};
+use std::marker::PhantomData;
impl Page for NodePage<'_> {
fn title(&self) -> String {
@@ -29,7 +30,7 @@ pub struct NodeUdata<'a> {
markup::define! {
NodePage<'a>(
- ri: RenderInfo<'a>,
+ ri: &'a RenderInfo<'a>,
node: NodeUdata<'a>,
children: &'a [NodeUdata<'a>],
parents: &'a [NodeUdata<'a>],
@@ -184,9 +185,9 @@ markup::define! {
}
}
-fn chapter_key_time(c: &Chapter, dur: f64) -> f64 {
- let start = c.time_start.unwrap_or(0.);
- let end = c.time_end.unwrap_or(dur);
+fn chapter_key_time(c: Object, dur: f64) -> f64 {
+ let start = c.get(CH_START).unwrap_or(0.);
+ let end = c.get(CH_END).unwrap_or(dur);
start * 0.8 + end * 0.2
}
@@ -199,17 +200,17 @@ pub fn aspect_class(kind: Tag) -> &'static str {
}
}
-fn external_id_url(key: IdentifierType, value: &str) -> Option<String> {
- Some(match key {
- IdentifierType::YoutubeVideo => format!("https://youtube.com/watch?v={value}"),
- IdentifierType::YoutubeChannel => format!("https://youtube.com/channel/{value}"),
- IdentifierType::YoutubeChannelHandle => format!("https://youtube.com/channel/@{value}"),
- IdentifierType::MusicbrainzRelease => format!("https://musicbrainz.org/release/{value}"),
- IdentifierType::MusicbrainzArtist => format!("https://musicbrainz.org/artist/{value}"),
- IdentifierType::MusicbrainzReleaseGroup => {
+fn external_id_url(key: Tag, value: &str) -> Option<String> {
+ Some(match TypedTag(key, PhantomData) {
+ IDENT_YOUTUBE_VIDEO => format!("https://youtube.com/watch?v={value}"),
+ IDENT_YOUTUBE_CHANNEL => format!("https://youtube.com/channel/{value}"),
+ IDENT_YOUTUBE_CHANNEL_HANDLE => format!("https://youtube.com/channel/@{value}"),
+ IDENT_MUSICBRAINZ_RELEASE => format!("https://musicbrainz.org/release/{value}"),
+ IDENT_MUSICBRAINZ_ARTIST => format!("https://musicbrainz.org/artist/{value}"),
+ IDENT_MUSICBRAINZ_RELEASE_GROUP => {
format!("https://musicbrainz.org/release-group/{value}")
}
- IdentifierType::MusicbrainzRecording => {
+ IDENT_MUSICBRAINZ_RECORDING => {
format!("https://musicbrainz.org/recording/{value}")
}
_ => return None,
diff --git a/ui/src/props.rs b/ui/src/props.rs
index 3547045..fe7f419 100644
--- a/ui/src/props.rs
+++ b/ui/src/props.rs
@@ -3,56 +3,61 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
+
use crate::{
- format::{MediaInfoExt, format_count, format_duration},
+ format::{format_count, format_duration},
locale::tr,
+ node_page::NodeUdata,
+ scaffold::RenderInfo,
};
+use chrono::DateTime;
+use jellycommon::{jellyobject::TypedTag, *};
+use std::marker::PhantomData;
markup::define! {
- Props<'a>(node: &'a Node, udata: &'a NodeUserData, full: bool, lang: &'a Language) {
+ Props<'a>(ri: &'a RenderInfo<'a>, nodeu: NodeUdata<'a>, full: bool) {
.props {
- @if let Some(m) = &node.media {
- p { @format_duration(m.duration) }
- p { @m.resolution_name() }
+ @if let Some(dur) = nodeu.node.get(NO_DURATION) {
+ p { @format_duration(dur) }
}
- @if let Some(d) = &node.release_date {
+ // @if let Some(res) = nodeu.node.get(NO_TRACK) {
+ // p { @m.resolution_name() }
+ // }
+ @if let Some(d) = nodeu.node.get(NO_RELEASEDATE) {
p { @if *full {
- @DateTime::from_timestamp_millis(*d).unwrap().naive_utc().to_string()
+ @DateTime::from_timestamp_millis(d).unwrap().naive_utc().to_string()
} else {
- @DateTime::from_timestamp_millis(*d).unwrap().date_naive().to_string()
+ @DateTime::from_timestamp_millis(d).unwrap().date_naive().to_string()
}}
}
- @match node.visibility {
- Visibility::Visible => {}
- Visibility::Reduced => {p.visibility{@trs(lang, "prop.vis.reduced")}}
- Visibility::Hidden => {p.visibility{@trs(lang, "prop.vis.hidden")}}
+ @match nodeu.node.get(NO_VISIBILITY).unwrap_or(VISI_VISIBLE) {
+ VISI_VISIBLE => {}
+ VISI_REDUCED => {p.visibility{@tr(ri.lang, "prop.vis.reduced")}}
+ VISI_HIDDEN => {p.visibility{@tr(ri.lang, "prop.vis.hidden")}}
}
// TODO
// @if !node.children.is_empty() {
// p { @format!("{} items", node.children.len()) }
// }
- @for (kind, value) in &node.ratings {
- @match kind {
- RatingType::YoutubeLikes => {p.likes{ @format_count(*value as usize) " Likes" }}
- RatingType::YoutubeViews => {p{ @format_count(*value as usize) " Views" }}
- RatingType::YoutubeFollowers => {p{ @format_count(*value as usize) " Subscribers" }}
- RatingType::RottenTomatoes => {p.rating{ @value " Tomatoes" }}
- RatingType::Metacritic if *full => {p{ "Metacritic Score: " @value }}
- RatingType::Imdb => {p.rating{ "IMDb " @value }}
- RatingType::Tmdb => {p.rating{ "TMDB " @format!("{:.01}", value) }}
- RatingType::Trakt => {p.rating{ "Trakt " @format!("{:.01}", value) }}
+ @for (kind, value) in nodeu.node.get(NO_RATINGS).unwrap_or_default().entries::<f64>() {
+ @match TypedTag(kind, PhantomData) {
+ RTYP_YOUTUBE_LIKES => {p.likes{ @format_count(value as usize) " Likes" }}
+ RTYP_YOUTUBE_VIEWS => {p{ @format_count(value as usize) " Views" }}
+ RTYP_YOUTUBE_FOLLOWERS => {p{ @format_count(value as usize) " Subscribers" }}
+ RTYP_ROTTEN_TOMATOES => {p.rating{ @value " Tomatoes" }}
+ RTYP_METACRITIC if *full => {p{ "Metacritic Score: " @value }}
+ RTYP_IMDB => {p.rating{ "IMDb " @value }}
+ RTYP_TMDB => {p.rating{ "TMDB " @format!("{:.01}", value) }}
+ RTYP_TRAKT => {p.rating{ "Trakt " @format!("{:.01}", value) }}
_ => {}
}
}
- @if let Some(f) = &node.federated {
- p.federation { @f }
- }
- @match udata.watched {
- WatchedState::None => {}
- WatchedState::Pending => { p.pending { @trs(lang, "prop.watched.pending") } }
- WatchedState::Progress(x) => { p.progress { @tr(**lang, "prop.watched.progress").replace("{time}", &format_duration(x)) } }
- WatchedState::Watched => { p.watched { @trs(lang, "prop.watched.watched") } }
- }
+ // @match nodeu.udata.watched {
+ // WatchedState::None => {}
+ // WatchedState::Pending => { p.pending { @tr(ri.lang, "prop.watched.pending") } }
+ // WatchedState::Progress(x) => { p.progress { @tr(ri.lang, "prop.watched.progress").replace("{time}", &format_duration(x)) } }
+ // WatchedState::Watched => { p.watched { @tr(ri.lang, "prop.watched.watched") } }
+ // }
}
}
}
diff --git a/ui/src/scaffold.rs b/ui/src/scaffold.rs
index 8b96f9f..82d6d5e 100644
--- a/ui/src/scaffold.rs
+++ b/ui/src/scaffold.rs
@@ -14,6 +14,7 @@ use jellycommon::{
u_account_login, u_account_logout, u_account_register, u_account_settings,
u_admin_dashboard, u_home, u_items, u_node_slug, u_search, u_stats,
},
+ user::{USER_ADMIN, USER_NAME},
};
use markup::{Render, raw};
use std::sync::LazyLock;
@@ -58,8 +59,8 @@ markup::define! {
}
div.account {
@if let Some(user) = &ri.user {
- span { @raw(tr(ri.lang, "nav.username").replace("{name}", &format!("<b class=\"username\">{}</b>", escape(&ri.user.display_name)))) } " "
- @if session.user.admin {
+ span { @raw(tr(ri.lang, "nav.username").replace("{name}", &format!("<b class=\"username\">{}</b>", escape(user.get(USER_NAME).unwrap_or("nameless user"))))) } " "
+ @if user.has(USER_ADMIN.0) {
a.admin.hybrid_button[href=u_admin_dashboard()] { p {@tr(ri.lang, "nav.admin")} } " "
}
a.settings.hybrid_button[href=u_account_settings()] { p {@tr(ri.lang, "nav.settings")} } " "
diff --git a/ui/src/search.rs b/ui/src/search.rs
index dd5aa0c..35ae82d 100644
--- a/ui/src/search.rs
+++ b/ui/src/search.rs
@@ -4,7 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{Page, locale::tr, node_card::NodeCard};
+use crate::{Page, locale::tr, node_card::NodeCard, scaffold::RenderInfo};
use markup::DynRender;
impl Page for SearchPage<'_> {
@@ -20,15 +20,15 @@ impl Page for SearchPage<'_> {
}
markup::define! {
- SearchPage<'a>(lang: &'a Language, r: Option<ApiSearchResponse>, query: &'a Option<String>) {
- h1 { @trs(lang, "search.title") }
+ SearchPage<'a>(ri: &'a RenderInfo<'a>, r: Option<ApiSearchResponse>, query: &'a Option<String>) {
+ h1 { @tr(ri.lang, "search.title") }
form[action="", method="GET"] {
- input[type="text", name="query", placeholder=&*tr(**lang, "search.placeholder"), value=&query];
+ input[type="text", name="query", placeholder=tr(ri.lang, "search.placeholder"), value=&query];
input[type="submit", value="Search"];
}
@if let Some(r) = &r {
- h2 { @trs(lang, "search.results.title") }
- p.stats { @tr(**lang, "search.results.stats").replace("{count}", &r.count.to_string()).replace("{dur}", &format!("{:?}", r.duration)) }
+ h2 { @tr(ri.lang, "search.results.title") }
+ p.stats { @tr(ri.lang, "search.results.stats").replace("{count}", &r.count.to_string()).replace("{dur}", &format!("{:?}", r.duration)) }
ul.children {@for (node, udata) in r.results.iter() {
li { @NodeCard { node, udata, lang } }
}}
diff --git a/ui/src/stats.rs b/ui/src/stats.rs
index c9001b0..d69f07e 100644
--- a/ui/src/stats.rs
+++ b/ui/src/stats.rs
@@ -8,13 +8,14 @@ use crate::{
Page,
format::{format_duration, format_duration_long, format_kind, format_size},
locale::tr,
+ scaffold::RenderInfo,
};
use jellycommon::routes::u_node_slug;
use markup::raw;
impl Page for StatsPage<'_> {
fn title(&self) -> String {
- tr(*self.lang, "stats.title").to_string()
+ tr(self.ri.lang, "stats.title").to_string()
}
fn to_render(&self) -> markup::DynRender<'_> {
markup::new!(@self)
@@ -22,35 +23,35 @@ impl Page for StatsPage<'_> {
}
markup::define! {
- StatsPage<'a>(lang: &'a Language, r: ApiStatsResponse) {
+ StatsPage<'a>(ri: &'a RenderInfo<'a>, r: ApiStatsResponse) {
.page.stats {
- h1 { @trs(lang, "stats.title") }
- p { @raw(tr(**lang, "stats.count")
+ h1 { @tr(ri.lang, "stats.title") }
+ p { @raw(tr(ri.lang, "stats.count")
.replace("{count}", &format!("<b>{}</b>", r.total.count))
)}
- p { @raw(tr(**lang, "stats.runtime")
- .replace("{dur}", &format!("<b>{}</b>", format_duration_long(r.total.runtime, **lang)))
+ p { @raw(tr(ri.lang, "stats.runtime")
+ .replace("{dur}", &format!("<b>{}</b>", format_duration_long(r.total.runtime, ri.lang)))
.replace("{size}", &format!("<b>{}</b>", format_size(r.total.size)))
)}
- p { @raw(tr(**lang, "stats.average")
+ p { @raw(tr(ri.lang, "stats.average")
.replace("{dur}", &format!("<b>{}</b>", format_duration(r.total.average_runtime())))
.replace("{size}", &format!("<b>{}</b>", format_size(r.total.average_size() as u64)))
)}
- h2 { @trs(lang, "stats.by_kind.title") }
+ h2 { @tr(ri.lang, "stats.by_kind.title") }
table.striped {
tr {
- th { @trs(lang, "stats.by_kind.kind") }
- th { @trs(lang, "stats.by_kind.count") }
- th { @trs(lang, "stats.by_kind.total_size") }
- th { @trs(lang, "stats.by_kind.total_runtime") }
- th { @trs(lang, "stats.by_kind.average_size") }
- th { @trs(lang, "stats.by_kind.average_runtime") }
- th { @trs(lang, "stats.by_kind.max_size") }
- th { @trs(lang, "stats.by_kind.max_runtime") }
+ th { @tr(ri.lang, "stats.by_kind.kind") }
+ th { @tr(ri.lang, "stats.by_kind.count") }
+ th { @tr(ri.lang, "stats.by_kind.total_size") }
+ th { @tr(ri.lang, "stats.by_kind.total_runtime") }
+ th { @tr(ri.lang, "stats.by_kind.average_size") }
+ th { @tr(ri.lang, "stats.by_kind.average_runtime") }
+ th { @tr(ri.lang, "stats.by_kind.max_size") }
+ th { @tr(ri.lang, "stats.by_kind.max_runtime") }
}
@for (k,b) in &r.kinds { tr {
- td { @format_kind(*k, **lang) }
+ td { @format_kind(*k, ri.lang) }
td { @b.count }
td { @format_size(b.size) }
td { @format_duration(b.runtime) }