use std::{ops::{Deref, DerefMut}, str::FromStr, sync::Arc, thread::sleep, time::{self, Duration}}; use filamento::{db::Db, files::FilesMem, UpdateMessage}; use jid::JID; use leptos::{logging::debug_warn, prelude::*, task::spawn_local}; use leptos_meta::Stylesheet; use stylance::import_style; use thiserror::Error; use tokio::{sync::mpsc::Receiver}; pub enum AppState { LoggedOut, LoggedIn, } #[derive(Clone)] pub struct Client { client: filamento::Client, jid: Arc, file_store: FilesMem, } impl Deref for Client { type Target = filamento::Client; fn deref(&self) -> &Self::Target { &self.client } } impl DerefMut for Client { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.client } } #[component] pub fn App() -> impl IntoView { let (app, set_app) = signal(AppState::LoggedOut); let client: RwSignal)>, LocalStorage> = RwSignal::new_local(None); view! { {move || match &*app.read() { AppState::LoggedOut => view! { }.into_any(), AppState::LoggedIn => { if let Some((client, updates)) = client.write_untracked().take() { debug_warn!("opening app"); view! { }.into_any() } else { set_app.set(AppState::LoggedOut); view! { }.into_any() } } }} } } #[derive(Clone, Debug, Error)] pub enum LoginError { #[error("Missing Password")] MissingPassword, #[error("Missing JID")] MissingJID, #[error("Invalid JID: {0}")] InvalidJID(#[from] jid::ParseError), } #[component] fn LoginPage(set_app: WriteSignal, set_client: RwSignal)>, LocalStorage>) -> impl IntoView { let jid = RwSignal::new("".to_string()); let password = RwSignal::new("".to_string()); let remember_me = RwSignal::new(false); let connect_on_login = RwSignal::new(true); 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 (login_pending, set_login_pending) = signal(false); let login = Action::new(move |_| { async move { set_login_pending.set(true); if jid.read_untracked().is_empty() { set_error.set(Some(LoginError::MissingJID)); set_login_pending.set(false); return } if password.read_untracked().is_empty() { set_error.set(Some(LoginError::MissingPassword)); set_login_pending.set(false); return } let jid = match JID::from_str(&jid.read_untracked()) { Ok(j) => j, Err(e) => { set_error.set(Some(e.into())); set_login_pending.set(false); return }, }; // initialise the client let db = Db::create_connect_and_migrate("mem.db").await.unwrap(); let files_mem = FilesMem::new(); let (client, updates) = filamento::Client::new(jid.clone(), password.read_untracked().clone(), db, files_mem.clone()); // TODO: remember_me // TODO: connect on login let client = Client { client, jid: Arc::new(jid), file_store: files_mem, }; // debug!("before setting app state"); set_client.set(Some((client, updates))); debug_warn!("going"); set_app.set(AppState::LoggedIn); debug_warn!("gone"); } }); view! {

Macaw Instant Messenger

{error_message}
} } #[component] fn Macaw( // TODO: logout // app_state: WriteSignal)>, LocalStorage>, client: Client, mut updates: Receiver, ) -> impl IntoView { let client = RwSignal::new_local(client); provide_context(client); debug_warn!("ajsdlkfjalsf"); let updates_routine = OnceResource::new(async move { debug_warn!("started"); while let Some(update) = updates.recv().await { match update { UpdateMessage::Online(online, items) => todo!(), UpdateMessage::Offline(offline) => todo!(), UpdateMessage::RosterUpdate(contact, user) => todo!(), UpdateMessage::RosterDelete(jid) => todo!(), UpdateMessage::Presence { from, presence } => todo!(), UpdateMessage::Message { to, from, message } => todo!(), UpdateMessage::MessageDelivery { id, chat, delivery } => todo!(), UpdateMessage::SubscriptionRequest(jid) => todo!(), UpdateMessage::NickChanged { jid, nick } => todo!(), UpdateMessage::AvatarChanged { jid, id } => todo!(), } } }); view! { "logged in" } }