diff --git a/web/src/components/activity.rs b/web/src/components/activity.rs index 8821c716..0f9da67d 100644 --- a/web/src/components/activity.rs +++ b/web/src/components/activity.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use leptos::*; use crate::prelude::*; @@ -6,10 +7,10 @@ use apb::{target::Addressed, Activity, Actor, Base, Object}; #[component] -pub fn ActivityLine(activity: serde_json::Value) -> impl IntoView { +pub fn ActivityLine(activity: crate::Object) -> impl IntoView { let object_id = activity.object().id().unwrap_or_default(); let actor_id = activity.actor().id().unwrap_or_default(); - let actor = CACHE.get_or(&actor_id, serde_json::Value::String(actor_id.clone())); + let actor = CACHE.get_or(&actor_id, serde_json::Value::String(actor_id.clone()).into()); let avatar = actor.icon().get().map(|x| x.url().id().unwrap_or_default()).unwrap_or_default(); let username = actor.preferred_username().unwrap_or_default().to_string(); let domain = actor.id().unwrap_or_default().replace("https://", "").split('/').next().unwrap_or_default().to_string(); diff --git a/web/src/components/object.rs b/web/src/components/object.rs index e2545330..3a6bb390 100644 --- a/web/src/components/object.rs +++ b/web/src/components/object.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use leptos::*; use crate::{prelude::*, URL_SENSITIVE}; @@ -77,11 +79,11 @@ pub fn Attachment( #[component] -pub fn Object(object: serde_json::Value) -> impl IntoView { +pub fn Object(object: crate::Object) -> impl IntoView { let oid = object.id().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 author = CACHE.get_or(&author_id, serde_json::Value::String(author_id.clone()).into()); let sensitive = object.sensitive().unwrap_or_default(); let addressed = object.addressed(); let public = addressed.iter().any(|x| x.as_str() == apb::target::PUBLIC); diff --git a/web/src/components/timeline.rs b/web/src/components/timeline.rs index a934cc82..9bfee7ea 100644 --- a/web/src/components/timeline.rs +++ b/web/src/components/timeline.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeSet, pin::Pin}; +use std::{collections::BTreeSet, pin::Pin, sync::Arc}; use apb::{Activity, Base, Object}; use leptos::*; @@ -78,13 +78,13 @@ pub fn TimelineRepliesRecursive(tl: Timeline, root: String) -> impl IntoView { Some(apb::ObjectType::Activity(_)) => x.object().id().map(|o| o == root).unwrap_or(false), _ => x.in_reply_to().id().map(|r| r == root).unwrap_or(false), }) - .collect::>(); + .collect::>(); view! { { let oid = object.object().id().unwrap_or_default().to_string(); @@ -220,7 +220,7 @@ async fn process_activities( actors_seen.insert(attributed_to); } if let Some(object_uri) = object.id() { - CACHE.put(object_uri.to_string(), object.clone()); + CACHE.put(object_uri.to_string(), Arc::new(object.clone())); } else { tracing::warn!("embedded object without id: {object:?}"); } @@ -239,7 +239,7 @@ async fn process_activities( out.push(activity_id.to_string()); CACHE.put( activity_id.to_string(), - activity.clone().set_object(apb::Node::maybe_link(object_id)) + Arc::new(activity.clone().set_object(apb::Node::maybe_link(object_id))) ); } else if let Some(object_id) = activity.object().id() { out.push(object_id); @@ -271,7 +271,7 @@ async fn process_activities( async fn fetch_and_update(kind: FetchKind, id: String, auth: Signal>) { match Http::fetch(&Uri::api(kind, &id, false), auth).await { - Ok(data) => CACHE.put(id, data), + Ok(data) => CACHE.put(id, Arc::new(data)), Err(e) => console_warn(&format!("could not fetch '{id}': {e}")), } } diff --git a/web/src/components/user.rs b/web/src/components/user.rs index 60fe8997..57916566 100644 --- a/web/src/components/user.rs +++ b/web/src/components/user.rs @@ -5,10 +5,10 @@ use apb::{Actor, Base, Object}; #[component] -pub fn ActorBanner(object: serde_json::Value) -> impl IntoView { - match object { +pub fn ActorBanner(object: crate::Object) -> impl IntoView { + match object.as_ref() { serde_json::Value::String(id) => view! { -
?" "{Uri::pretty(&id)}
+
?" "{Uri::pretty(id)}
}, serde_json::Value::Object(_) => { let uid = object.id().unwrap_or_default().to_string(); diff --git a/web/src/lib.rs b/web/src/lib.rs index f2161dd7..22f92651 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -21,23 +21,25 @@ lazy_static::lazy_static! { pub static ref CACHE: ObjectCache = ObjectCache::default(); } +pub type Object = Arc; + #[derive(Debug, Clone, Default)] -pub struct ObjectCache(pub Arc>); +pub struct ObjectCache(pub Arc>); impl ObjectCache { - pub fn get(&self, k: &str) -> Option { + pub fn get(&self, k: &str) -> Option { self.0.get(k).map(|x| x.clone()) } - pub fn get_or(&self, k: &str, or: serde_json::Value) -> serde_json::Value { + pub fn get_or(&self, k: &str, or: Object) -> Object { self.get(k).unwrap_or(or) } - pub fn put(&self, k: String, v: serde_json::Value) { + pub fn put(&self, k: String, v: Object) { self.0.insert(k, v); } - pub async fn fetch(&self, k: &str, kind: FetchKind) -> reqwest::Result { + pub async fn fetch(&self, k: &str, kind: FetchKind) -> reqwest::Result { match self.get(k) { Some(x) => Ok(x), None => { @@ -45,7 +47,7 @@ impl ObjectCache { .await? .json::() .await?; - self.put(k.to_string(), obj); + self.put(k.to_string(), Arc::new(obj)); Ok(self.get(k).expect("not found in cache after insertion")) } } diff --git a/web/src/page.rs b/web/src/page.rs index 03940f55..6d8e4fb9 100644 --- a/web/src/page.rs +++ b/web/src/page.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use apb::{Actor, Base, Collection, Object}; use leptos::*; @@ -51,6 +53,7 @@ pub fn UserPage(tl: Timeline) -> impl IntoView { Some(x) => Some(x.clone()), None => { let user : serde_json::Value = Http::fetch(&Uri::api(FetchKind::User, &id, true), auth).await.ok()?; + let user = Arc::new(user); CACHE.put(Uri::full(FetchKind::User, &id), user.clone()); Some(user) }, @@ -163,6 +166,7 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView { Some(x) => Some(x.clone()), None => { let obj = Http::fetch::(&Uri::api(FetchKind::Object, &oid, true), auth).await.ok()?; + let obj = Arc::new(obj); CACHE.put(Uri::full(FetchKind::Object, &oid), obj.clone()); Some(obj) } @@ -225,9 +229,9 @@ pub fn TimelinePage(name: &'static str, tl: Timeline) -> impl IntoView { #[component] pub fn DebugPage() -> impl IntoView { - let (object, set_object) = create_signal(serde_json::Value::String( + let (object, set_object) = create_signal(Arc::new(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"); let (query, set_query) = create_signal("".to_string()); @@ -242,14 +246,14 @@ pub fn DebugPage() -> impl IntoView { 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())), + None => set_object.set(Arc::new(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())), + Ok(x) => set_object.set(Arc::new(x)), + Err(e) => set_object.set(Arc::new(serde_json::Value::String(e.to_string()))), } }); } @@ -273,7 +277,7 @@ pub fn DebugPage() -> impl IntoView {
-				{move || serde_json::to_string_pretty(&object.get()).unwrap_or("unserializable".to_string())}
+				{move || serde_json::to_string_pretty(object.get().as_ref()).unwrap_or("unserializable".to_string())}
 			
}