diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-20 01:53:09 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-20 01:53:09 +0100 |
| commit | bc155f7abea6ee5155b6460d367a6797205db4fd (patch) | |
| tree | a2c55d96c493cf2b71bfc79539d4f8d0d8953392 /ui/src/node_page.rs | |
| parent | 10736db63ad6d99e6cdce41920aa10dbeab02129 (diff) | |
| download | jellything-bc155f7abea6ee5155b6460d367a6797205db4fd.tar jellything-bc155f7abea6ee5155b6460d367a6797205db4fd.tar.bz2 jellything-bc155f7abea6ee5155b6460d367a6797205db4fd.tar.zst | |
started ui refactor
Diffstat (limited to 'ui/src/node_page.rs')
| -rw-r--r-- | ui/src/node_page.rs | 345 |
1 files changed, 167 insertions, 178 deletions
diff --git a/ui/src/node_page.rs b/ui/src/node_page.rs index 3e0c64f..fa5c93b 100644 --- a/ui/src/node_page.rs +++ b/ui/src/node_page.rs @@ -4,193 +4,183 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{ - Page, - filter_sort::NodeFilterSortForm, - format::format_chapter, - locale::{Language, trs}, - node_card::{NodeCard, NodeCardWide}, - props::Props, +use crate::Page; +use jellycommon::{ + jellyobject::{Object, Tag}, + *, }; -use jellycommon::routes::{ - u_node_image, u_node_slug, u_node_slug_person_asset, u_node_slug_player, - u_node_slug_player_time, u_node_slug_thumbnail, u_node_slug_update_rating, u_node_slug_watched, -}; -use std::sync::Arc; impl Page for NodePage<'_> { fn title(&self) -> String { - self.node.title.clone().unwrap_or_default() + self.node.node.get(NO_TITLE).unwrap_or_default().to_string() } fn class(&self) -> Option<&'static str> { - if self.player { - Some("player") - } else { - Some("node-page") - } + Some("node-page") } fn to_render(&self) -> markup::DynRender<'_> { markup::new!(@self) } } +pub struct NodeUdata<'a> { + pub node: Object<'a>, + pub udata: Object<'a>, +} + markup::define! { NodePage<'a>( - node: &'a Node, - udata: &'a NodeUserData, - children: &'a [(Arc<Node>, NodeUserData)], - parents: &'a [(Arc<Node>, NodeUserData)], - similar: &'a [(Arc<Node>, NodeUserData)], - filter: &'a NodeFilterSort, - lang: &'a Language, - player: bool, + ri: RenderInfo<'a>, + node: NodeUdata<'a>, + children: &'a [NodeUdata<'a>], + parents: &'a [NodeUdata<'a>], + similar: &'a [NodeUdata<'a>], ) { - @if !matches!(node.kind, NodeKind::Collection) && !player { - img.backdrop[src=u_node_image(&node.slug, PictureSlot::Backdrop, 2048), loading="lazy"]; - } - .page.node { - @if !matches!(node.kind, NodeKind::Collection) && !player { - @let cls = format!("bigposter {}", aspect_class(node.kind)); - div[class=cls] { img[src=u_node_image(&node.slug, PictureSlot::Cover, 2048), loading="lazy"]; } - } - .title { - h1 { @node.title } - ul.parents { @for (node, _) in *parents { li { - a.component[href=u_node_slug(&node.slug)] { @node.title } - }}} - @if node.media.is_some() { - a.play[href=u_node_slug_player(&node.slug)] { @trs(lang, "node.player_link") } - } - @if !matches!(node.kind, NodeKind::Collection | NodeKind::Channel) { - @if matches!(udata.watched, WatchedState::None | WatchedState::Pending | WatchedState::Progress(_)) { - form.mark_watched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Watched)] { - input[type="submit", value=trs(lang, "node.watched.set")]; - } - } - @if matches!(udata.watched, WatchedState::Watched) { - form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] { - input[type="submit", value=trs(lang, "node.watched.unset")]; - } - } - @if matches!(udata.watched, WatchedState::None) { - form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Pending)] { - input[type="submit", value=trs(lang, "node.watchlist.set")]; - } - } - @if matches!(udata.watched, WatchedState::Pending) { - form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] { - input[type="submit", value=trs(lang, "node.watchlist.unset")]; - } - } - form.rating[method="POST", action=u_node_slug_update_rating(&node.slug)] { - input[type="range", name="rating", min=-10, max=10, step=1, value=udata.rating]; - input[type="submit", value=trs(lang, "node.update_rating")]; - } - } - } - .details { - @Props { node, udata, full: true, lang } - h3 { @node.tagline } - @if let Some(description) = &node.description { - p { @for line in description.lines() { @line br; } } - } - @if let Some(media) = &node.media { - @if !media.chapters.is_empty() { - h2 { @trs(lang, "node.chapters") } - ul.children.hlist { @for chap in &media.chapters { - @let (inl, sub) = format_chapter(chap); - li { .card."aspect-thumb" { - .poster { - a[href=u_node_slug_player_time(&node.slug, chap.time_start.unwrap_or(0.))] { - img[src=u_node_slug_thumbnail(&node.slug, chapter_key_time(chap, media.duration), 1024), loading="lazy"]; - } - .cardhover { .props { p { @inl } } } - } - .title { span { @sub } } - }} - }} - } - @if !node.credits.is_empty() { - h2 { @trs(lang, "node.people") } - @for (group, people) in &node.credits { - details[open=group==&CreditCategory::Cast] { - summary { h3 { @format!("{}", group) } } - ul.children.hlist { @for (i, pe) in people.iter().enumerate() { - li { .card."aspect-port" { - .poster { - a[href="#"] { - img[src=u_node_slug_person_asset(&node.slug, *group, i, 1024), loading="lazy"]; - } - } - .title { - // TODO span { @pe.person.name } br; - @if let Some(c) = pe.characters.first() { - span.subtitle { @c } - } - @if let Some(c) = pe.jobs.first() { - span.subtitle { @c } - } - } - }} - }} - } - } - } - details { - summary { @trs(lang, "media.tracks") } - ol { @for track in &media.tracks { - li { @format!("{track}") } - }} - } - } - @if !node.identifiers.is_empty() { - details { - summary { @trs(lang, "node.external_ids") } - table { - @for (key, value) in &node.identifiers { tr { - tr { - td { @trs(lang, &format!("id.{}", key)) } - @if let Some(url) = external_id_url(*key, value) { - td { a[href=url] { pre { @value } } } - } else { - td { pre { @value } } - } - } - }} - } - } - } - @if !node.tags.is_empty() { - details { - summary { @trs(lang, "node.tags") } - ol { @for tag in &node.tags { - li { @tag } - }} - } - } - } - @if matches!(node.kind, NodeKind::Collection | NodeKind::Channel) { - @NodeFilterSortForm { f: filter, lang } - } - @if !similar.is_empty() { - h2 { @trs(lang, "node.similar") } - ul.children.hlist {@for (node, udata) in similar.iter() { - li { @NodeCard { node, udata, lang } } - }} - } - @match node.kind { - NodeKind::Show | NodeKind::Series | NodeKind::Season => { - ol { @for (node, udata) in children.iter() { - li { @NodeCardWide { node, udata, lang } } - }} - } - NodeKind::Collection | NodeKind::Channel | _ => { - ul.children {@for (node, udata) in children.iter() { - li { @NodeCard { node, udata, lang } } - }} - } - } - } + // @if !matches!(node.kind, NodeKind::Collection) && !player { + // img.backdrop[src=u_node_image(&node.slug, PictureSlot::Backdrop, 2048), loading="lazy"]; + // } + // .page.node { + // @if !matches!(node.kind, NodeKind::Collection) && !player { + // @let cls = format!("bigposter {}", aspect_class(node.kind)); + // div[class=cls] { img[src=u_node_image(&node.slug, PictureSlot::Cover, 2048), loading="lazy"]; } + // } + // .title { + // h1 { @node.title } + // ul.parents { @for (node, _) in *parents { li { + // a.component[href=u_node_slug(&node.slug)] { @node.title } + // }}} + // @if node.media.is_some() { + // a.play[href=u_node_slug_player(&node.slug)] { @trs(lang, "node.player_link") } + // } + // @if !matches!(node.kind, NodeKind::Collection | NodeKind::Channel) { + // @if matches!(udata.watched, WatchedState::None | WatchedState::Pending | WatchedState::Progress(_)) { + // form.mark_watched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Watched)] { + // input[type="submit", value=trs(lang, "node.watched.set")]; + // } + // } + // @if matches!(udata.watched, WatchedState::Watched) { + // form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] { + // input[type="submit", value=trs(lang, "node.watched.unset")]; + // } + // } + // @if matches!(udata.watched, WatchedState::None) { + // form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::Pending)] { + // input[type="submit", value=trs(lang, "node.watchlist.set")]; + // } + // } + // @if matches!(udata.watched, WatchedState::Pending) { + // form.mark_unwatched[method="POST", action=u_node_slug_watched(&node.slug, ApiWatchedState::None)] { + // input[type="submit", value=trs(lang, "node.watchlist.unset")]; + // } + // } + // form.rating[method="POST", action=u_node_slug_update_rating(&node.slug)] { + // input[type="range", name="rating", min=-10, max=10, step=1, value=udata.rating]; + // input[type="submit", value=trs(lang, "node.update_rating")]; + // } + // } + // } + // .details { + // @Props { node, udata, full: true, lang } + // h3 { @node.tagline } + // @if let Some(description) = &node.description { + // p { @for line in description.lines() { @line br; } } + // } + // @if let Some(media) = &node.media { + // @if !media.chapters.is_empty() { + // h2 { @trs(lang, "node.chapters") } + // ul.children.hlist { @for chap in &media.chapters { + // @let (inl, sub) = format_chapter(chap); + // li { .card."aspect-thumb" { + // .poster { + // a[href=u_node_slug_player_time(&node.slug, chap.time_start.unwrap_or(0.))] { + // img[src=u_node_slug_thumbnail(&node.slug, chapter_key_time(chap, media.duration), 1024), loading="lazy"]; + // } + // .cardhover { .props { p { @inl } } } + // } + // .title { span { @sub } } + // }} + // }} + // } + // @if !node.credits.is_empty() { + // h2 { @trs(lang, "node.people") } + // @for (group, people) in &node.credits { + // details[open=group==&CreditCategory::Cast] { + // summary { h3 { @format!("{}", group) } } + // ul.children.hlist { @for (i, pe) in people.iter().enumerate() { + // li { .card."aspect-port" { + // .poster { + // a[href="#"] { + // img[src=u_node_slug_person_asset(&node.slug, *group, i, 1024), loading="lazy"]; + // } + // } + // .title { + // // TODO span { @pe.person.name } br; + // @if let Some(c) = pe.characters.first() { + // span.subtitle { @c } + // } + // @if let Some(c) = pe.jobs.first() { + // span.subtitle { @c } + // } + // } + // }} + // }} + // } + // } + // } + // details { + // summary { @trs(lang, "media.tracks") } + // ol { @for track in &media.tracks { + // li { @format!("{track}") } + // }} + // } + // } + // @if !node.identifiers.is_empty() { + // details { + // summary { @trs(lang, "node.external_ids") } + // table { + // @for (key, value) in &node.identifiers { tr { + // tr { + // td { @trs(lang, &format!("id.{}", key)) } + // @if let Some(url) = external_id_url(*key, value) { + // td { a[href=url] { pre { @value } } } + // } else { + // td { pre { @value } } + // } + // } + // }} + // } + // } + // } + // @if !node.tags.is_empty() { + // details { + // summary { @trs(lang, "node.tags") } + // ol { @for tag in &node.tags { + // li { @tag } + // }} + // } + // } + // } + // @if matches!(node.kind, NodeKind::Collection | NodeKind::Channel) { + // @NodeFilterSortForm { f: filter, lang } + // } + // @if !similar.is_empty() { + // h2 { @trs(lang, "node.similar") } + // ul.children.hlist {@for (node, udata) in similar.iter() { + // li { @NodeCard { node, udata, lang } } + // }} + // } + // @match node.kind { + // NodeKind::Show | NodeKind::Series | NodeKind::Season => { + // ol { @for (node, udata) in children.iter() { + // li { @NodeCardWide { node, udata, lang } } + // }} + // } + // NodeKind::Collection | NodeKind::Channel | _ => { + // ul.children {@for (node, udata) in children.iter() { + // li { @NodeCard { node, udata, lang } } + // }} + // } + // } + // } } } @@ -200,13 +190,12 @@ fn chapter_key_time(c: &Chapter, dur: f64) -> f64 { start * 0.8 + end * 0.2 } -pub fn aspect_class(kind: NodeKind) -> &'static str { - use NodeKind::*; +pub fn aspect_class(kind: Tag) -> &'static str { match kind { - Video | Episode => "aspect-thumb", - Collection => "aspect-land", - Season | Show | Series | Movie | ShortFormVideo => "aspect-port", - Channel | Music | Unknown => "aspect-square", + KIND_VIDEO | KIND_EPISODE => "aspect-thumb", + KIND_COLLECTION => "aspect-land", + KIND_SEASON | KIND_SHOW | KIND_SERIES | KIND_MOVIE | KIND_SHORTFORMVIDEO => "aspect-port", + KIND_CHANNEL | KIND_MUSIC | _ => "aspect-square", } } |