use std::{collections::HashSet, str::FromStr}; use filamento::{error::{CommandError, SubscribeError}, roster::ContactStoreFields}; use jid::{BareJID, JID}; use leptos::{html::Input, prelude::*}; use reactive_stores::Store; use thiserror::Error; use crate::{client::Client, roster::{Roster, RosterStoreFields}}; #[derive(Clone, Debug, Error)] pub enum AddContactError { #[error("Missing JID")] MissingJID, #[error("Invalid JID: {0}")] InvalidJID(#[from] jid::ParseError), #[error("Subscription: {0}")] Db(#[from] CommandError), } #[component] // TODO: rename pub fn AddContact() -> impl IntoView { let requests: ReadSignal> = use_context().expect("no pending subscriptions in context"); let set_requests: WriteSignal> = use_context().expect("no pending subscriptions write signal in context"); let roster: Store = use_context().expect("no roster in context"); let jid = RwSignal::new("".to_string()); // TODO: compartmentalise into error component, form component... let (error, set_error) = signal(None::); let error_message = move || { error.with(|error| { if let Some(error) = error { view! {
{error.to_string()}
}.into_any() } else { view! {}.into_any() } }) }; let (add_contact_pending, set_add_contact_pending) = signal(false); let client = use_context::().expect("client not in context"); let client2 = client.clone(); let client3 = client.clone(); let client4 = client.clone(); let add_contact= Action::new_local(move |_| { let client = client.clone(); async move { set_add_contact_pending.set(true); if jid.read_untracked().is_empty() { set_error.set(Some(AddContactError::MissingJID)); set_add_contact_pending.set(false); return; } let jid = match JID::from_str(&jid.read_untracked()) { Ok(j) => j.to_bare(), Err(e) => { set_error.set(Some(e.into())); set_add_contact_pending.set(false); return; } }; let chat_jid = jid; // TODO: more options? match client.buddy_request(chat_jid).await { Ok(c) => c, Err(e) => { set_error.set(Some(e.into())); set_add_contact_pending.set(false); return; }, }; set_add_contact_pending.set(false); } }); let jid_input = NodeRef::::new(); let _focus = Effect::new(move |_| { if let Some(input) = jid_input.get() { let _ = input.focus(); input.set_text_content(Some("")); // input.style("height: 0"); // let height = input.scroll_height(); // input.style(format!("height: {}px", height)); } }); let outgoing = move || roster.contacts().get().into_iter().filter(|(jid, contact)| { match *contact.contact.subscription().read() { filamento::roster::Subscription::None => false, filamento::roster::Subscription::PendingOut => true, filamento::roster::Subscription::PendingIn => false, filamento::roster::Subscription::PendingInPendingOut => true, filamento::roster::Subscription::OnlyOut => false, filamento::roster::Subscription::OnlyIn => false, filamento::roster::Subscription::OutPendingIn => false, filamento::roster::Subscription::InPendingOut => true, filamento::roster::Subscription::Buddy => false, } }).collect::>(); let accept_friend_request = Action::new_local(move |jid: &BareJID| { let client = client2.clone(); let jid = jid.clone(); async move { // TODO: error client.accept_buddy_request(jid).await; } }); let reject_friend_request = Action::new_local(move |jid: &BareJID| { let client = client3.clone(); let jid = jid.clone(); async move { // TODO: error client.unsubscribe_contact(jid.clone()).await; set_requests.write().remove(&jid); } }); let cancel_subscription_request = Action::new_local(move |jid: &BareJID| { let client = client4.clone(); let jid = jid.clone(); async move { // TODO: error client.unsubscribe_from_contact(jid).await; } }); view! {
{error_message}
{move || if !requests.read().is_empty() { view! {

Incoming Subscription Requests

{ let request2 = request.clone(); let request3 = request.clone(); let jid_string = move || request.to_string(); view! {
{jid_string}
Accept
Reject
} }
}.into_any() } else { view! {}.into_any() }} {move || if !outgoing().is_empty() { view! {

Pending Outgoing Subscription Requests

{ let jid2 = jid.clone(); let jid_string = move || jid.to_string(); view! {
{jid_string}
Cancel
} }
}.into_any() } else { view! {}.into_any() }}
} }