diff options
| -rw-r--r-- | common/src/api.rs | 4 | ||||
| -rw-r--r-- | common/src/routes.rs | 3 | ||||
| -rw-r--r-- | server/src/request_info.rs | 8 | ||||
| -rw-r--r-- | server/src/ui/node.rs | 53 | ||||
| -rw-r--r-- | server/src/ui_responder.rs | 6 | ||||
| -rw-r--r-- | ui/src/components/mod.rs | 6 | ||||
| -rw-r--r-- | ui/src/components/node_card.rs | 65 | ||||
| -rw-r--r-- | ui/src/components/node_list.rs | 16 | ||||
| -rw-r--r-- | ui/src/old/node_card.rs | 59 |
9 files changed, 150 insertions, 70 deletions
diff --git a/common/src/api.rs b/common/src/api.rs index c25405d..2aac57b 100644 --- a/common/src/api.rs +++ b/common/src/api.rs @@ -34,8 +34,8 @@ fields! { NKU_ROLE: &str = 2027 "role"; NODELIST_TITLE: &str = 2007 "title"; - NODELIST_DISPLAYSTYLE: &str = 2008 "displaystyle"; - NODELIST_ITEM: &str = 2009 "item"; + NODELIST_DISPLAYSTYLE: Tag = 2008 "displaystyle"; + NODELIST_ITEM: Object = 2009 "item"; MESSAGE_KIND: &str = 2029 "kind"; MESSAGE_TEXT: &str = 2030 "text"; diff --git a/common/src/routes.rs b/common/src/routes.rs index 1d3a8da..fcef144 100644 --- a/common/src/routes.rs +++ b/common/src/routes.rs @@ -94,6 +94,3 @@ pub fn u_stats() -> String { pub fn u_search() -> String { "/search".to_owned() } -pub fn u_asset(token: &str, size: usize) -> String { - format!("/asset/{token}?size={size}") -} diff --git a/server/src/request_info.rs b/server/src/request_info.rs index 6ed311e..49b416a 100644 --- a/server/src/request_info.rs +++ b/server/src/request_info.rs @@ -25,7 +25,7 @@ use std::sync::Arc; pub struct RequestInfo<'a> { pub lang: &'a str, pub accept: Accept, - pub debug: bool, + pub debug: &'a str, pub user: Option<ObjectBuffer>, pub state: Arc<State>, } @@ -49,7 +49,11 @@ impl<'a> RequestInfo<'a> { accept: Accept::from_request_ut(request), user: user_from_request(state, request)?, state: state.clone(), - debug: request.query_value::<&str>("debug").is_some(), + debug: request + .query_value::<&str>("debug") + .transpose() + .unwrap() + .unwrap_or("none"), }) } pub fn require_user(&'a self) -> MyResult<Object<'a>> { diff --git a/server/src/ui/node.rs b/server/src/ui/node.rs index 0473a94..d510c71 100644 --- a/server/src/ui/node.rs +++ b/server/src/ui/node.rs @@ -5,11 +5,60 @@ */ use super::error::MyResult; use crate::{request_info::RequestInfo, ui_responder::UiResponse}; -use jellycommon::jellyobject::ObjectBuffer; +use anyhow::{Result, anyhow}; +use jellycommon::{ + jellyobject::{Object, Path}, + *, +}; +use jellydb::{Filter, Query, Sort}; use rocket::get; #[get("/n/<slug>")] pub fn r_node(ri: RequestInfo<'_>, slug: &str) -> MyResult<UiResponse> { ri.require_user()?; - Ok(ri.respond_ui(ObjectBuffer::empty())) + + let mut node = None; + let mut children = None; + ri.state.database.transaction(&mut |txn| { + if let Some(row) = txn.query_single(Query { + filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.as_bytes().to_vec()), + sort: Sort::None, + })? { + node = Some(Object::EMPTY.insert(NKU_NODE, txn.get(row)?.unwrap().as_object())); + + let rows = txn + .query(Query { + sort: Sort::None, + filter: Filter::Match(Path(vec![NO_PARENT.0]), row.to_be_bytes().to_vec()), + })? + .collect::<Result<Vec<_>>>()?; + + children = Some( + rows.into_iter() + .map(|(row, _)| { + Ok(Object::EMPTY.insert(NKU_NODE, txn.get(row)?.unwrap().as_object())) + }) + .collect::<Result<Vec<_>>>()?, + ); + } + Ok(()) + })?; + + let (Some(node), Some(children)) = (node, children) else { + Err(anyhow!("not found"))? + }; + + let children = children.iter().map(|c| c.as_object()).collect::<Vec<_>>(); + + let mut children_list = Object::EMPTY.insert(NODELIST_DISPLAYSTYLE, NLSTYLE_GRID); + children_list = children_list + .as_object() + .insert_multi(NODELIST_ITEM, &children); + + Ok(ri.respond_ui( + Object::EMPTY + .insert(VIEW_NODE_PAGE, node.as_object()) + .as_object() + .insert(VIEW_NODE_LIST, children_list.as_object()), + )) } diff --git a/server/src/ui_responder.rs b/server/src/ui_responder.rs index 2df6208..aed91a2 100644 --- a/server/src/ui_responder.rs +++ b/server/src/ui_responder.rs @@ -23,9 +23,11 @@ pub enum UiResponse { impl RequestInfo<'_> { pub fn respond_ui(&self, view: ObjectBuffer) -> UiResponse { - if self.debug { + if self.debug == "json" { let value = object_to_json(&TAGREG, view.as_object()); UiResponse::Json(serde_json::to_string(&value).unwrap()) + } else if self.debug == "raw" { + UiResponse::Object(view) } else { UiResponse::Html(render_view(self.render_info(), view.as_object())) } @@ -37,7 +39,7 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for UiResponse { match self { UiResponse::Html(x) => RawHtml(x).respond_to(request), UiResponse::Json(x) => RawJson(x).respond_to(request), - UiResponse::Object(_) => todo!(), + UiResponse::Object(x) => x.to_bytes().respond_to(request), } } } diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs index cb9ec1b..3c51535 100644 --- a/ui/src/components/mod.rs +++ b/ui/src/components/mod.rs @@ -7,6 +7,8 @@ pub mod admin; pub mod login; pub mod message; +pub mod node_card; +pub mod node_list; pub mod node_page; pub mod props; pub mod stats; @@ -17,6 +19,7 @@ use crate::{ admin::{AdminDashboard, AdminImport}, login::{AccountLogin, AccountLogout, AccountSetPassword}, message::Message, + node_list::NodeList, node_page::NodePage, }, }; @@ -31,6 +34,9 @@ define! { @if let Some(nku) = view.get(VIEW_NODE_PAGE) { @NodePage { ri, nku } } + @if let Some(nl) = view.get(VIEW_NODE_LIST) { + @NodeList { ri, nl } + } @if let Some(()) = view.get(VIEW_ACCOUNT_LOGIN) { @AccountLogin { ri } } diff --git a/ui/src/components/node_card.rs b/ui/src/components/node_card.rs new file mode 100644 index 0000000..00382c8 --- /dev/null +++ b/ui/src/components/node_card.rs @@ -0,0 +1,65 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2026 metamuffin <metamuffin.org> +*/ + +use crate::{ + RenderInfo, + components::{node_page::aspect_class, props::Props}, +}; +use jellycommon::{ + jellyobject::Object, + routes::{u_image, u_node_slug, u_node_slug_player}, + *, +}; + +markup::define! { + NodeCard<'a>(ri: &'a RenderInfo<'a>, nku: Object<'a>) { + @let node = nku.get(NKU_NODE).unwrap_or_default(); + @let slug = node.get(NO_SLUG).unwrap_or_default(); + @let cls = format!("node card poster {}", aspect_class(node)); + div[class=cls] { + .poster { + a[href=u_node_slug(&slug)] { + img[src=u_image(node.get(NO_PICTURES).unwrap_or_default().get(PICT_COVER).unwrap_or_default(), 512), loading="lazy"]; + } + .cardhover.item { + // @if node.media.is_some() { + a.play.icon[href=u_node_slug_player(&slug)] { "play_arrow" } + // } + @Props { ri, nku: *nku, full: false } + } + } + div.title { + a[href=u_node_slug(&slug)] { + @node.get(NO_TITLE) + } + } + div.subtitle { + span { + @node.get(NO_SUBTITLE) + } + } + } + } + NodeCardWide<'a>(ri: &'a RenderInfo<'a>, nku: Object<'a>) { + div[class="node card widecard poster"] { + // div[class=&format!("poster {}", aspect_class(node.kind))] { + // a[href=u_node_slug(&node.slug)] { + // img[src=u_node_image(&node.slug, PictureSlot::Cover, 512), loading="lazy"]; + // } + // .cardhover.item { + // @if node.media.is_some() { + // a.play.icon[href=u_node_slug_player(&node.slug)] { "play_arrow" } + // } + // } + // } + // div.details { + // a.title[href=u_node_slug(&node.slug)] { @node.title } + // @Props { node, udata, full: false, lang } + // span.overview { @node.description } + // } + } + } +} diff --git a/ui/src/components/node_list.rs b/ui/src/components/node_list.rs index e69de29..c3571de 100644 --- a/ui/src/components/node_list.rs +++ b/ui/src/components/node_list.rs @@ -0,0 +1,16 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2026 metamuffin <metamuffin.org> +*/ + +use crate::{RenderInfo, components::node_card::NodeCard}; +use jellycommon::{jellyobject::Object, *}; + +markup::define! { + NodeList<'a>(ri: &'a RenderInfo<'a>, nl: Object<'a>) { + ul.nodelist { @for nku in nl.iter(NODELIST_ITEM) { + li { @NodeCard { ri, nku } } + }} + } +} diff --git a/ui/src/old/node_card.rs b/ui/src/old/node_card.rs deleted file mode 100644 index f87f490..0000000 --- a/ui/src/old/node_card.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* - This file is part of jellything (https://codeberg.org/metamuffin/jellything) - which is licensed under the GNU Affero General Public License (version 3); see /COPYING. - Copyright (C) 2026 metamuffin <metamuffin.org> -*/ - -use crate::{ - node_page::{NodeUdata, aspect_class}, - scaffold::RenderInfo, -}; -use jellycommon::*; - -markup::define! { - NodeCard<'a>(ri: &'a RenderInfo<'a>, nodeu: NodeUdata<'a>) { - @let cls = format!("node card poster {}", aspect_class(nodeu.node.get(NO_KIND).unwrap_or(KIND_COLLECTION))); - div[class=cls] { - // .poster { - // a[href=u_node_slug(&node.slug)] { - // img[src=u_node_image(&node.slug, PictureSlot::Cover, 512), loading="lazy"]; - // } - // .cardhover.item { - // @if node.media.is_some() { - // a.play.icon[href=u_node_slug_player(&node.slug)] { "play_arrow" } - // } - // @Props { node, udata, full: false, lang } - // } - // } - // div.title { - // a[href=u_node_slug(&node.slug)] { - // @node.title - // } - // } - // div.subtitle { - // span { - // @node.subtitle - // } - // } - } - } - NodeCardWide<'a>(ri: &'a RenderInfo<'a>, nodeu: NodeUdata<'a>) { - div[class="node card widecard poster"] { - // div[class=&format!("poster {}", aspect_class(node.kind))] { - // a[href=u_node_slug(&node.slug)] { - // img[src=u_node_image(&node.slug, PictureSlot::Cover, 512), loading="lazy"]; - // } - // .cardhover.item { - // @if node.media.is_some() { - // a.play.icon[href=u_node_slug_player(&node.slug)] { "play_arrow" } - // } - // } - // } - // div.details { - // a.title[href=u_node_slug(&node.slug)] { @node.title } - // @Props { node, udata, full: false, lang } - // span.overview { @node.description } - // } - } - } -} |