aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/src/api.rs4
-rw-r--r--common/src/routes.rs3
-rw-r--r--server/src/request_info.rs8
-rw-r--r--server/src/ui/node.rs53
-rw-r--r--server/src/ui_responder.rs6
-rw-r--r--ui/src/components/mod.rs6
-rw-r--r--ui/src/components/node_card.rs65
-rw-r--r--ui/src/components/node_list.rs16
-rw-r--r--ui/src/old/node_card.rs59
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 }
- // }
- }
- }
-}