Compare commits
No commits in common. "7d14bccdeeb77d69eb572dcab10c17a58ce9fe7a" and "6793f0fdc9799710b76dd5173844337495468bfd" have entirely different histories.
7d14bccdee
...
6793f0fdc9
9 changed files with 49 additions and 81 deletions
|
@ -311,7 +311,7 @@ pub fn set_maybe_node(obj: &mut serde_json::Value, key: &str, node: crate::Node<
|
||||||
if node.is_nothing() {
|
if node.is_nothing() {
|
||||||
set_maybe_value(obj, key, None)
|
set_maybe_value(obj, key, None)
|
||||||
} else {
|
} else {
|
||||||
set_maybe_value(obj, key, Some(node.into_inner()))
|
set_maybe_value(obj, key, Some(node.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,20 +133,6 @@ impl<T : super::Base> Node<T> {
|
||||||
|
|
||||||
#[cfg(feature = "unstructured")]
|
#[cfg(feature = "unstructured")]
|
||||||
impl Node<serde_json::Value> {
|
impl Node<serde_json::Value> {
|
||||||
pub fn into_inner(self) -> serde_json::Value {
|
|
||||||
match self {
|
|
||||||
Self::Object(x) => *x,
|
|
||||||
Self::Link(l) => serde_json::Value::String(l.href().unwrap_or_default().to_string()),
|
|
||||||
Self::Empty => serde_json::Value::Null,
|
|
||||||
Self::Array(arr) => serde_json::Value::Array(
|
|
||||||
arr
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| x.into_inner())
|
|
||||||
.collect()
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn link(uri: String) -> Self {
|
pub fn link(uri: String) -> Self {
|
||||||
Node::Link(Box::new(uri))
|
Node::Link(Box::new(uri))
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,18 @@ pub fn ActorHeader() -> impl IntoView {
|
||||||
move |id| {
|
move |id| {
|
||||||
async move {
|
async move {
|
||||||
match cache::OBJECTS.get(&Uri::full(U::Actor, &id)) {
|
match cache::OBJECTS.get(&Uri::full(U::Actor, &id)) {
|
||||||
Some(x) => Some(x.clone()),
|
Some(x) => Ok::<_, String>(x.clone()),
|
||||||
None => {
|
None => {
|
||||||
let user = cache::OBJECTS.resolve(&id, U::Actor, auth).await?;
|
let user : serde_json::Value = Http::fetch(&Uri::api(U::Actor, &id, true), auth)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let user = std::sync::Arc::new(user);
|
||||||
|
let uid = Uri::full(U::Actor, &id);
|
||||||
|
cache::OBJECTS.store(&uid, user.clone());
|
||||||
if let Some(url) = user.url().id().str() {
|
if let Some(url) = user.url().id().str() {
|
||||||
cache::WEBFINGER.store(&url, user.id().unwrap_or_default().to_string());
|
cache::WEBFINGER.store(&url, uid);
|
||||||
}
|
}
|
||||||
Some(user)
|
Ok(user)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +32,8 @@ pub fn ActorHeader() -> impl IntoView {
|
||||||
);
|
);
|
||||||
move || match actor.get() {
|
move || match actor.get() {
|
||||||
None => view! { <Loader /> }.into_view(),
|
None => view! { <Loader /> }.into_view(),
|
||||||
Some(None) => view! { <code class="center cw color">"could not resolve user"</code> }.into_view(),
|
Some(Err(e)) => view! { <code class="center cw color">"could not resolve user: "{e}</code> }.into_view(),
|
||||||
Some(Some(actor)) => {
|
Some(Ok(actor)) => {
|
||||||
let avatar_url = actor.icon().get().map(|x| x.url().id().str().unwrap_or(FALLBACK_IMAGE_URL.into())).unwrap_or(FALLBACK_IMAGE_URL.into());
|
let avatar_url = actor.icon().get().map(|x| x.url().id().str().unwrap_or(FALLBACK_IMAGE_URL.into())).unwrap_or(FALLBACK_IMAGE_URL.into());
|
||||||
let background_url = actor.image().get().map(|x| x.url().id().str().unwrap_or(FALLBACK_IMAGE_URL.into())).unwrap_or(FALLBACK_IMAGE_URL.into());
|
let background_url = actor.image().get().map(|x| x.url().id().str().unwrap_or(FALLBACK_IMAGE_URL.into())).unwrap_or(FALLBACK_IMAGE_URL.into());
|
||||||
let username = actor.preferred_username().unwrap_or_default().to_string();
|
let username = actor.preferred_username().unwrap_or_default().to_string();
|
||||||
|
|
|
@ -10,12 +10,6 @@ use leptos_use::{
|
||||||
UseCookieOptions, UseElementSizeReturn
|
UseCookieOptions, UseElementSizeReturn
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO this is getting out of hand
|
|
||||||
// when we will add lists there will have to potentially be multiple timelines (one per list)
|
|
||||||
// per user, which doesn't scale with this model. we should either take the "go back to where
|
|
||||||
// you were" into our own hands (maybe with timeline "segments"? would also solve slow load,
|
|
||||||
// but infinite-scroll upwards too may be hard to do) or understand how it works (with page
|
|
||||||
// stacks?) and keep timelines local to views.
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Feeds {
|
pub struct Feeds {
|
||||||
pub home: Timeline,
|
pub home: Timeline,
|
||||||
|
@ -25,7 +19,6 @@ pub struct Feeds {
|
||||||
pub user: Timeline,
|
pub user: Timeline,
|
||||||
pub server: Timeline,
|
pub server: Timeline,
|
||||||
pub context: Timeline,
|
pub context: Timeline,
|
||||||
pub replies: Timeline,
|
|
||||||
pub tag: Timeline,
|
pub tag: Timeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +32,6 @@ impl Feeds {
|
||||||
server: Timeline::new(format!("{URL_BASE}/outbox/page")),
|
server: Timeline::new(format!("{URL_BASE}/outbox/page")),
|
||||||
tag: Timeline::new(format!("{URL_BASE}/tags/upub/page")),
|
tag: Timeline::new(format!("{URL_BASE}/tags/upub/page")),
|
||||||
context: Timeline::new(format!("{URL_BASE}/outbox/page")), // TODO ehhh
|
context: Timeline::new(format!("{URL_BASE}/outbox/page")), // TODO ehhh
|
||||||
replies: Timeline::new(format!("{URL_BASE}/outbox/page")), // TODO ehhh
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +42,6 @@ impl Feeds {
|
||||||
self.user.reset(None);
|
self.user.reset(None);
|
||||||
self.server.reset(None);
|
self.server.reset(None);
|
||||||
self.context.reset(None);
|
self.context.reset(None);
|
||||||
self.replies.reset(None);
|
|
||||||
self.tag.reset(None);
|
self.tag.reset(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +155,6 @@ pub fn App() -> impl IntoView {
|
||||||
<Route path="global" view=move || view! { <Feed tl=feeds.global /> } />
|
<Route path="global" view=move || view! { <Feed tl=feeds.global /> } />
|
||||||
<Route path="local" view=move || view! { <Feed tl=feeds.server /> } />
|
<Route path="local" view=move || view! { <Feed tl=feeds.server /> } />
|
||||||
<Route path="notifications" view=move || view! { <Feed tl=feeds.notifications ignore_filters=true /> } />
|
<Route path="notifications" view=move || view! { <Feed tl=feeds.notifications ignore_filters=true /> } />
|
||||||
<Route path="tags/:id" view=move || view! { <HashtagFeed tl=feeds.tag /> } />
|
|
||||||
|
|
||||||
<Route path="about" view=AboutPage />
|
<Route path="about" view=AboutPage />
|
||||||
<Route path="config" view=move || view! { <ConfigPage setter=set_config /> } />
|
<Route path="config" view=move || view! { <ConfigPage setter=set_config /> } />
|
||||||
|
@ -176,14 +166,9 @@ pub fn App() -> impl IntoView {
|
||||||
<Route path="followers" view=move || view! { <FollowList outgoing=false /> } />
|
<Route path="followers" view=move || view! { <FollowList outgoing=false /> } />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
|
<Route path="tags/:id" view=move || view! { <HashtagFeed tl=feeds.tag /> } />
|
||||||
|
|
||||||
<Route path="objects/:id" view=ObjectView >
|
<Route path="objects/:id" view=ObjectView />
|
||||||
<Route path="" view=ObjectContext />
|
|
||||||
<Route path="replies" view=ObjectReplies />
|
|
||||||
// <Route path="liked" view=ObjectLiked />
|
|
||||||
// <Route path="announced" view=ObjectAnnounced />
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
// <Route path="/web/activities/:id" view=move || view! { <ActivityPage tl=context_tl /> } />
|
// <Route path="/web/activities/:id" view=move || view! { <ActivityPage tl=context_tl /> } />
|
||||||
|
|
||||||
<Route path="search" view=SearchPage />
|
<Route path="search" view=SearchPage />
|
||||||
|
|
|
@ -109,8 +109,6 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
|
||||||
on:input=move |ev| set_content.set(event_target_value(&ev))
|
on:input=move |ev| set_content.set(event_target_value(&ev))
|
||||||
></textarea>
|
></textarea>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<table class="align rev w-100">
|
<table class="align rev w-100">
|
||||||
<tr>
|
<tr>
|
||||||
<td><input id="priv-public" type="radio" name="privacy" value="public" title="public" node_ref=public_ref /></td>
|
<td><input id="priv-public" type="radio" name="privacy" value="public" title="public" node_ref=public_ref /></td>
|
||||||
|
|
|
@ -41,7 +41,7 @@ pub mod cache {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LookupStatus<T> {
|
pub enum LookupStatus<T> {
|
||||||
Resolving, // TODO use this to avoid fetching twice!
|
Resolving,
|
||||||
Found(T),
|
Found(T),
|
||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
@ -90,26 +90,6 @@ impl<T> Cache for DashmapCache<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DashmapCache<Object> {
|
|
||||||
pub async fn resolve(&self, key: &str, kind: UriClass, auth: Auth) -> Option<Object> {
|
|
||||||
let full_key = Uri::full(kind, key);
|
|
||||||
match self.get(&full_key) {
|
|
||||||
Some(x) => Some(x),
|
|
||||||
None => {
|
|
||||||
let obj = match Http::fetch::<serde_json::Value>(&Uri::api(kind, key, true), auth).await {
|
|
||||||
Ok(obj) => Arc::new(obj),
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("failed loading object from backend: {e}");
|
|
||||||
return None;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cache::OBJECTS.store(&full_key, obj.clone());
|
|
||||||
Some(obj)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO would be cool unifying a bit the fetch code too
|
// TODO would be cool unifying a bit the fetch code too
|
||||||
|
|
||||||
impl DashmapCache<Object> {
|
impl DashmapCache<Object> {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
pub mod view;
|
pub mod view;
|
||||||
pub mod context;
|
|
||||||
pub mod replies;
|
|
||||||
|
|
||||||
pub mod item;
|
pub mod item;
|
||||||
|
|
||||||
pub mod attachment;
|
pub mod attachment;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_router::*;
|
use leptos_router::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -8,21 +10,39 @@ use apb::{Base, Object};
|
||||||
pub fn ObjectView() -> impl IntoView {
|
pub fn ObjectView() -> impl IntoView {
|
||||||
let params = use_params_map();
|
let params = use_params_map();
|
||||||
let auth = use_context::<Auth>().expect("missing auth context");
|
let auth = use_context::<Auth>().expect("missing auth context");
|
||||||
|
let feeds = use_context::<Feeds>().expect("missing feeds context");
|
||||||
let object = create_local_resource(
|
let object = create_local_resource(
|
||||||
move || params.get().get("id").cloned().unwrap_or_default(),
|
move || params.get().get("id").cloned().unwrap_or_default(),
|
||||||
move |oid| async move {
|
move |oid| async move {
|
||||||
let obj = cache::OBJECTS.resolve(&oid, U::Object, auth).await?;
|
let obj = match cache::OBJECTS.get(&Uri::full(U::Object, &oid)) {
|
||||||
if let Ok(author) = obj.attributed_to().id() {
|
Some(x) => x.clone(),
|
||||||
cache::OBJECTS.resolve(author, U::Actor, auth).await;
|
None => {
|
||||||
|
let obj = match Http::fetch::<serde_json::Value>(&Uri::api(U::Object, &oid, true), auth).await {
|
||||||
|
Ok(obj) => Arc::new(obj),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("failed loading object from backend: {e}");
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Ok(author) = obj.attributed_to().id() {
|
||||||
|
if let Ok(user) = Http::fetch::<serde_json::Value>(
|
||||||
|
&Uri::api(U::Actor, author, true), auth
|
||||||
|
).await {
|
||||||
|
cache::OBJECTS.store(&Uri::full(U::Actor, author), Arc::new(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache::OBJECTS.store(&Uri::full(U::Object, &oid), obj.clone());
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Ok(ctx) = obj.context().id() {
|
||||||
|
let tl_url = format!("{}/context/page", Uri::api(U::Object, ctx, false));
|
||||||
|
if !feeds.context.next.get_untracked().starts_with(&tl_url) {
|
||||||
|
feeds.context.reset(Some(tl_url));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(obj)
|
|
||||||
|
|
||||||
// if let Ok(ctx) = obj.context().id() {
|
Some(obj)
|
||||||
// let tl_url = format!("{}/context/page", Uri::api(U::Object, ctx, false));
|
|
||||||
// if !feeds.context.next.get_untracked().starts_with(&tl_url) {
|
|
||||||
// feeds.context.reset(Some(tl_url));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -35,14 +55,12 @@ pub fn ObjectView() -> impl IntoView {
|
||||||
},
|
},
|
||||||
Some(Some(o)) => {
|
Some(Some(o)) => {
|
||||||
let object = o.clone();
|
let object = o.clone();
|
||||||
let base = Uri::web(U::Object, o.id().unwrap_or_default());
|
|
||||||
view!{
|
view!{
|
||||||
<Object object=object />
|
<Object object=object />
|
||||||
<hr class="color ma-2" />
|
<hr class="color ma-2" />
|
||||||
<code class="cw color center mt-1 mb-1 ml-3 mr-3">
|
<div class="mr-1-r ml-1-r">
|
||||||
<a href=format!("{base}/context")><b>context</b></a> | <a href=format!("{base}/replies")><b>replies</b></a>
|
<Thread tl=feeds.context root=o.id().unwrap_or_default().to_string() />
|
||||||
</code>
|
</div>
|
||||||
<Outlet />
|
|
||||||
}.into_view()
|
}.into_view()
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -19,8 +19,6 @@ pub use crate::{
|
||||||
view::ObjectView,
|
view::ObjectView,
|
||||||
attachment::Attachment,
|
attachment::Attachment,
|
||||||
item::{Object, Summary, LikeButton, RepostButton, ReplyButton},
|
item::{Object, Summary, LikeButton, RepostButton, ReplyButton},
|
||||||
context::ObjectContext,
|
|
||||||
replies::ObjectReplies,
|
|
||||||
},
|
},
|
||||||
timeline::{
|
timeline::{
|
||||||
Timeline,
|
Timeline,
|
||||||
|
|
Loading…
Reference in a new issue