From b9a25bc3d7065196358f6ad62b643918d83393c0 Mon Sep 17 00:00:00 2001 From: alemi Date: Thu, 23 May 2024 16:41:45 +0200 Subject: [PATCH] feat(web): mentions, dynamically resolved and shown --- web/Cargo.toml | 3 ++- web/src/components/post.rs | 53 +++++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 7af22302..a7dc2f5f 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -34,4 +34,5 @@ lazy_static = "1.4" chrono = { version = "0.4", features = ["serde"] } web-sys = { version = "0.3", features = ["Screen"] } mdhtml = { path = "../mdhtml/" } -jrd = "0.1.0" +jrd = "0.1" +tld = "2.35" diff --git a/web/src/components/post.rs b/web/src/components/post.rs index 49846b0e..3a9b39d2 100644 --- a/web/src/components/post.rs +++ b/web/src/components/post.rs @@ -1,7 +1,8 @@ use apb::{ActivityMut, Base, BaseMut, Object, ObjectMut}; use leptos::*; -use crate::prelude::*; +use leptos_use::DebounceOptions; +use crate::{prelude::*, WEBFINGER}; #[derive(Debug, Clone, Copy, Default)] pub struct ReplyControls { @@ -34,11 +35,34 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView { let reply = use_context::().expect("missing reply controls"); let (posting, set_posting) = create_signal(false); let (error, set_error) = create_signal(None); + let (content, set_content) = create_signal("".to_string()); let summary_ref: NodeRef = create_node_ref(); - let content_ref: NodeRef = create_node_ref(); let public_ref: NodeRef = create_node_ref(); let followers_ref: NodeRef = create_node_ref(); let private_ref: NodeRef = create_node_ref(); + + // TODO is this too abusive with resources? im even checking if TLD exists... + let mentions = create_local_resource( + move || content.get(), + move |c| async move { + let mut out = Vec::new(); + for word in c.split(' ') { + if !word.starts_with('@') { break }; + let stripped = word.replacen('@', "", 1); + if let Some((user, domain)) = stripped.split_once('@') { + if let Some(tld) = domain.split('.').last() { + if tld::exist(tld) { + if let Some(uid) = WEBFINGER.blocking_resolve(user, domain).await { + out.push(uid); + } + } + } + } + } + out + }, + ); + view! {
{move || @@ -51,13 +75,22 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView { on:click=move|_| reply.clear() title={format!("> {r} | ctx: {}", reply.context.get().unwrap_or_default())} > - "📨" + "✒️" - {actor_strip} + {actor_strip} + "["reply"]" } }) } + {move || + mentions.get() + .map(|x| x.into_iter().map(|u| match CACHE.get(&u) { + Some(u) => view! { "📨" }.into_view(), + None => view! { "📨"{u} }.into_view(), + }) + .collect_view()) + } @@ -65,7 +98,10 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView {
- + @@ -76,7 +112,7 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView { set_posting.set(true); spawn_local(async move { let summary = get_if_some(summary_ref); - let content = content_ref.get().map(|x| x.value()).unwrap_or_default(); + let content = content.get(); let mut cc_vec = Vec::new(); let mut to_vec = Vec::new(); if get_checked(followers_ref) { @@ -92,6 +128,9 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView { } } } + for mention in mentions.get().as_deref().unwrap_or(&[]) { + to_vec.push(mention.to_string()); + } let payload = serde_json::Value::Object(serde_json::Map::default()) .set_object_type(Some(apb::ObjectType::Note)) .set_summary(summary.as_deref()) @@ -105,7 +144,7 @@ pub fn PostBox(advanced: WriteSignal) -> impl IntoView { Ok(()) => { set_error.set(None); if let Some(x) = summary_ref.get() { x.set_value("") } - if let Some(x) = content_ref.get() { x.set_value("") } + set_content.set("".to_string()); }, } set_posting.set(false);