diff --git a/web/index.html b/web/index.html index 97418f42..d20e0044 100644 --- a/web/index.html +++ b/web/index.html @@ -152,6 +152,11 @@ img.inline-avatar { max-height: 2em; } + img.attachment { + max-height: 15em; + border: 5px solid #bf616a; + padding: 5px; + } div.tl-header { background-color: #bf616a55; color: #bf616a; diff --git a/web/src/components/mod.rs b/web/src/components/mod.rs index cfe95081..0c906f0a 100644 --- a/web/src/components/mod.rs +++ b/web/src/components/mod.rs @@ -2,13 +2,13 @@ mod activity; pub use activity::ActivityLine; mod object; -pub use object::{Object, ObjectInline}; +pub use object::Object; mod user; pub use user::ActorBanner; mod timeline; -pub use timeline::{TimelineFeed, Timeline}; +pub use timeline::{TimelineFeed, TimelineReplies, Timeline}; use leptos::*; diff --git a/web/src/components/object.rs b/web/src/components/object.rs index cf275fc2..c46b5a65 100644 --- a/web/src/components/object.rs +++ b/web/src/components/object.rs @@ -6,64 +6,28 @@ use apb::{target::Addressed, Base, Object}; #[component] pub fn Object(object: serde_json::Value) -> impl IntoView { - let oid = object.id().unwrap_or_default().to_string(); - let in_reply_to = object.in_reply_to().id().unwrap_or_default(); - let summary = object.summary().unwrap_or_default().to_string(); - let content = dissolve::strip_html_tags(object.content().unwrap_or_default()); - let author_id = object.attributed_to().id().unwrap_or_default(); - let author = CACHE.get_or(&author_id, serde_json::Value::String(author_id.clone())); - view! { -
- - {move || if !in_reply_to.is_empty() { - Some(view! { - - - - }) - } else { None }} - {move || if !summary.is_empty() { - Some(view! { - - - - }) - } else { None }} - - - - - - - -
- "in reply to "{Uri::pretty(&in_reply_to)} -
{summary.clone()}
{ - content.into_iter().map(|x| view! {

{x}

}).collect_view() - }
- - - - -
-
- } -} - -#[component] -pub fn ObjectInline(object: serde_json::Value) -> impl IntoView { + let uid = object.id().unwrap_or_default().to_string(); let summary = object.summary().unwrap_or_default().to_string(); let content = dissolve::strip_html_tags(object.content().unwrap_or_default()); let author_id = object.attributed_to().id().unwrap_or_default(); let author = CACHE.get_or(&author_id, serde_json::Value::String(author_id.clone())); + let attachments = object.attachment() + .map(|x| view! { +

+ }) + .collect_view(); view! { @@ -72,6 +36,7 @@ pub fn ObjectInline(object: serde_json::Value) -> impl IntoView { {if summary.is_empty() { None } else { Some(view! { {summary} })}} {content.into_iter().map(|x| view! {

{x}

}).collect_view()} + {attachments} } } diff --git a/web/src/components/timeline.rs b/web/src/components/timeline.rs index c60d7483..9f3411e8 100644 --- a/web/src/components/timeline.rs +++ b/web/src/components/timeline.rs @@ -44,6 +44,52 @@ impl Timeline { } } +#[component] +pub fn TimelineRepliesRecursive(tl: Timeline, root: String) -> impl IntoView { + let root_values = move || tl.feed + .get() + .into_iter() + .filter_map(|x| CACHE.get(&x)) + .filter(|x| x.object().get().map(|o| o.in_reply_to().id().map(|r| r == root).unwrap_or(false)).unwrap_or(false)) + .collect::>(); + + view! { + + + } + } + / > + } +} + +#[component] +pub fn TimelineReplies(tl: Timeline, root: String) -> impl IntoView { + let auth = use_context::().expect("missing auth context"); + + view! { +
+ +
+
+ +
+ } +} + #[component] pub fn TimelineFeed(tl: Timeline) -> impl IntoView { let auth = use_context::().expect("missing auth context"); @@ -57,7 +103,7 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView { Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => { let object_id = item.object().id().unwrap_or_default(); let object = CACHE.get(&object_id).map(|obj| { - view! { } + view! { } }); view! { diff --git a/web/src/page.rs b/web/src/page.rs index ed9b7c4e..db8d58cc 100644 --- a/web/src/page.rs +++ b/web/src/page.rs @@ -162,7 +162,7 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView { view!{
- +
}.into_view() }, @@ -203,6 +203,7 @@ pub fn DebugPage() -> impl IntoView { let (object, set_object) = create_signal(serde_json::Value::String( "use this view to fetch remote AP objects and inspect their content".into()) ); + let cached_ref: NodeRef = create_node_ref(); let auth = use_context::().expect("missing auth context"); view! {
@@ -210,19 +211,28 @@ pub fn DebugPage() -> impl IntoView {
(&url, auth).await { - Ok(x) => set_object.set(x), - Err(e) => set_object.set(serde_json::Value::String(e.to_string())), + if cached { + match CACHE.get(&fetch_url) { + Some(x) => set_object.set(x), + None => set_object.set(serde_json::Value::String("not in cache!".into())), } - }); + } else { + let url = format!("{URL_BASE}/dbg?id={fetch_url}"); + spawn_local(async move { + match Http::fetch::(&url, auth).await { + Ok(x) => set_object.set(x), + Err(e) => set_object.set(serde_json::Value::String(e.to_string())), + } + }); + } } >
+ {object.in_reply_to().id().map(|reply| view! { + reply + })} + "↗"
+