diff --git a/web/src/app.rs b/web/src/app.rs index 0a76836c..788c25ca 100644 --- a/web/src/app.rs +++ b/web/src/app.rs @@ -7,17 +7,19 @@ use leptos_use::{use_cookie, use_cookie_with_options, utils::FromToStringCodec, #[component] pub fn App() -> impl IntoView { - let (auth, set_auth) = use_cookie_with_options::( + let (token, set_token) = use_cookie_with_options::( "token", UseCookieOptions::default() .max_age(1000 * 60 * 60 * 6) ); - let (username, set_username) = use_cookie::("username"); + let (user, set_username) = use_cookie::("username"); + + let auth = Auth { token, user }; provide_context(auth); - let home_tl = Timeline::new(format!("{URL_BASE}/users/{}/inbox/page", username.get().unwrap_or_default())); + let home_tl = Timeline::new(format!("{URL_BASE}/users/{}/inbox/page", auth.username())); let server_tl = Timeline::new(format!("{URL_BASE}/inbox/page")); - let user_tl = Timeline::new(format!("{URL_BASE}/users/{}/outbox/page", username.get().unwrap_or_default())); + let user_tl = Timeline::new(format!("{URL_BASE}/users/{}/outbox/page", auth.username())); let context_tl = Timeline::new(format!("{URL_BASE}/outbox/page")); let reply_controls = ReplyControls::default(); @@ -34,7 +36,7 @@ pub fn App() -> impl IntoView { } }); - if auth.get().is_some() { + if auth.present() { spawn_local(async move { if let Err(e) = home_tl.more(auth).await { tracing::error!("error populating timeline: {e}"); @@ -56,10 +58,8 @@ pub fn App() -> impl IntoView {
@@ -67,9 +67,9 @@ pub fn App() -> impl IntoView {
{move || if advanced.get() { view! { - + }} else { view! { - + }}}
@@ -91,7 +91,7 @@ pub fn App() -> impl IntoView {
} } else { view! { } diff --git a/web/src/auth.rs b/web/src/auth.rs index 46c03a64..33b4f8c7 100644 --- a/web/src/auth.rs +++ b/web/src/auth.rs @@ -2,40 +2,46 @@ use leptos::*; use crate::prelude::*; -pub type Auth = Signal>; pub trait AuthToken { fn present(&self) -> bool; fn token(&self) -> String; + fn username(&self) -> String; + fn outbox(&self) -> String; +} + +#[derive(Debug, Clone, Copy)] +pub struct Auth { + pub token: Signal>, + pub user: Signal>, } #[component] pub fn LoginBox( token_tx: WriteSignal>, - token: Signal>, - username: Signal>, username_tx: WriteSignal>, home_tl: Timeline, server_tl: Timeline, ) -> impl IntoView { + let auth = use_context::().expect("missing auth context"); let username_ref: NodeRef = create_node_ref(); let password_ref: NodeRef = create_node_ref(); view! {
-
- "hi "{move || username.get().unwrap_or_default() } + -
+
() .await else { if let Some(rf) = password_ref.get() { rf.set_value("") }; return }; - logging::log!("logged in until {}", auth.expires); + logging::log!("logged in until {}", auth_response.expires); // update our username and token cookies - let username = auth.user.split('/').last().unwrap_or_default().to_string(); + let username = auth_response.user.split('/').last().unwrap_or_default().to_string(); username_tx.set(Some(username.clone())); - token_tx.set(Some(auth.token)); + token_tx.set(Some(auth_response.token)); // reset home feed and point it to our user's inbox home_tl.reset(format!("{URL_BASE}/users/{}/inbox/page", username)); spawn_local(async move { - if let Err(e) = home_tl.more(token).await { + if let Err(e) = home_tl.more(auth).await { tracing::error!("failed refreshing home timeline: {e}"); } }); // reset server feed: there may be more content now that we're authed server_tl.reset(format!("{URL_BASE}/inbox/page")); spawn_local(async move { - if let Err(e) = server_tl.more(token).await { + if let Err(e) = server_tl.more(auth).await { tracing::error!("failed refreshing server timeline: {e}"); } }); @@ -97,17 +103,20 @@ struct AuthResponse { expires: chrono::DateTime, } -impl AuthToken for Signal> { +impl AuthToken for Auth { fn token(&self) -> String { - match self.get() { - None => String::new(), - Some(x) => x.clone(), - } + self.token.get().unwrap_or_default() } + + fn username(&self) -> String { + self.user.get().unwrap_or_default() + } + fn present(&self) -> bool { - match self.get() { - None => false, - Some(x) => !x.is_empty(), - } + self.token.get().map_or(false, |x| !x.is_empty()) + } + + fn outbox(&self) -> String { + format!("{URL_BASE}/users/{}/outbox", self.user.get().unwrap_or_default()) } } diff --git a/web/src/components/object.rs b/web/src/components/object.rs index 43bf1116..9118c43f 100644 --- a/web/src/components/object.rs +++ b/web/src/components/object.rs @@ -95,7 +95,7 @@ pub fn Object(object: crate::Object) -> impl IntoView { let likes = object.audience().get() .map_or(0, |x| x.total_items().unwrap_or(0)); let already_liked = object.audience().get() - .map_or(false, |x| !x.ordered_items().is_empty()); + .map_or(false, |x| !x.ordered_items().is_empty()); // TODO check if contains my uid let attachments_padding = if object.attachment().is_empty() { None } else { @@ -162,18 +162,17 @@ pub fn LikeButton( view! { { set_clicked.set(false); set_count.set(count.get() + 1); @@ -212,10 +211,10 @@ pub fn ReplyButton(n: u64, target: String) -> impl IntoView { {comments} " 📨" @@ -231,23 +230,22 @@ pub fn RepostButton(n: u64, target: String) -> impl IntoView { view! { set_count.set(count.get() + 1), Err(e) => tracing::error!("failed sending like: {e}"), } diff --git a/web/src/components/timeline.rs b/web/src/components/timeline.rs index 9bfee7ea..60d86f24 100644 --- a/web/src/components/timeline.rs +++ b/web/src/components/timeline.rs @@ -27,14 +27,14 @@ impl Timeline { self.over.set(false); } - pub async fn more(&self, auth: Signal>) -> reqwest::Result<()> { + pub async fn more(&self, auth: Auth) -> reqwest::Result<()> { self.loading.set(true); let res = self.more_inner(auth).await; self.loading.set(false); res } - async fn more_inner(&self, auth: Signal>) -> reqwest::Result<()> { + async fn more_inner(&self, auth: Auth) -> reqwest::Result<()> { use apb::{Collection, CollectionPage}; let feed_url = self.next.get(); @@ -202,10 +202,7 @@ pub fn TimelineFeed(tl: Timeline) -> impl IntoView { } } -async fn process_activities( - activities: Vec, - auth: Signal>, -) -> Vec { +async fn process_activities(activities: Vec, auth: Auth) -> Vec { use apb::ActivityMut; let mut sub_tasks : Vec>>> = Vec::new(); let mut gonna_fetch = BTreeSet::new(); @@ -269,14 +266,14 @@ async fn process_activities( out } -async fn fetch_and_update(kind: FetchKind, id: String, auth: Signal>) { +async fn fetch_and_update(kind: FetchKind, id: String, auth: Auth) { match Http::fetch(&Uri::api(kind, &id, false), auth).await { Ok(data) => CACHE.put(id, Arc::new(data)), Err(e) => console_warn(&format!("could not fetch '{id}': {e}")), } } -async fn fetch_and_update_with_user(kind: FetchKind, id: String, auth: Signal>) { +async fn fetch_and_update_with_user(kind: FetchKind, id: String, auth: Auth) { fetch_and_update(kind.clone(), id.clone(), auth).await; if let Some(obj) = CACHE.get(&id) { if let Some(actor_id) = match kind { diff --git a/web/src/control.rs b/web/src/control.rs index 9d02457c..1f38653b 100644 --- a/web/src/control.rs +++ b/web/src/control.rs @@ -40,7 +40,7 @@ fn post_author(post_id: &str) -> Option { } #[component] -pub fn PostBox(username: Signal>, advanced: WriteSignal) -> impl IntoView { +pub fn PostBox(advanced: WriteSignal) -> impl IntoView { let auth = use_context::().expect("missing auth context"); let reply = use_context::().expect("missing reply controls"); let (posting, set_posting) = create_signal(false); @@ -89,9 +89,9 @@ pub fn PostBox(username: Signal>, advanced: WriteSignal) -> let summary = summary_ref.get().map(|x| x.value()); let content = content_ref.get().map(|x| x.value()).unwrap_or_default(); let (to, cc) = if get_checked(public_ref) { - (apb::Node::links(vec![apb::target::PUBLIC.to_string()]), apb::Node::links(vec![format!("{URL_BASE}/users/{}/followers", username.get().unwrap_or_default())])) + (apb::Node::links(vec![apb::target::PUBLIC.to_string()]), apb::Node::links(vec![format!("{URL_BASE}/users/{}/followers", auth.username())])) } else if get_checked(followers_ref) { - (apb::Node::links(vec![format!("{URL_BASE}/users/{}/followers", username.get().unwrap_or_default())]), apb::Node::Empty) + (apb::Node::links(vec![format!("{URL_BASE}/users/{}/followers", auth.username())]), apb::Node::Empty) } else if get_checked(private_ref) { (apb::Node::links(vec![]), apb::Node::Empty) } else { @@ -105,8 +105,7 @@ pub fn PostBox(username: Signal>, advanced: WriteSignal) -> .set_in_reply_to(apb::Node::maybe_link(reply.reply_to.get())) .set_to(to) .set_cc(cc); - let target_url = format!("{URL_BASE}/users/test/outbox"); - match Http::post(&target_url, &payload, auth).await { + match Http::post(&auth.outbox(), &payload, auth).await { Err(e) => set_error.set(Some(e.to_string())), Ok(()) => { set_error.set(None); @@ -134,7 +133,7 @@ pub fn PostBox(username: Signal>, advanced: WriteSignal) -> } #[component] -pub fn AdvancedPostBox(username: Signal>, advanced: WriteSignal) -> impl IntoView { +pub fn AdvancedPostBox(advanced: WriteSignal) -> impl IntoView { let auth = use_context::().expect("missing auth context"); let (posting, set_posting) = create_signal(false); let (error, set_error) = create_signal(None); @@ -208,7 +207,7 @@ pub fn AdvancedPostBox(username: Signal>, advanced: WriteSignal - + @@ -252,7 +251,7 @@ pub fn AdvancedPostBox(username: Signal>, advanced: WriteSignal set_error.set(Some(e.to_string())), Ok(()) => set_error.set(None), diff --git a/web/src/lib.rs b/web/src/lib.rs index 22f92651..5f196563 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -14,6 +14,7 @@ pub const URL_SENSITIVE: &str = "https://cdn.alemi.dev/social/nsfw.png"; pub const NAME: &str = "μ"; use std::sync::Arc; +use auth::Auth; @@ -81,14 +82,14 @@ impl Http { method: reqwest::Method, url: &str, data: Option<&T>, - token: leptos::Signal> + auth: Auth, ) -> reqwest::Result { use leptos::SignalGet; let mut req = reqwest::Client::new() .request(method, url); - if let Some(auth) = token.get() { + if let Some(auth) = auth.token.get().filter(|x| !x.is_empty()) { req = req.header("Authorization", format!("Bearer {}", auth)); } @@ -101,14 +102,14 @@ impl Http { .error_for_status() } - pub async fn fetch(url: &str, token: leptos::Signal>) -> reqwest::Result { + pub async fn fetch(url: &str, token: Auth) -> reqwest::Result { Self::request::<()>(reqwest::Method::GET, url, None, token) .await? .json::() .await } - pub async fn post(url: &str, data: &T, token: leptos::Signal>) -> reqwest::Result<()> { + pub async fn post(url: &str, data: &T, token: Auth) -> reqwest::Result<()> { Self::request(reqwest::Method::POST, url, Some(data), token) .await?; Ok(())