aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-03-07 13:34:04 +0100
committermetamuffin <metamuffin@disroot.org>2026-03-07 13:34:04 +0100
commit3a6205f6077c1555f2f3bcf308f5e9605b8dbf53 (patch)
tree0742ed69f9abf8973ee62291ba0984e7caffae18
parent4ce6d64648634bd8d22e8ed0676e0e5b22947dc3 (diff)
downloadjellything-3a6205f6077c1555f2f3bcf308f5e9605b8dbf53.tar
jellything-3a6205f6077c1555f2f3bcf308f5e9605b8dbf53.tar.bz2
jellything-3a6205f6077c1555f2f3bcf308f5e9605b8dbf53.tar.zst
reenable node page extras
-rw-r--r--common/src/api.rs2
-rw-r--r--server/src/routes/node.rs235
-rw-r--r--ui/locale/src/lib.rs4
-rw-r--r--ui/src/components/node_card.rs2
-rw-r--r--ui/src/components/node_page.rs41
5 files changed, 131 insertions, 153 deletions
diff --git a/common/src/api.rs b/common/src/api.rs
index bf79cc0..9cf5e04 100644
--- a/common/src/api.rs
+++ b/common/src/api.rs
@@ -12,7 +12,7 @@ use std::collections::BTreeMap;
pub struct Nku<'a> {
pub node: Cow<'a, Object>,
pub userdata: Cow<'a, Object>,
- pub role: Option<&'a str>,
+ pub role: Option<Cow<'a, str>>,
}
pub struct Stats {
diff --git a/server/src/routes/node.rs b/server/src/routes/node.rs
index ca07bac..d7ef0b5 100644
--- a/server/src/routes/node.rs
+++ b/server/src/routes/node.rs
@@ -6,27 +6,33 @@
use super::error::MyResult;
use crate::request_info::RequestInfo;
-use anyhow::anyhow;
+use anyhow::{Result, anyhow};
use jellycommon::{
- jellyobject::{EMPTY, Path},
+ jellyobject::{EMPTY, Object, Path, Tag},
*,
};
-use jellydb::{Filter, Query};
+use jellydb::{Filter, MultiBehaviour, Query, Sort, SortOrder, Transaction, ValueSort};
use jellyui::components::node_page::NodePage;
use rocket::{get, response::content::RawHtml};
-use std::borrow::Cow;
+use std::{borrow::Cow, collections::BTreeMap};
#[get("/n/<slug>")]
pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
ri.require_user()?;
let mut nku = None;
+ let mut children = Vec::new();
+ let mut credits = Vec::new();
+ let mut credited = Vec::new();
ri.state.database.transaction(&mut |txn| {
if let Some(row) = txn.query_single(Query {
filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.into()),
..Default::default()
})? {
let n = txn.get(row)?.unwrap();
+ children = c_children(txn, row, &n)?;
+ credits = c_credits(txn, &n)?;
+ credited = c_credited(txn, row)?;
nku = Some(Nku {
node: Cow::Owned(n),
userdata: Cow::Borrowed(EMPTY),
@@ -42,146 +48,99 @@ pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<RawHtml<String>> {
Ok(ri.respond_ui(&NodePage {
ri: &ri.render_info(),
nku,
+ children: &children,
+ credited: &credited,
+ credits: &credits,
}))
}
-// fn c_children(
-// page: &mut ObjectBufferBuilder,
-// txn: &mut dyn Transaction,
-// row: u64,
-// nku: &Object,
-// ) -> Result<()> {
-// let kind = nku
-// .get(NKU_NODE)
-// .unwrap_or_default()
-// .get(NO_KIND)
-// .unwrap_or(KIND_COLLECTION);
-
-// let (order, path) = match kind {
-// KIND_CHANNEL => (SortOrder::Descending, Path(vec![NO_RELEASEDATE.0])),
-// KIND_SEASON | KIND_SHOW => (SortOrder::Ascending, Path(vec![NO_INDEX.0])),
-// _ => (SortOrder::Ascending, Path(vec![NO_TITLE.0])),
-// };
-
-// let children_rows = txn
-// .query(Query {
-// sort: Sort::Value(ValueSort {
-// multi: MultiBehaviour::First,
-// offset: None,
-// order,
-// path,
-// }),
-// filter: Filter::All(vec![
-// Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
-// Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
-// ]),
-// ..Default::default()
-// })?
-// .collect::<Result<Vec<_>>>()?;
-
-// if children_rows.is_empty() {
-// return Ok(());
-// }
-
-// let mut list = ObjectBufferBuilder::default();
-
-// list.push(
-// NODELIST_DISPLAYSTYLE,
-// match kind {
-// KIND_SEASON | KIND_SHOW => NLSTYLE_LIST,
-// _ => NLSTYLE_GRID,
-// },
-// );
-
-// for (row, _) in children_rows {
-// list.push(
-// NODELIST_ITEM,
-// Object::EMPTY
-// .insert(NKU_NODE, txn.get(row)?.unwrap().as_object())
-// .as_object(),
-// );
-// }
-
-// page.push(VIEW_NODE_LIST, list.finish().as_object());
-// Ok(())
-// }
-
-// fn c_credits(
-// page: &mut ObjectBufferBuilder,
-// txn: &mut dyn Transaction,
-// nku: &Object,
-// ) -> Result<()> {
-// if !nku.get(NKU_NODE).unwrap_or_default().has(NO_CREDIT.0) {
-// return Ok(());
-// }
-
-// let mut cats = BTreeMap::<_, Vec<_>>::new();
-// for cred in nku.get(NKU_NODE).unwrap_or_default().iter(NO_CREDIT) {
-// let mut o = ObjectBuffer::empty();
-// if let Some(row) = cred.get(CR_NODE) {
-// let node = txn.get(row)?.unwrap();
-// o = o.as_object().insert(NKU_NODE, node.as_object());
-// }
-// if let Some(role) = cred.get(CR_ROLE) {
-// o = o.as_object().insert(NKU_ROLE, role)
-// }
-// cats.entry(cred.get(CR_KIND).unwrap_or(CRCAT_CREW))
-// .or_default()
-// .push(o);
-// }
-// let mut cats = cats.into_iter().collect::<Vec<_>>();
-// cats.sort_by_key(|(c, _)| match *c {
-// CRCAT_CAST => 0,
-// CRCAT_CREW => 1,
-// _ => 100,
-// });
-// for (cat, elems) in cats {
-// let mut list = ObjectBufferBuilder::default();
-// list.push(NODELIST_DISPLAYSTYLE, NLSTYLE_INLINE);
-// list.push(NODELIST_TITLE, &format!("tag.cred.kind.{cat}"));
-// for item in elems {
-// list.push(NODELIST_ITEM, item.as_object());
-// }
-// page.push(VIEW_NODE_LIST, list.finish().as_object());
-// }
+fn c_children(txn: &mut dyn Transaction, row: u64, node: &Object) -> Result<Vec<Nku<'static>>> {
+ let kind = node.get(NO_KIND).unwrap_or(KIND_COLLECTION);
+ let (order, path) = match kind {
+ KIND_CHANNEL => (SortOrder::Descending, Path(vec![NO_RELEASEDATE.0])),
+ KIND_SEASON | KIND_SHOW => (SortOrder::Ascending, Path(vec![NO_INDEX.0])),
+ _ => (SortOrder::Ascending, Path(vec![NO_TITLE.0])),
+ };
-// Ok(())
-// }
+ let children_rows = txn
+ .query(Query {
+ sort: Sort::Value(ValueSort {
+ multi: MultiBehaviour::First,
+ offset: None,
+ order,
+ path,
+ }),
+ filter: Filter::All(vec![
+ Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
+ Filter::Match(Path(vec![NO_PARENT.0]), row.into()),
+ ]),
+ ..Default::default()
+ })?
+ .collect::<Result<Vec<_>>>()?;
-// fn c_credited(page: &mut ObjectBufferBuilder, txn: &mut dyn Transaction, row: u64) -> Result<()> {
-// let children_rows = txn
-// .query(Query {
-// sort: Sort::Value(ValueSort {
-// multi: MultiBehaviour::First,
-// offset: None,
-// order: SortOrder::Ascending,
-// path: Path(vec![NO_TITLE.0]),
-// }),
-// filter: Filter::All(vec![
-// Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
-// Filter::Match(Path(vec![NO_CREDIT.0, CR_NODE.0]), row.into()),
-// ]),
-// ..Default::default()
-// })?
-// .collect::<Result<Vec<_>>>()?;
+ let mut list = Vec::new();
+ for (row, _) in children_rows {
+ list.push(Nku {
+ node: Cow::Owned(txn.get(row)?.unwrap()),
+ role: None,
+ userdata: Cow::Borrowed(EMPTY),
+ });
+ }
+ Ok(list)
+}
-// if children_rows.is_empty() {
-// return Ok(());
-// }
+fn c_credits(txn: &mut dyn Transaction, node: &Object) -> Result<Vec<(Tag, Vec<Nku<'static>>)>> {
+ if !node.has(NO_CREDIT.0) {
+ return Ok(Vec::new());
+ }
-// let mut list = ObjectBufferBuilder::default();
-// list.push(NODELIST_DISPLAYSTYLE, NLSTYLE_GRID);
-// list.push(NODELIST_TITLE, "node.credited");
+ let mut cats = BTreeMap::<_, Vec<_>>::new();
+ for cred in node.iter(NO_CREDIT) {
+ let Some(row) = cred.get(CR_NODE) else {
+ continue;
+ };
+ cred.get(CR_ROLE);
+ cats.entry(cred.get(CR_KIND).unwrap_or(CRCAT_CREW))
+ .or_default()
+ .push(Nku {
+ node: Cow::Owned(txn.get(row)?.unwrap()),
+ role: cred.get(CR_ROLE).map(String::from).map(Cow::Owned),
+ userdata: Cow::Borrowed(EMPTY),
+ });
+ }
+ let mut cats = cats.into_iter().collect::<Vec<_>>();
+ cats.sort_by_key(|(c, _)| match *c {
+ CRCAT_CAST => 0,
+ CRCAT_CREW => 1,
+ _ => 100,
+ });
+ Ok(cats)
+}
-// for (row, _) in children_rows {
-// list.push(
-// NODELIST_ITEM,
-// Object::EMPTY
-// .insert(NKU_NODE, txn.get(row)?.unwrap().as_object())
-// .as_object(),
-// );
-// }
+fn c_credited(txn: &mut dyn Transaction, row: u64) -> Result<Vec<Nku<'static>>> {
+ let children_rows = txn
+ .query(Query {
+ sort: Sort::Value(ValueSort {
+ multi: MultiBehaviour::First,
+ offset: None,
+ order: SortOrder::Ascending,
+ path: Path(vec![NO_TITLE.0]),
+ }),
+ filter: Filter::All(vec![
+ Filter::Match(Path(vec![NO_VISIBILITY.0]), VISI_VISIBLE.into()),
+ Filter::Match(Path(vec![NO_CREDIT.0, CR_NODE.0]), row.into()),
+ ]),
+ ..Default::default()
+ })?
+ .collect::<Result<Vec<_>>>()?;
-// page.push(VIEW_NODE_LIST, list.finish().as_object());
-// Ok(())
-// }
+ let mut list = Vec::new();
+ for (row, _) in children_rows {
+ list.push(Nku {
+ node: Cow::Owned(txn.get(row)?.unwrap()),
+ role: None,
+ userdata: Cow::Borrowed(EMPTY),
+ });
+ }
+ Ok(list)
+}
diff --git a/ui/locale/src/lib.rs b/ui/locale/src/lib.rs
index 2028418..d50d766 100644
--- a/ui/locale/src/lib.rs
+++ b/ui/locale/src/lib.rs
@@ -48,8 +48,8 @@ pub fn tr(lang: &str, key: &str) -> Cow<'static, str> {
tr_map
.get(key)
.copied()
- .unwrap_or("MISSING TRANSLATION")
- .into()
+ .map(Cow::Borrowed)
+ .unwrap_or_else(|| format!("MISSING TRANSLATION {:?}", key).into())
}
pub fn escape(str: &str) -> String {
diff --git a/ui/src/components/node_card.rs b/ui/src/components/node_card.rs
index 2f5bae7..e9853c7 100644
--- a/ui/src/components/node_card.rs
+++ b/ui/src/components/node_card.rs
@@ -37,7 +37,7 @@ markup::define! {
}
div.subtitle {
span {
- @nku.role.or(node.get(NO_SUBTITLE))
+ @nku.role.as_deref().or(node.get(NO_SUBTITLE))
}
}
}
diff --git a/ui/src/components/node_page.rs b/ui/src/components/node_page.rs
index 2a5848b..265e2d4 100644
--- a/ui/src/components/node_page.rs
+++ b/ui/src/components/node_page.rs
@@ -4,7 +4,11 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{RenderInfo, components::props::Props, page};
+use crate::{
+ RenderInfo,
+ components::{node_card::NodeCard, props::Props},
+ page,
+};
use jellycommon::{
jellyobject::{EMPTY, Object, Tag, TypedTag},
routes::{u_image, u_node_slug_player},
@@ -29,7 +33,13 @@ page!(Player<'_>, |x| x
.into());
markup::define! {
- NodePage<'a>(ri: &'a RenderInfo<'a>, nku: Nku<'a>) {
+ NodePage<'a>(
+ ri: &'a RenderInfo<'a>,
+ nku: Nku<'a>,
+ children: &'a [Nku<'a>],
+ credits: &'a [(Tag, Vec<Nku<'a>>)],
+ credited: &'a [Nku<'a>]
+ ) {
@let node = &nku.node;
@let slug = node.get(NO_SLUG).unwrap_or_default();
@let pics = node.get(NO_PICTURES).unwrap_or(EMPTY);
@@ -145,15 +155,24 @@ markup::define! {
}
}
}
- // @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 } }
- // }}
- // }
+
+ @for (cat, items) in *credits {
+ h2 { @tr(ri.lang, &format!("tag.cred.kind.{cat}")) }
+ ul.nl.inline { @for nku in items {
+ li { @NodeCard { ri, nku } }
+ }}
+ }
+ @if !credited.is_empty() {
+ h2 { @tr(ri.lang, &format!("node.credited")) }
+ ul.nl.grid { @for nku in *credited {
+ li { @NodeCard { ri, nku } }
+ }}
+ }
+ @if !children.is_empty() {
+ ul.nl.grid { @for nku in *children {
+ li { @NodeCard { ri, nku } }
+ }}
+ }
}
Player<'a>(ri: &'a RenderInfo<'a>, nku: Nku<'a>) {