diff options
| author | 2025-04-25 23:00:14 +0100 | |
|---|---|---|
| committer | 2025-04-25 23:00:14 +0100 | |
| commit | b1fc29669b54d544e02d2d73b8dc841d43a5c92a (patch) | |
| tree | c972506b2ab5730a6fb0b750b40871f164d49152 /src | |
| download | macaw-web-b1fc29669b54d544e02d2d73b8dc841d43a5c92a.tar.gz macaw-web-b1fc29669b54d544e02d2d73b8dc841d43a5c92a.tar.bz2 macaw-web-b1fc29669b54d544e02d2d73b8dc841d43a5c92a.zip  | |
initial commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 222 | ||||
| -rw-r--r-- | src/main.rs | 6 | 
2 files changed, 228 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8e4f407 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,222 @@ +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<FilesMem>, +    jid: Arc<JID>, +    file_store: FilesMem, +} + +impl Deref for Client { +    type Target = filamento::Client<FilesMem>; + +    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<Option<(Client, Receiver<UpdateMessage>)>, LocalStorage> = RwSignal::new_local(None); + +    view! { +        {move || match &*app.read() { +            AppState::LoggedOut => view! { <LoginPage set_app set_client=client /> }.into_any(), +            AppState::LoggedIn => { +                if let Some((client, updates)) = client.write_untracked().take() { +                    debug_warn!("opening app"); +                    view! { <Macaw client updates /> }.into_any() +                } else { +                    set_app.set(AppState::LoggedOut); +                    view! { <LoginPage set_app set_client=client /> }.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<AppState>, set_client: RwSignal<Option<(Client, Receiver<UpdateMessage>)>, 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::<LoginError>); + +    let error_message = move || { +        error.with(|error| { +            if let Some(error) = error { +                view! { <div class="error">{error.to_string()}</div> }.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! { +        <div class="center fill"> +            <div id="login-form" class="panel"> +                <div id="hero"> +                    <img src="/assets/icon.png" /> +                    <h1>Macaw Instant Messenger</h1> +                </div> +                {error_message} +                <form on:submit=move |ev| { +                    ev.prevent_default(); +                    login.dispatch(()); +                }> +                    <label for="jid">JID</label> +                    <input +                        disabled=login_pending +                        placeholder="caw@macaw.chat" +                        type="text" +                        bind:value=jid +                        name="jid" +                        id="jid" +                        autofocus="true" +                    /> +                    <label for="password">Password</label> +                    <input +                        disabled=login_pending +                        placeholder="••••••••" +                        type="password" +                        bind:value=password +                        name="password" +                        id="password" +                    /> +                    <div> +                        <label for="remember_me">Remember me</label> +                        <input +                            disabled=login_pending +                            type="checkbox" +                            bind:checked=remember_me +                            name="remember_me" +                            id="remember_me" +                        /> +                    </div> +                    <div> +                        <label for="connect_on_login">Connect on login</label> +                        <input +                            disabled=login_pending +                            type="checkbox" +                            bind:checked=connect_on_login +                            name="connect_on_login" +                            id="connect_on_login" +                        /> +                    </div> +                    <input disabled=login_pending class="button" type="submit" value="Log In" /> +                </form> +            </div> +        </div> +    } +} + +#[component] +fn Macaw( +    // TODO: logout +    // app_state: WriteSignal<Option<essage>)>, LocalStorage>, +    client: Client, +    mut updates: Receiver<UpdateMessage>, +) -> 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" } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8b89d4d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +use leptos::prelude::*; +use macaw_web::App; + +fn main() { +    leptos::mount::mount_to_body(App) +}  | 
