From c28b5fd49c6f1e4f9b8e42239bb09a04b50a4b6a Mon Sep 17 00:00:00 2001 From: alemi Date: Mon, 15 Apr 2024 22:03:32 +0200 Subject: [PATCH] feat(web): object page and some initial routing --- web/src/context.rs | 2 +- web/src/lib.rs | 97 +++++++++++++++++++++++++++++++++++++--------- web/src/main.rs | 4 +- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/web/src/context.rs b/web/src/context.rs index ff1ae07..69ee496 100644 --- a/web/src/context.rs +++ b/web/src/context.rs @@ -15,7 +15,7 @@ pub struct Context { #[derive(Debug, Default)] pub struct Cache { - pub user: DashMap, + pub actors: DashMap, } #[derive(Debug, Default)] diff --git a/web/src/lib.rs b/web/src/lib.rs index cbe079e..3a8cae4 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -15,6 +15,14 @@ struct LoginForm { password: String, } +fn web_uri(kind: &str, url: &str) -> String { + if url.starts_with(URL_BASE) { + format!("/web/{kind}/{}", url.split('/').last().unwrap_or_default().to_string()) + } else { + format!("/web/{kind}/+{}", url.replace("https://", "").replace('/', "@")) + } +} + #[component] pub fn LoginBox( rx: Signal>, @@ -114,7 +122,8 @@ pub fn ActorBanner(object: serde_json::Value) -> impl IntoView {
{id}
}, serde_json::Value::Object(_) => { - let uid = object.id().unwrap_or_default().split('/').last().unwrap_or_default().to_string(); + let uid = object.id().unwrap_or_default().to_string(); + let uri = web_uri("users", &uid); let avatar_url = object.icon().get().map(|x| x.url().id().unwrap_or_default()).unwrap_or_default(); let display_name = object.name().unwrap_or_default().to_string(); let username = object.preferred_username().unwrap_or_default().to_string(); @@ -127,7 +136,7 @@ pub fn ActorBanner(object: serde_json::Value) -> impl IntoView { {display_name} - {username}@{domain} + {username}@{domain} @@ -145,7 +154,7 @@ pub fn Actor() -> impl IntoView { let actor = create_local_resource(move || params.get().get("id").cloned().unwrap_or_default(), |uid| { async move { let uid = format!("{URL_BASE}/users/{uid}"); - match CTX.cache.user.get(&uid) { + match CTX.cache.actors.get(&uid) { Some(x) => x.clone(), None => reqwest::get(uid) .await @@ -163,16 +172,16 @@ pub fn Actor() -> impl IntoView {

- {x.actor_type().unwrap_or(apb::ActorType::Person).as_ref().to_string()} + {x.summary().unwrap_or("").to_string()}

-

{x.summary().unwrap_or("").to_string()}

    +
  • type" "{x.actor_type().unwrap_or(apb::ActorType::Person).as_ref().to_string()}
  • following" "{x.following().get().map(|x| x.total_items().unwrap_or(0))}
  • followers" "{x.followers().get().map(|x| x.total_items().unwrap_or(0))}
  • created" "{x.published().map(|x| x.to_rfc3339())}
  • @@ -184,11 +193,60 @@ pub fn Actor() -> impl IntoView { } #[component] -pub fn Activity(activity: serde_json::Value) -> impl IntoView { +pub fn ObjectPage() -> impl IntoView { + let params = use_params_map(); + let object = create_local_resource(move || params.get().get("id").cloned().unwrap_or_default(), |oid| { + async move { + let uid = format!("{URL_BASE}/objects/{oid}"); + match CTX.cache.actors.get(&uid) { + Some(x) => x.clone(), + None => reqwest::get(uid) + .await + .unwrap() + .json::() + .await + .unwrap(), + } + } + }); + view! { + {move || match object.get() { + Some(o) => view!{ }.into_view(), + None => view! {

    loading ...

    }.into_view(), + }} + } +} + +#[component] +pub fn Object(object: serde_json::Value) -> impl IntoView { + let summary = object.summary().unwrap_or_default().to_string(); + let content = object.content().unwrap_or_default().to_string(); + let date = object.published().map(|x| x.to_rfc3339()).unwrap_or_default(); + let author_id = object.attributed_to().id().unwrap_or_default(); + let author = CTX.cache.actors.get(&author_id).map(|x| view! { }); + view! { + {author} + + + + + + + + + + +
    {summary}
    {content}
    {date}
    + } +} + +#[component] +pub fn InlineActivity(activity: serde_json::Value) -> impl IntoView { let object = activity.clone().object().extract().unwrap_or_else(|| serde_json::Value::String(activity.object().id().unwrap_or_default()) ); let object_id = object.id().unwrap_or_default().to_string(); + let object_uri = web_uri("objects", &object_id); let content = dissolve::strip_html_tags(object.content().unwrap_or_default()); let addressed = activity.addressed(); let audience = format!("[ {} ]", addressed.join(", ")); @@ -200,7 +258,9 @@ pub fn Activity(activity: serde_json::Value) -> impl IntoView { "[private]" }; let title = object.summary().unwrap_or_default().to_string(); - let date = object.published().map(|x| x.to_rfc3339()).unwrap_or_default(); + let date = object.published().map(|x| x.to_rfc3339()).unwrap_or_else(|| + activity.published().map(|x| x.to_rfc3339()).unwrap_or_default() + ); let kind = activity.activity_type().unwrap_or(apb::ActivityType::Activity); view! { {match kind { @@ -208,16 +268,17 @@ pub fn Activity(activity: serde_json::Value) -> impl IntoView { apb::ActivityType::Create => view! {

    {title}

    - {x}

    } - /> + { + content + .into_iter() + .map(|x| view! {

    {x}

    }.into_view()) + .collect::>() + }
    }, kind => view! {
    - {kind.as_ref().to_string()}" >> "{object_id} + {kind.as_ref().to_string()}" >> "{object_id}
    }, }} @@ -240,7 +301,7 @@ pub fn Timeline( view! {
    -
    +
    {format!("{:?}", err.get())}

    } > {move || items.with(|x| match x { None => Ok(view! {

    loading...

    }.into_view()), @@ -256,7 +317,7 @@ pub fn Timeline( view! {
    - +

    } @@ -297,7 +358,7 @@ async fn fetch_activities_with_users( let mut out = Vec::new(); for x in activities { if let Some(uid) = x.actor().id() { - if let Some(actor) = CTX.cache.user.get(&uid) { + if let Some(actor) = CTX.cache.actors.get(&uid) { out.push(x.set_actor(apb::Node::object(actor.clone()))) } else { let mut req = reqwest::Client::new() @@ -309,7 +370,7 @@ async fn fetch_activities_with_users( // TODO don't fail whole timeline fetch when one user fails fetching... let actor = req.send().await?.json::().await?; - CTX.cache.user.insert(uid, actor.clone()); + CTX.cache.actors.insert(uid, actor.clone()); out.push(x.set_actor(apb::Node::object(actor))) } diff --git a/web/src/main.rs b/web/src/main.rs index 5807951..8cf7681 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -3,7 +3,7 @@ use leptos_router::*; use leptos_use::{use_cookie, utils::FromToStringCodec}; use upub_web::{ - Actor, LoginBox, PostBox, Timeline + Actor, LoginBox, ObjectPage, PostBox, Timeline }; @@ -45,7 +45,7 @@ fn main() { } /> } /> - // +