forked from alemi/upub
feat: error boundary for fetching
This commit is contained in:
parent
a5454af7a3
commit
3808d09976
1 changed files with 82 additions and 68 deletions
|
@ -180,6 +180,10 @@ pub fn Activity(activity: serde_json::Value) -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("{0}")]
|
||||||
|
struct OmgReqwestErrorIsNotClonable(String);
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Timeline(
|
pub fn Timeline(
|
||||||
token: Signal<Option<String>>,
|
token: Signal<Option<String>>,
|
||||||
|
@ -187,9 +191,48 @@ pub fn Timeline(
|
||||||
let (timeline, set_timeline) = create_signal(format!("{BASE_URL}/inbox/page"));
|
let (timeline, set_timeline) = create_signal(format!("{BASE_URL}/inbox/page"));
|
||||||
let users : Arc<DashMap<String, serde_json::Value>> = Arc::new(DashMap::new());
|
let users : Arc<DashMap<String, serde_json::Value>> = Arc::new(DashMap::new());
|
||||||
let _users = users.clone(); // TODO i think there is syntactic sugar i forgot?
|
let _users = users.clone(); // TODO i think there is syntactic sugar i forgot?
|
||||||
let items = create_resource(move || timeline.get(), move |feed_url| {
|
let items = create_local_resource(move || timeline.get(), move |feed_url| {
|
||||||
let __users = _users.clone(); // TODO lmao this is meme tier
|
let __users = _users.clone(); // TODO lmao this is meme tier
|
||||||
async move {
|
async move { fetch_activities_with_users(&feed_url, token, __users).await }
|
||||||
|
});
|
||||||
|
view! {
|
||||||
|
<div class="ml-1">
|
||||||
|
<TimelinePicker tx=set_timeline rx=timeline />
|
||||||
|
<ErrorBoundary fallback=move |err| view! { <p>{format!("{:?}", err.get())}</p> } >
|
||||||
|
{move || items.with(|x| match x {
|
||||||
|
None => Ok(view! { <p>loading...</p> }.into_view()),
|
||||||
|
Some(data) => match data {
|
||||||
|
Err(e) => Err(OmgReqwestErrorIsNotClonable(e.to_string())),
|
||||||
|
Ok(values) => Ok(
|
||||||
|
values
|
||||||
|
.iter()
|
||||||
|
.map(|object| {
|
||||||
|
let actor = object.actor().extract().unwrap_or_else(||
|
||||||
|
serde_json::Value::String(object.actor().id().unwrap_or_default())
|
||||||
|
);
|
||||||
|
view! {
|
||||||
|
<div class="post-card ml-1 mr-1">
|
||||||
|
<Actor object=actor />
|
||||||
|
<Activity activity=object.clone() />
|
||||||
|
</div>
|
||||||
|
<hr/ >
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<Fragment>>()
|
||||||
|
.into_view()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</ErrorBoundary>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_activities_with_users(
|
||||||
|
feed_url: &str,
|
||||||
|
token: Signal<Option<String>>,
|
||||||
|
users: Arc<DashMap<String, serde_json::Value>>,
|
||||||
|
) -> reqwest::Result<Vec<serde_json::Value>> {
|
||||||
let mut req = reqwest::Client::new().get(feed_url);
|
let mut req = reqwest::Client::new().get(feed_url);
|
||||||
|
|
||||||
if let Some(token) = token.get() {
|
if let Some(token) = token.get() {
|
||||||
|
@ -198,9 +241,9 @@ pub fn Timeline(
|
||||||
|
|
||||||
let activities : Vec<serde_json::Value> = req
|
let activities : Vec<serde_json::Value> = req
|
||||||
.send()
|
.send()
|
||||||
.await.unwrap()
|
.await?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await.unwrap()
|
.await?
|
||||||
.ordered_items()
|
.ordered_items()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -211,7 +254,7 @@ pub fn Timeline(
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for x in activities {
|
for x in activities {
|
||||||
if let Some(uid) = x.actor().id() {
|
if let Some(uid) = x.actor().id() {
|
||||||
if let Some(actor) = __users.get(&uid) {
|
if let Some(actor) = users.get(&uid) {
|
||||||
out.push(x.set_actor(apb::Node::object(actor.clone())))
|
out.push(x.set_actor(apb::Node::object(actor.clone())))
|
||||||
} else {
|
} else {
|
||||||
let mut req = reqwest::Client::new()
|
let mut req = reqwest::Client::new()
|
||||||
|
@ -221,8 +264,9 @@ pub fn Timeline(
|
||||||
req = req.header("Authorization", format!("Bearer {token}"));
|
req = req.header("Authorization", format!("Bearer {token}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let actor = req.send().await.unwrap().json::<serde_json::Value>().await.unwrap();
|
// TODO don't fail whole timeline fetch when one user fails fetching...
|
||||||
__users.insert(uid, actor.clone());
|
let actor = req.send().await?.json::<serde_json::Value>().await?;
|
||||||
|
users.insert(uid, actor.clone());
|
||||||
|
|
||||||
out.push(x.set_actor(apb::Node::object(actor)))
|
out.push(x.set_actor(apb::Node::object(actor)))
|
||||||
}
|
}
|
||||||
|
@ -231,35 +275,5 @@ pub fn Timeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
Ok(out)
|
||||||
}
|
|
||||||
});
|
|
||||||
view! {
|
|
||||||
<div class="ml-1">
|
|
||||||
<TimelinePicker tx=set_timeline rx=timeline />
|
|
||||||
{move || match items.get() {
|
|
||||||
None => view! { <p>loading...</p> }.into_view(),
|
|
||||||
Some(data) => {
|
|
||||||
view! {
|
|
||||||
<For
|
|
||||||
each=move || data.clone() // TODO wtf this clone??
|
|
||||||
key=|x| x.id().unwrap_or("").to_string()
|
|
||||||
children=move |x: serde_json::Value| {
|
|
||||||
let actor = x.actor().extract().unwrap_or_else(||
|
|
||||||
serde_json::Value::String(x.actor().id().unwrap_or_default())
|
|
||||||
);
|
|
||||||
view! {
|
|
||||||
<div class="post-card ml-1 mr-1">
|
|
||||||
<Actor object=actor />
|
|
||||||
<Activity activity=x />
|
|
||||||
</div>
|
|
||||||
<hr/ >
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}.into_view()
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue