From 0e902e1f0a56e2f59cb91065a0ad8600631a1e49 Mon Sep 17 00:00:00 2001 From: cel 🌸 Date: Sat, 26 Apr 2025 14:57:41 +0100 Subject: before removing leptos-query --- Cargo.lock | 137 +++++++++++++++++++++++++++++-- Cargo.toml | 10 ++- index.html | 2 + src/lib.rs | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 3 + 5 files changed, 389 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf9b487..471b27e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,6 +318,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-link", ] @@ -375,6 +376,16 @@ dependencies = [ "winnow 0.7.7", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const_format" version = "0.2.34" @@ -734,6 +745,7 @@ dependencies = [ "jid", "lampada", "rusqlite", + "serde", "sha1", "sha2", "sha3", @@ -1424,6 +1436,7 @@ name = "jid" version = "0.1.0" dependencies = [ "rusqlite", + "serde", ] [[package]] @@ -1469,6 +1482,7 @@ dependencies = [ "jid", "luz", "peanuts", + "serde", "stanza", "thiserror 2.0.12", "tokio", @@ -1504,11 +1518,11 @@ dependencies = [ "leptos_hot_reload", "leptos_macro", "leptos_server", - "oco_ref", + "oco_ref 0.2.0", "or_poisoned", "paste", "reactive_graph", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "serde", "serde_qs", @@ -1607,6 +1621,33 @@ dependencies = [ "web-sys", ] +[[package]] +name = "leptos_reactive" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4161acbf80f59219d8d14182371f57302bc7ff81ee41aba8ba1ff7295727f23" +dependencies = [ + "base64", + "cfg-if", + "futures", + "indexmap", + "js-sys", + "oco_ref 0.1.1", + "paste", + "pin-project", + "rustc-hash 1.1.0", + "self_cell", + "serde", + "serde-wasm-bindgen", + "serde_json", + "slotmap", + "thiserror 1.0.69", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "leptos_server" version = "0.7.8" @@ -1741,13 +1782,21 @@ dependencies = [ name = "macaw-web" version = "0.1.0" dependencies = [ + "console_error_panic_hook", "filamento", + "futures", + "indexmap", "jid", "leptos", "leptos_meta", + "leptos_reactive", + "serde", "stylance", "thiserror 2.0.12", "tokio", + "tracing", + "tracing-wasm", + "uuid", ] [[package]] @@ -1984,6 +2033,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "oco_ref" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51ebcefb2f0b9a5e0bea115532c8ae4215d1b01eff176d0f4ba4192895c2708" +dependencies = [ + "serde", + "thiserror 1.0.69", +] + [[package]] name = "oco_ref" version = "0.2.0" @@ -2434,7 +2493,7 @@ dependencies = [ "hydration_context", "or_poisoned", "pin-project-lite", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "serde", "slotmap", @@ -2454,7 +2513,7 @@ dependencies = [ "paste", "reactive_graph", "reactive_stores_macro", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -2578,6 +2637,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -2667,6 +2732,12 @@ dependencies = [ "libc", ] +[[package]] +name = "self_cell" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -2685,6 +2756,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -2814,6 +2896,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2856,6 +2947,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ + "serde", "version_check", ] @@ -3038,14 +3130,14 @@ dependencies = [ "js-sys", "linear-map", "next_tuple", - "oco_ref", + "oco_ref 0.2.0", "once_cell", "or_poisoned", "parking_lot", "paste", "reactive_graph", "reactive_stores", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "slotmap", "throw_error", @@ -3118,6 +3210,16 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "throw_error" version = "0.2.0" @@ -3289,6 +3391,28 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + [[package]] name = "trust-dns-proto" version = "0.22.0" @@ -3442,6 +3566,7 @@ checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ "getrandom 0.3.2", "js-sys", + "serde", "wasm-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index c42775a..5dc98c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,18 @@ version = "0.1.0" edition = "2024" [dependencies] -filamento = { path = "../luz/filamento" } +console_error_panic_hook = "0.1.7" +filamento = { path = "../luz/filamento", features = ["serde"] } +futures = "0.3.31" +indexmap = "2.9.0" jid = { path = "../luz/jid" } leptos = { version = "0.7.8", features = ["csr"] } leptos_meta = "0.7.8" +leptos_reactive = { version = "0.6.15", features = ["csr"] } +serde = "1.0.219" stylance = "0.6.0" thiserror = "2.0.12" tokio = { version = "1.44.2", features = ["sync"] } +tracing = "0.1.41" +tracing-wasm = "0.2.1" +uuid = { version = "1.16.0", features = ["v4"] } diff --git a/index.html b/index.html index 6579e70..a3ab66f 100644 --- a/index.html +++ b/index.html @@ -15,6 +15,8 @@ + Macaw IM + diff --git a/src/lib.rs b/src/lib.rs index 8e4f407..b6e7cea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,18 @@ use std::{ops::{Deref, DerefMut}, str::FromStr, sync::Arc, thread::sleep, time::{self, Duration}}; -use filamento::{db::Db, files::FilesMem, UpdateMessage}; +use filamento::{chat::{Chat, Message}, db::Db, error::{CommandError, ConnectionError, DatabaseError}, files::FilesMem, user::User, UpdateMessage}; +use indexmap::IndexMap; use jid::JID; -use leptos::{logging::debug_warn, prelude::*, task::spawn_local}; +use leptos::{prelude::*, task::spawn_local}; +use futures::stream::StreamExt; use leptos_meta::Stylesheet; +use leptos_query::{create_query, provide_query_client, provide_query_client_with_options, DefaultQueryOptions, QueryOptions, QueryResult, QueryScope}; +use leptos_reactive::{SignalGetUntracked, SignalStream}; use stylance::import_style; use thiserror::Error; use tokio::{sync::mpsc::Receiver}; +use tracing::debug; +use uuid::Uuid; pub enum AppState { LoggedOut, @@ -44,7 +50,6 @@ pub fn App() -> impl IntoView { 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); @@ -63,6 +68,8 @@ pub enum LoginError { MissingJID, #[error("Invalid JID: {0}")] InvalidJID(#[from] jid::ParseError), + #[error("Connection Error: {0}")] + ConnectionError(#[from] CommandError), } #[component] @@ -71,8 +78,8 @@ fn LoginPage(set_app: WriteSignal, set_client: RwSignal); + let (error, set_error) = signal_local(None::); let error_message = move || { error.with(|error| { if let Some(error) = error { @@ -85,7 +92,7 @@ fn LoginPage(set_app: WriteSignal, set_client: RwSignal, set_client: RwSignal {}, + Err(e) => { + set_error.set(Some(e.into())); + set_login_pending.set(false); + return + }, + } + } // debug!("before setting app state"); set_client.set(Some((client, updates))); - debug_warn!("going"); set_app.set(AppState::LoggedIn); - debug_warn!("gone"); } }); @@ -196,27 +211,235 @@ fn Macaw( client: Client, mut updates: Receiver, ) -> impl IntoView { + // TODO: is there some kind of context_local? let client = RwSignal::new_local(client); provide_context(client); - debug_warn!("ajsdlkfjalsf"); + + let (new_messages, set_new_messages) = signal(None::<(JID, MacawMessage)>); + provide_context(new_messages); + + provide_query_client_with_options(DefaultQueryOptions { + resource_option: leptos_query::ResourceOption::Local, + ..Default::default() + }); 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!(), + UpdateMessage::Online(online, items) => {}, + UpdateMessage::Offline(offline) => {}, + UpdateMessage::RosterUpdate(contact, user) => {}, + UpdateMessage::RosterDelete(jid) => {}, + UpdateMessage::Presence { from, presence } => {}, + UpdateMessage::Message { to, from, message } => { + let new_message = MacawMessage::got_message_and_user(message, from); + set_new_messages.set(Some((to, new_message))); + }, + UpdateMessage::MessageDelivery { id, chat, delivery } => {}, + UpdateMessage::SubscriptionRequest(jid) => {}, + UpdateMessage::NickChanged { jid, nick } => {}, + UpdateMessage::AvatarChanged { jid, id } => {}, + } + } + }); + + view! { + "logged in" + + } +} + +fn chat_query() -> QueryScope> { + create_query(get_chat, QueryOptions::default()) +} + +async fn get_chat(jid: JID) -> Result { + let client: Client = use_context::>().unwrap().read_untracked().clone(); + client.get_chat(jid).await.map_err(|e| e.to_string()) +} + +fn user_query() -> QueryScope> { + create_query(get_user, QueryOptions::default()) +} + +async fn get_user(jid: JID) -> Result { + let client: Client = use_context::>().unwrap().read_untracked().clone(); + client.get_user(jid).await.map_err(|e| e.to_string()) +} + +fn message_query() -> QueryScope> { + create_query(get_message, QueryOptions::default()) +} + +async fn get_message(id: Uuid) -> Result { + let client: Client = use_context::>().unwrap().read_untracked().clone(); + client.get_message(id).await.map_err(|e| e.to_string()) +} + +// fn got_chat(chat: Chat) -> QueryScope { +// let fetcher = move |_| { +// let chat = (&chat).clone(); +// async { +// MacawChat { +// chat +// } +// }}; +// create_query(fetcher, QueryOptions::default()) +// } + +#[derive(Clone)] +struct MacawChat { + chat: ReadSignal>>>, + user: ReadSignal>>>, +} + +impl MacawChat { + fn got_chat_and_user(chat: Chat, user: User) -> Self { + let correspondent = chat.correspondent.clone(); + let chat_query = chat_query(); + chat_query.set_query_data(correspondent.clone(), Ok(chat)); + let chat = chat_query.use_query(move || correspondent.clone()); + let jid = user.jid.clone(); + let user_query = user_query(); + user_query.set_query_data(jid.clone(), Ok(user)); + let user = user_query.use_query(move || jid.clone()); + Self { + chat: ReadSignal::from_stream_unsync(chat.data.to_stream()), + user: ReadSignal::from_stream_unsync(user.data.to_stream()), + } + } + + fn get(jid: JID) -> Self { + let jid1 = jid.clone(); + let chat = chat_query().use_query(move || (&jid1).clone()); + let user = user_query().use_query(move || (&jid).clone()); + Self { + chat: ReadSignal::from_stream_unsync(chat.data.to_stream()), + user: ReadSignal::from_stream_unsync(user.data.to_stream()), + } + } +} + +impl Deref for MacawChat { + type Target = ReadSignal>>>; + + fn deref(&self) -> &Self::Target { + &self.chat + } +} + +impl DerefMut for MacawChat { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.chat + } +} + +#[derive(Clone)] +struct MacawMessage { + message: ReadSignal>>>, + user: ReadSignal>>>, +} + +impl MacawMessage { + fn got_message_and_user(message: Message, user: User) -> Self { + debug!("executing the got message"); + let message_id = message.id; + let message_query = message_query(); + message_query.set_query_data(message.id, Ok(message)); + let message = message_query.use_query(move || message_id); + let jid = user.jid.clone(); + let user_query = user_query(); + user_query.set_query_data(jid.clone(), Ok(user)); + let user = user_query.use_query(move || jid.clone()); + Self { + message: ReadSignal::from_stream_unsync(message.data.to_stream()), + user: ReadSignal::from_stream_unsync(user.data.to_stream()), + } + } +} + +impl Deref for MacawMessage { + type Target = ReadSignal>>>; + + fn deref(&self) -> &Self::Target { + &self.message + } +} + +impl DerefMut for MacawMessage { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.message + } +} + +fn ChatsList() -> impl IntoView { + let chats: LocalResource, LocalStorage>, WriteSignal, LocalStorage>), String>> = LocalResource::new(move || async move || -> Result<_, _> { + let client: Client = use_context::>().unwrap().read().clone(); + let chats = client.get_chats_ordered_with_latest_messages_and_users().await.map_err(|e| e.to_string())?; + let chats = chats.into_iter().map(|((chat, chat_user), (message, message_user))| { + (chat.correspondent.clone(), (MacawChat::got_chat_and_user(chat, chat_user), MacawMessage::got_message_and_user(message, message_user))) + + }).collect::>(); + let (chats, set_chats) = signal_local(chats); + Ok((chats, set_chats)) + }()); + + // TODO: filter new messages signal + let new_messages_signal: ReadSignal> = use_context().unwrap(); + OnceResource::new(async move { + let mut new_message = new_messages_signal.to_stream(); + match chats.await { + Ok((c, set_c)) => { + while let Some(new_message) = new_message.next().await { + debug!("got new message in let"); + if let Some((to, new_message)) = new_message { + if let Some((chat, _latest_message)) = set_c.write().shift_remove(&to) { + debug!("chat existed"); + set_c.write().insert_before(0, to, (chat, new_message)); + debug!("done setting"); + } else { + debug!("the chat didn't exist"); + let chat = MacawChat::get(to.clone()); + set_c.write().insert_before(0, to, (chat, new_message)); + debug!("done setting"); + } + } + } + debug!("set the new message"); } + Err(_) => {}, } }); - view! { "logged in" } + view! { +
+

Chats

+
+ {move || { + if let Some(chats) = &*chats.read() { + match &**chats { + Ok((chats, _)) => { + let chats = chats.clone(); + view! { + +

{chat.0.to_string()}

+
+ } + .into_any() + } + Err(e) => { + view! {
{format!("{}", e)}
}.into_any() + } + } + } else { + None::.into_any() + } + }} +
+
+ } } diff --git a/src/main.rs b/src/main.rs index 8b89d4d..fd4a1de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,5 +2,8 @@ use leptos::prelude::*; use macaw_web::App; fn main() { + tracing_wasm::set_as_global_default(); + console_error_panic_hook::set_once(); + leptos::mount::mount_to_body(App) } -- cgit