feat(web): better timeline, idk
cant think read the diff bad day
This commit is contained in:
parent
63bde2b5e0
commit
abed664f0a
8 changed files with 102 additions and 47 deletions
|
@ -23,7 +23,7 @@ pub fn LoginBox(
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
<div class="w-100" class:hidden=move || !token.present() >
|
<div class="w-100" class:hidden=move || !token.present() >
|
||||||
"hi "<a href={move || Uri::web("users", &username.get().unwrap_or_default() )} >{move || username.get().unwrap_or_default() }</a>
|
"hi "<a href={move || Uri::web(FetchKind::User, &username.get().unwrap_or_default() )} >{move || username.get().unwrap_or_default() }</a>
|
||||||
<input style="float:right" type="submit" value="logout" on:click=move |_| {
|
<input style="float:right" type="submit" value="logout" on:click=move |_| {
|
||||||
token_tx.set(None);
|
token_tx.set(None);
|
||||||
home_tl.reset(format!("{URL_BASE}/outbox/page"));
|
home_tl.reset(format!("{URL_BASE}/outbox/page"));
|
||||||
|
|
|
@ -8,13 +8,8 @@ use apb::{target::Addressed, Activity, Actor, Base, Object};
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ActivityLine(activity: serde_json::Value) -> impl IntoView {
|
pub fn ActivityLine(activity: serde_json::Value) -> impl IntoView {
|
||||||
let object_id = activity.object().id().unwrap_or_default();
|
let object_id = activity.object().id().unwrap_or_default();
|
||||||
let object = CACHE.get(&object_id).unwrap_or(serde_json::Value::String(object_id.clone()));
|
|
||||||
let addressed = activity.addressed();
|
|
||||||
let actor_id = activity.actor().id().unwrap_or_default();
|
let actor_id = activity.actor().id().unwrap_or_default();
|
||||||
let actor = match CACHE.get(&actor_id) {
|
let actor = CACHE.get_or(&actor_id, serde_json::Value::String(actor_id.clone()));
|
||||||
Some(a) => a,
|
|
||||||
None => serde_json::Value::String(actor_id.clone()),
|
|
||||||
};
|
|
||||||
let avatar = actor.icon().get().map(|x| x.url().id().unwrap_or_default()).unwrap_or_default();
|
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 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();
|
let domain = actor.id().unwrap_or_default().replace("https://", "").split('/').next().unwrap_or_default().to_string();
|
||||||
|
@ -24,13 +19,13 @@ pub fn ActivityLine(activity: serde_json::Value) -> impl IntoView {
|
||||||
<table class="align w-100" style="table-layout: fixed">
|
<table class="align w-100" style="table-layout: fixed">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href={Uri::web("users", &actor_id)} class="clean hover">
|
<a href={Uri::web(FetchKind::User, &actor_id)} class="clean hover">
|
||||||
<img src={avatar} class="avatar-inline mr-s ml-1" /><b>{username}</b><small>@{domain}</small>
|
<img src={avatar} class="avatar-inline mr-s ml-1" /><b>{username}</b><small>@{domain}</small>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="rev" >
|
<td class="rev" >
|
||||||
<code class="color moreinfo" title={object_id.clone()} >{kind.as_ref().to_string()}</code>
|
<code class="color moreinfo" title={object_id.clone()} >{kind.as_ref().to_string()}</code>
|
||||||
<a class="hover ml-1" href={Uri::web("objects", &object_id)} >
|
<a class="hover ml-1" href={Uri::web(FetchKind::Object, &object_id)} >
|
||||||
<DateTime t=activity.published() />
|
<DateTime t=activity.published() />
|
||||||
</a>
|
</a>
|
||||||
<PrivacyMarker addressed=activity.addressed() />
|
<PrivacyMarker addressed=activity.addressed() />
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use apb::{target::Addressed, Activity, Actor, Base, Object};
|
use apb::{target::Addressed, Base, Object};
|
||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -10,9 +10,8 @@ pub fn Object(object: serde_json::Value) -> impl IntoView {
|
||||||
let in_reply_to = object.in_reply_to().id().unwrap_or_default();
|
let in_reply_to = object.in_reply_to().id().unwrap_or_default();
|
||||||
let summary = object.summary().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 content = dissolve::strip_html_tags(object.content().unwrap_or_default());
|
||||||
let date = object.published();
|
|
||||||
let author_id = object.attributed_to().id().unwrap_or_default();
|
let author_id = object.attributed_to().id().unwrap_or_default();
|
||||||
let author = CACHE.get(&author_id).unwrap_or(serde_json::Value::String(author_id.clone()));
|
let author = CACHE.get_or(&author_id, serde_json::Value::String(author_id.clone()));
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
<table class="w-100 post-table pa-1 mb-s" >
|
<table class="w-100 post-table pa-1 mb-s" >
|
||||||
|
@ -20,7 +19,7 @@ pub fn Object(object: serde_json::Value) -> impl IntoView {
|
||||||
Some(view! {
|
Some(view! {
|
||||||
<tr class="post-table" >
|
<tr class="post-table" >
|
||||||
<td class="post-table pa-1" colspan="2" >
|
<td class="post-table pa-1" colspan="2" >
|
||||||
"in reply to "<small><a class="clean hover" href={Uri::web("objects", &in_reply_to)}>{Uri::pretty(&in_reply_to)}</a></small>
|
"in reply to "<small><a class="clean hover" href={Uri::web(FetchKind::Object, &in_reply_to)}>{Uri::pretty(&in_reply_to)}</a></small>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
})
|
})
|
||||||
|
@ -38,7 +37,7 @@ pub fn Object(object: serde_json::Value) -> impl IntoView {
|
||||||
}</td>
|
}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="post-table" >
|
<tr class="post-table" >
|
||||||
<td class="post-table pa-1" ><ActorBanner object=author tiny=true /></td>
|
<td class="post-table pa-1" ><ActorBanner object=author /></td>
|
||||||
<td class="post-table pa-1 center" >
|
<td class="post-table pa-1 center" >
|
||||||
<a class="clean hover" href={oid} target="_blank">
|
<a class="clean hover" href={oid} target="_blank">
|
||||||
<DateTime t=object.published() />
|
<DateTime t=object.published() />
|
||||||
|
@ -52,15 +51,17 @@ pub fn Object(object: serde_json::Value) -> impl IntoView {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ObjectInline(object: serde_json::Value, author: serde_json::Value) -> impl IntoView {
|
pub fn ObjectInline(object: serde_json::Value) -> impl IntoView {
|
||||||
let summary = object.summary().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 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! {
|
view! {
|
||||||
<table class="align w-100">
|
<table class="align w-100">
|
||||||
<tr>
|
<tr>
|
||||||
<td><ActorBanner object=author /></td>
|
<td><ActorBanner object=author /></td>
|
||||||
<td class="rev" >
|
<td class="rev" >
|
||||||
<a class="clean hover" href={Uri::web("objects", object.id().unwrap_or_default())}>
|
<a class="clean hover" href={Uri::web(FetchKind::Object, object.id().unwrap_or_default())}>
|
||||||
<DateTime t=object.published() />
|
<DateTime t=object.published() />
|
||||||
</a>
|
</a>
|
||||||
<PrivacyMarker addressed=object.addressed() />
|
<PrivacyMarker addressed=object.addressed() />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::collections::BTreeSet;
|
use std::{collections::BTreeSet, pin::Pin};
|
||||||
|
|
||||||
use apb::{Activity, Base};
|
use apb::{Activity, Base, Object};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
@ -55,10 +55,10 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView {
|
||||||
match CACHE.get(&id) {
|
match CACHE.get(&id) {
|
||||||
Some(item) => match item.base_type() {
|
Some(item) => match item.base_type() {
|
||||||
Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => {
|
Some(apb::BaseType::Object(apb::ObjectType::Activity(_))) => {
|
||||||
let author_id = item.actor().id().unwrap_or_default();
|
|
||||||
let author = CACHE.get(&author_id).unwrap_or(serde_json::Value::String(author_id.clone()));
|
|
||||||
let object_id = item.object().id().unwrap_or_default();
|
let object_id = item.object().id().unwrap_or_default();
|
||||||
let object = CACHE.get(&object_id).map(|obj| view! { <ObjectInline object=obj author=author /> });
|
let object = CACHE.get(&object_id).map(|obj| {
|
||||||
|
view! { <ObjectInline object=obj /> }
|
||||||
|
});
|
||||||
view! {
|
view! {
|
||||||
<ActivityLine activity=item />
|
<ActivityLine activity=item />
|
||||||
{object}
|
{object}
|
||||||
|
@ -66,7 +66,7 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView {
|
||||||
}.into_view()
|
}.into_view()
|
||||||
},
|
},
|
||||||
Some(apb::BaseType::Object(apb::ObjectType::Note)) => view! {
|
Some(apb::BaseType::Object(apb::ObjectType::Note)) => view! {
|
||||||
<Object object=item />
|
<Object object=item.clone() />
|
||||||
<hr/ >
|
<hr/ >
|
||||||
}.into_view(),
|
}.into_view(),
|
||||||
_ => view! { <p><code>type not implemented</code></p><hr /> }.into_view(),
|
_ => view! { <p><code>type not implemented</code></p><hr /> }.into_view(),
|
||||||
|
@ -96,21 +96,28 @@ async fn process_activities(
|
||||||
auth: Signal<Option<String>>,
|
auth: Signal<Option<String>>,
|
||||||
) -> Vec<String> {
|
) -> Vec<String> {
|
||||||
use apb::ActivityMut;
|
use apb::ActivityMut;
|
||||||
let mut sub_tasks = Vec::new();
|
let mut sub_tasks : Vec<Pin<Box<dyn futures::Future<Output = ()>>>> = Vec::new();
|
||||||
let mut gonna_fetch = BTreeSet::new();
|
let mut gonna_fetch = BTreeSet::new();
|
||||||
|
let mut actors_seen = BTreeSet::new();
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
|
|
||||||
for activity in activities {
|
for activity in activities {
|
||||||
// save embedded object if present
|
// save embedded object if present
|
||||||
if let Some(object) = activity.object().get() {
|
if let Some(object) = activity.object().get() {
|
||||||
|
// also fetch actor attributed to
|
||||||
|
if let Some(attributed_to) = object.attributed_to().id() {
|
||||||
|
actors_seen.insert(attributed_to);
|
||||||
|
}
|
||||||
if let Some(object_uri) = object.id() {
|
if let Some(object_uri) = object.id() {
|
||||||
CACHE.put(object_uri.to_string(), object.clone());
|
CACHE.put(object_uri.to_string(), object.clone());
|
||||||
|
} else {
|
||||||
|
tracing::warn!("embedded object without id: {object:?}");
|
||||||
}
|
}
|
||||||
} else { // try fetching it
|
} else { // try fetching it
|
||||||
if let Some(object_id) = activity.object().id() {
|
if let Some(object_id) = activity.object().id() {
|
||||||
if !gonna_fetch.contains(&object_id) {
|
if !gonna_fetch.contains(&object_id) {
|
||||||
gonna_fetch.insert(object_id.clone());
|
gonna_fetch.insert(object_id.clone());
|
||||||
sub_tasks.push(fetch_and_update("objects", object_id, auth));
|
sub_tasks.push(Box::pin(fetch_and_update_with_user(FetchKind::Object, object_id, auth)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,20 +135,36 @@ async fn process_activities(
|
||||||
if let Some(uid) = activity.actor().id() {
|
if let Some(uid) = activity.actor().id() {
|
||||||
if CACHE.get(&uid).is_none() && !gonna_fetch.contains(&uid) {
|
if CACHE.get(&uid).is_none() && !gonna_fetch.contains(&uid) {
|
||||||
gonna_fetch.insert(uid.clone());
|
gonna_fetch.insert(uid.clone());
|
||||||
sub_tasks.push(fetch_and_update("users", uid, auth));
|
sub_tasks.push(Box::pin(fetch_and_update(FetchKind::User, uid, auth)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for user in actors_seen {
|
||||||
|
sub_tasks.push(Box::pin(fetch_and_update(FetchKind::User, user, auth)));
|
||||||
|
}
|
||||||
|
|
||||||
futures::future::join_all(sub_tasks).await;
|
futures::future::join_all(sub_tasks).await;
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_and_update(kind: &'static str, id: String, auth: Signal<Option<String>>) {
|
async fn fetch_and_update(kind: FetchKind, id: String, auth: Signal<Option<String>>) {
|
||||||
match Http::fetch(&Uri::api(kind, &id, false), auth).await {
|
match Http::fetch(&Uri::api(kind, &id, false), auth).await {
|
||||||
Ok(data) => CACHE.put(id, data),
|
Ok(data) => CACHE.put(id, data),
|
||||||
Err(e) => console_warn(&format!("could not fetch '{id}': {e}")),
|
Err(e) => console_warn(&format!("could not fetch '{id}': {e}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn fetch_and_update_with_user(kind: FetchKind, id: String, auth: Signal<Option<String>>) {
|
||||||
|
fetch_and_update(kind.clone(), id.clone(), auth).await;
|
||||||
|
if let Some(obj) = CACHE.get(&id) {
|
||||||
|
if let Some(actor_id) = match kind {
|
||||||
|
FetchKind::Object => obj.attributed_to().id(),
|
||||||
|
FetchKind::Activity => obj.actor().id(),
|
||||||
|
FetchKind::User | FetchKind::Context => None,
|
||||||
|
} {
|
||||||
|
fetch_and_update(FetchKind::User, actor_id, auth).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use apb::{target::Addressed, Activity, Actor, Base, Object};
|
use apb::{Actor, Base, Object};
|
||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ActorBanner(
|
pub fn ActorBanner(object: serde_json::Value) -> impl IntoView {
|
||||||
object: serde_json::Value,
|
|
||||||
#[prop(optional)]
|
|
||||||
tiny: bool
|
|
||||||
) -> impl IntoView {
|
|
||||||
match object {
|
match object {
|
||||||
serde_json::Value::String(id) => view! {
|
serde_json::Value::String(id) => view! {
|
||||||
<div><b>?</b>" "<a class="clean hover" href={Uri::web("users", &id)}>{Uri::pretty(&id)}</a></div>
|
<div><b>?</b>" "<a class="clean hover" href={Uri::web(FetchKind::User, &id)}>{Uri::pretty(&id)}</a></div>
|
||||||
},
|
},
|
||||||
serde_json::Value::Object(_) => {
|
serde_json::Value::Object(_) => {
|
||||||
let uid = object.id().unwrap_or_default().to_string();
|
let uid = object.id().unwrap_or_default().to_string();
|
||||||
let uri = Uri::web("users", &uid);
|
let uri = Uri::web(FetchKind::User, &uid);
|
||||||
let avatar_url = object.icon().get().map(|x| x.url().id().unwrap_or_default()).unwrap_or_default();
|
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 display_name = object.name().unwrap_or_default().to_string();
|
||||||
let username = object.preferred_username().unwrap_or_default().to_string();
|
let username = object.preferred_username().unwrap_or_default().to_string();
|
||||||
|
|
|
@ -28,12 +28,48 @@ impl ObjectCache {
|
||||||
self.0.get(k).map(|x| x.clone())
|
self.0.get(k).map(|x| x.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_or(&self, k: &str, or: serde_json::Value) -> serde_json::Value {
|
||||||
|
self.get(k).unwrap_or(or)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn put(&self, k: String, v: serde_json::Value) {
|
pub fn put(&self, k: String, v: serde_json::Value) {
|
||||||
self.0.insert(k, v);
|
self.0.insert(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn fetch(&self, k: &str, kind: FetchKind) -> reqwest::Result<serde_json::Value> {
|
||||||
|
match self.get(k) {
|
||||||
|
Some(x) => Ok(x),
|
||||||
|
None => {
|
||||||
|
let obj = reqwest::get(Uri::api(kind, k, true))
|
||||||
|
.await?
|
||||||
|
.json::<serde_json::Value>()
|
||||||
|
.await?;
|
||||||
|
self.put(k.to_string(), obj);
|
||||||
|
Ok(self.get(k).expect("not found in cache after insertion"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum FetchKind {
|
||||||
|
User,
|
||||||
|
Object,
|
||||||
|
Activity,
|
||||||
|
Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for FetchKind {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::User => "users",
|
||||||
|
Self::Object => "objects",
|
||||||
|
Self::Activity => "activities",
|
||||||
|
Self::Context => "context",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Http;
|
pub struct Http;
|
||||||
|
|
||||||
|
@ -79,7 +115,8 @@ impl Http {
|
||||||
pub struct Uri;
|
pub struct Uri;
|
||||||
|
|
||||||
impl Uri {
|
impl Uri {
|
||||||
pub fn full(kind: &str, id: &str) -> String {
|
pub fn full(kind: FetchKind, id: &str) -> String {
|
||||||
|
let kind = kind.as_ref();
|
||||||
if id.starts_with('+') {
|
if id.starts_with('+') {
|
||||||
id.replace('+', "https://").replace('@', "/")
|
id.replace('+', "https://").replace('@', "/")
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,7 +148,8 @@ impl Uri {
|
||||||
/// - https://other.domain.net/unexpected/path/root
|
/// - https://other.domain.net/unexpected/path/root
|
||||||
/// - +other.domain.net@users@root
|
/// - +other.domain.net@users@root
|
||||||
/// - root
|
/// - root
|
||||||
pub fn web(kind: &str, url: &str) -> String {
|
pub fn web(kind: FetchKind, url: &str) -> String {
|
||||||
|
let kind = kind.as_ref();
|
||||||
format!("/web/{kind}/{}", Self::short(url))
|
format!("/web/{kind}/{}", Self::short(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +161,8 @@ impl Uri {
|
||||||
/// - https://other.domain.net/unexpected/path/root
|
/// - https://other.domain.net/unexpected/path/root
|
||||||
/// - +other.domain.net@users@root
|
/// - +other.domain.net@users@root
|
||||||
/// - root
|
/// - root
|
||||||
pub fn api(kind: &str, url: &str, fetch: bool) -> String {
|
pub fn api(kind: FetchKind, url: &str, fetch: bool) -> String {
|
||||||
|
let kind = kind.as_ref();
|
||||||
format!("{URL_BASE}/{kind}/{}{}", Self::short(url), if fetch { "?fetch=true" } else { "" })
|
format!("{URL_BASE}/{kind}/{}{}", Self::short(url), if fetch { "?fetch=true" } else { "" })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,11 +38,11 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
|
||||||
let _id = id.clone(); // wtf triple clone??? TODO!!
|
let _id = id.clone(); // wtf triple clone??? TODO!!
|
||||||
let actor = create_local_resource(move || _id.clone(), move |id| {
|
let actor = create_local_resource(move || _id.clone(), move |id| {
|
||||||
async move {
|
async move {
|
||||||
match CACHE.get(&Uri::full("users", &id)) {
|
match CACHE.get(&Uri::full(FetchKind::User, &id)) {
|
||||||
Some(x) => Some(x.clone()),
|
Some(x) => Some(x.clone()),
|
||||||
None => {
|
None => {
|
||||||
let user : serde_json::Value = Http::fetch(&Uri::api("users", &id, true), auth).await.ok()?;
|
let user : serde_json::Value = Http::fetch(&Uri::api(FetchKind::User, &id, true), auth).await.ok()?;
|
||||||
CACHE.put(Uri::full("users", &id), user.clone());
|
CACHE.put(Uri::full(FetchKind::User, &id), user.clone());
|
||||||
Some(user)
|
Some(user)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
|
||||||
let following = object.following().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
let following = object.following().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
||||||
let followers = object.followers().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
let followers = object.followers().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
||||||
let statuses = object.outbox().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
let statuses = object.outbox().get().map(|x| x.total_items().unwrap_or(0)).unwrap_or(0);
|
||||||
let tl_url = format!("{}/outbox/page", Uri::api("users", &id.clone(), false));
|
let tl_url = format!("{}/outbox/page", Uri::api(FetchKind::User, &id.clone(), false));
|
||||||
if !tl.next.get().starts_with(&tl_url) {
|
if !tl.next.get().starts_with(&tl_url) {
|
||||||
tl.reset(tl_url);
|
tl.reset(tl_url);
|
||||||
}
|
}
|
||||||
|
@ -136,11 +136,11 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
||||||
let auth = use_context::<Auth>().expect("missing auth context");
|
let auth = use_context::<Auth>().expect("missing auth context");
|
||||||
let object = create_local_resource(move || params.get().get("id").cloned().unwrap_or_default(), move |oid| {
|
let object = create_local_resource(move || params.get().get("id").cloned().unwrap_or_default(), move |oid| {
|
||||||
async move {
|
async move {
|
||||||
match CACHE.get(&Uri::full("objects", &oid)) {
|
match CACHE.get(&Uri::full(FetchKind::Object, &oid)) {
|
||||||
Some(x) => Some(x.clone()),
|
Some(x) => Some(x.clone()),
|
||||||
None => {
|
None => {
|
||||||
let obj = Http::fetch::<serde_json::Value>(&Uri::api("objects", &oid, true), auth).await.ok()?;
|
let obj = Http::fetch::<serde_json::Value>(&Uri::api(FetchKind::Object, &oid, true), auth).await.ok()?;
|
||||||
CACHE.put(Uri::full("objects", &oid), obj.clone());
|
CACHE.put(Uri::full(FetchKind::Object, &oid), obj.clone());
|
||||||
Some(obj)
|
Some(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,12 +154,13 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
|
||||||
None => view! { <p> loading ... </p> }.into_view(),
|
None => view! { <p> loading ... </p> }.into_view(),
|
||||||
Some(None) => view! { <p><code>loading failed</code></p> }.into_view(),
|
Some(None) => view! { <p><code>loading failed</code></p> }.into_view(),
|
||||||
Some(Some(o)) => {
|
Some(Some(o)) => {
|
||||||
let tl_url = format!("{}/page", Uri::api("context", &o.context().id().unwrap_or_default(), false));
|
let object = o.clone();
|
||||||
|
let tl_url = format!("{}/page", Uri::api(FetchKind::Context, &o.context().id().unwrap_or_default(), false));
|
||||||
if !tl.next.get().starts_with(&tl_url) {
|
if !tl.next.get().starts_with(&tl_url) {
|
||||||
tl.reset(tl_url);
|
tl.reset(tl_url);
|
||||||
}
|
}
|
||||||
view!{
|
view!{
|
||||||
<Object object=o.clone() />
|
<Object object=object />
|
||||||
<div class="ml-1 mr-1 mt-2">
|
<div class="ml-1 mr-1 mt-2">
|
||||||
<TimelineFeed tl=tl />
|
<TimelineFeed tl=tl />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
Http, Uri,
|
Http, Uri, FetchKind,
|
||||||
CACHE, URL_BASE,
|
CACHE, URL_BASE,
|
||||||
auth::{Auth, AuthToken},
|
auth::{Auth, AuthToken},
|
||||||
page::*,
|
page::*,
|
||||||
|
|
Loading…
Reference in a new issue