diff options
-rw-r--r-- | Cargo.lock | 149 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/lib.rs | 363 |
3 files changed, 307 insertions, 208 deletions
@@ -4,14 +4,14 @@ version = 4 [[package]] name = "accessory" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb3791c4beae5b827e93558ac83a88e63a841aad61759a05d9b577ef16030470" +checksum = "28e416a3ab45838bac2ab2d81b1088d738d7b2d2c5272a54d39366565a29bd80" dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -90,7 +90,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -118,7 +118,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -129,7 +129,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -143,7 +143,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -159,7 +159,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -259,9 +259,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder-lite" @@ -310,9 +310,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -545,7 +545,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -556,7 +556,7 @@ checksum = "2364b9aa47e460ce9bca6ac1777d14c98eef7e274eb077beed49f3adc94183ed" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -576,7 +576,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "unicode-xid", ] @@ -599,7 +599,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -715,7 +715,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -868,7 +868,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1243,7 +1243,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1325,14 +1325,14 @@ checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "indexed_db_futures" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94eebf0199c01a1560770d964efb1fb09183a3afc0b1c1397fac1bfdbfa7d6cf" +checksum = "ac6179b8a3ef894b4645271be8131a444679c8d39b8307d70cbb34e471b68ea4" dependencies = [ "accessory", "cfg-if", @@ -1359,7 +1359,7 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1380,7 +1380,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1579,7 +1579,7 @@ dependencies = [ "quote", "rstml", "serde", - "syn 2.0.100", + "syn 2.0.101", "walkdir", ] @@ -1601,7 +1601,7 @@ dependencies = [ "quote", "rstml", "server_fn_macro", - "syn 2.0.100", + "syn 2.0.101", "uuid", ] @@ -1760,6 +1760,7 @@ dependencies = [ "futures", "getrandom 0.2.16", "jid", + "js-sys", "lazy_static", "peanuts", "pin-project", @@ -1790,6 +1791,7 @@ dependencies = [ "leptos", "leptos_meta", "leptos_reactive", + "reactive_stores", "serde", "stylance", "thiserror 2.0.12", @@ -1819,7 +1821,7 @@ dependencies = [ "proc-macro2", "quote", "sealed", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1831,7 +1833,7 @@ dependencies = [ "proc-macro2", "quote", "sealed", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1844,7 +1846,7 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1856,7 +1858,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1982,7 +1984,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2082,7 +2084,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2162,7 +2164,7 @@ dependencies = [ [[package]] name = "peanuts" version = "0.1.0" -source = "git+https://bunny.garden/peanuts#a0cc279551267dfa7a17fe4b449dd1a1fcd3526f" +source = "git+https://bunny.garden/peanuts#9aa337bd703426f737c5d4f94fe84c4a646b7836" dependencies = [ "async-recursion", "circular", @@ -2201,7 +2203,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2251,7 +2253,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2273,7 +2275,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2304,7 +2306,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "version_check", "yansi", ] @@ -2325,7 +2327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2371,7 +2373,7 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2526,7 +2528,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2610,7 +2612,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.100", + "syn 2.0.101", "syn_derive", "thiserror 2.0.12", ] @@ -2706,7 +2708,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2775,7 +2777,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2850,7 +2852,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "xxhash-rust", ] @@ -2861,7 +2863,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb8b274f568c94226a8045668554aace8142a59b8bca5414ac5a79627c825568" dependencies = [ "server_fn_macro", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3044,7 +3046,7 @@ dependencies = [ "proc-macro2", "quote", "stylance-core", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3066,9 +3068,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -3084,7 +3086,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3095,7 +3097,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3196,7 +3198,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3207,7 +3209,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3289,7 +3291,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3323,14 +3325,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee80eb7e23abaa636caa9afe6042c6d8a8c0686165b8165f4fbf2988f0dd347f" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -3340,27 +3342,34 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow 0.7.7", ] [[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3379,7 +3388,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3481,7 +3490,7 @@ checksum = "3c36781cc0e46a83726d9879608e4cf6c2505237e263a8eb8c24502989cfdb28" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3646,7 +3655,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -3681,7 +3690,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3760,7 +3769,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3771,7 +3780,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4027,7 +4036,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] @@ -4048,7 +4057,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4068,7 +4077,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "synstructure", ] @@ -4091,7 +4100,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -9,9 +9,10 @@ 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 = { version = "0.7.5", features = ["csr"] } leptos_meta = "0.7.8" leptos_reactive = { version = "0.6.15", features = ["csr"] } +reactive_stores = "0.1.8" serde = "1.0.219" stylance = "0.6.0" thiserror = "2.0.12" @@ -1,16 +1,36 @@ -use std::{ops::{Deref, DerefMut}, str::FromStr, sync::Arc, thread::sleep, time::{self, Duration}}; - -use filamento::{chat::{Chat, Message}, db::Db, error::{CommandError, ConnectionError, DatabaseError}, files::FilesMem, user::User, UpdateMessage}; +use std::{ + borrow::Borrow, + cell::RefCell, + collections::HashMap, + marker::PhantomData, + ops::{Deref, DerefMut}, + rc::Rc, + str::FromStr, + sync::{atomic::AtomicUsize, Arc}, + thread::sleep, + time::{self, Duration}, +}; + +use filamento::{ + chat::{Chat, Message}, + db::Db, + error::{CommandError, ConnectionError, DatabaseError}, + files::FilesMem, + user::User, + UpdateMessage, +}; +use futures::stream::StreamExt; use indexmap::IndexMap; use jid::JID; -use leptos::{prelude::*, task::spawn_local}; -use futures::stream::StreamExt; +use leptos::{ + prelude::*, + task::{spawn, spawn_local}, +}; 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 reactive_stores::Store; use stylance::import_style; use thiserror::Error; -use tokio::{sync::mpsc::Receiver}; +use tokio::sync::{mpsc::Receiver, Mutex}; use tracing::debug; use uuid::Uuid; @@ -43,7 +63,7 @@ impl DerefMut for 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); + let client: RwSignal<Option<(Client, Receiver<UpdateMessage>)>> = RwSignal::new(None); view! { {move || match &*app.read() { @@ -73,13 +93,13 @@ pub enum LoginError { } #[component] -fn LoginPage(set_app: WriteSignal<AppState>, set_client: RwSignal<Option<(Client, Receiver<UpdateMessage>)>, LocalStorage>) -> impl IntoView { +fn LoginPage(set_app: WriteSignal<AppState>, set_client: RwSignal<Option<(Client, Receiver<UpdateMessage>)>>) -> 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_local(None::<LoginError>); + let (error, set_error) = signal(None::<LoginError>); let error_message = move || { error.with(|error| { if let Some(error) = error { @@ -128,7 +148,7 @@ fn LoginPage(set_app: WriteSignal<AppState>, set_client: RwSignal<Option<(Client jid: Arc::new(jid), file_store: files_mem, }; - // TODO: connect on login + if *connect_on_login.read_untracked() { match client.connect().await { Ok(_) => {}, @@ -211,19 +231,26 @@ fn Macaw( client: Client, mut updates: Receiver<UpdateMessage>, ) -> impl IntoView { - // TODO: is there some kind of context_local? - let client = RwSignal::new_local(client); provide_context(client); 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 messages_store: StateStore<Uuid, Store<Message>> = StateStore::new(); + provide_context(RwSignal::new_local(messages_store)); + let chats_store: StateStore<JID, Store<Chat>> = StateStore::new(); + provide_context(RwSignal::new_local(chats_store)); + let users_store: StateStore<JID, Store<User>> = StateStore::new(); + provide_context(RwSignal::new_local(users_store)); - let updates_routine = OnceResource::new(async move { + // // here we create a signal in the root that can be consumed + // // anywhere in the app. + // let (count, set_count) = signal(0); + // // we'll pass the setter to specific components, + // // but provide the count itself to the whole app via context + // provide_context(count); + + OnceResource::new(async move { while let Some(update) = updates.recv().await { match update { UpdateMessage::Online(online, items) => {}, @@ -243,85 +270,148 @@ fn Macaw( } }); - view! { - "logged in" - <ChatsList /> + view! { <ChatsList /> } +} + +// V has to be an arc signal +struct StateStore<K, V> { + store: Rc<RefCell<HashMap<K, (V, usize)>>>, +} + +impl<K, V> Clone for StateStore<K, V> { + fn clone(&self) -> Self { + Self { + store: self.store.clone(), + } } } -fn chat_query() -> QueryScope<JID, Result<Chat, String>> { - create_query(get_chat, QueryOptions::default()) +impl<K, V> StateStore<K, V> { + pub fn new() -> Self { + Self { + store: Rc::new(RefCell::new(HashMap::new())), + } + } +} + +impl<K: Eq + std::hash::Hash + Clone, V: Clone> StateStore<K, V> { + pub fn store(&self, key: K, value: V) -> StateListener<K, V> { + let mut store = self.store.borrow_mut(); + if let Some((v, count)) = store.get_mut(&key) { + *v = value.clone(); + *count += 1; + StateListener { + value, + cleaner: StateCleaner { key, _ty: PhantomData }, + } + } else { + store.insert(key.clone(), (value.clone(), 1)); + StateListener { + value, + cleaner: StateCleaner { + key, + _ty: PhantomData, + } + } + } + } + + pub fn init() { + + } +} + +impl<K: Eq + std::hash::Hash , V> StateStore<K, V> { + pub fn update(&self, key: &K, value: V) { + if let Some((v, _)) = self.store.borrow_mut().get_mut(key) { + *v = value; + } + } + + pub fn modify(&self, key: &K, modify: impl Fn(&mut V)) { + if let Some((v, _)) = self.store.borrow_mut().get_mut(key) { + modify(v); + } + } + + fn remove(&self, key: &K) { + let mut store = self.store.borrow_mut(); + if let Some((_v, count)) = store.get_mut(key) { + *count -= 1; + if *count == 0 { + store.remove(key); + } + } + } } -async fn get_chat(jid: JID) -> Result<Chat, String> { - let client: Client = use_context::<RwSignal<Client, LocalStorage>>().unwrap().read_untracked().clone(); - client.get_chat(jid).await.map_err(|e| e.to_string()) +#[derive(Clone)] +struct StateListener<K, V> where K: Eq + std::hash::Hash + 'static, V: 'static { + value: V, + cleaner: StateCleaner<K, V> } -fn user_query() -> QueryScope<JID, Result<User, String>> { - create_query(get_user, QueryOptions::default()) +impl<K: std::cmp::Eq + std::hash::Hash, V> Deref for StateListener<K, V> { + type Target = V; + + fn deref(&self) -> &Self::Target { + &self.value + } } -async fn get_user(jid: JID) -> Result<User, String> { - let client: Client = use_context::<RwSignal<Client, LocalStorage>>().unwrap().read_untracked().clone(); - client.get_user(jid).await.map_err(|e| e.to_string()) +impl<K: std::cmp::Eq + std::hash::Hash, V> DerefMut for StateListener<K, V> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.value + } } -fn message_query() -> QueryScope<Uuid, Result<Message, String>> { - create_query(get_message, QueryOptions::default()) +struct StateCleaner<K, V> where K: Eq + std::hash::Hash + 'static, V: 'static { + key: K, + _ty: PhantomData<V>, + // state_store: StateStore<K, V>, } -async fn get_message(id: Uuid) -> Result<Message, String> { - let client: Client = use_context::<RwSignal<Client, LocalStorage>>().unwrap().read_untracked().clone(); - client.get_message(id).await.map_err(|e| e.to_string()) +impl<K, V> Clone for StateCleaner<K, V> where K: Eq + std::hash::Hash + Clone { + fn clone(&self) -> Self { + let state_store = use_context::<RwSignal<StateStore<K, V>, LocalStorage>>().unwrap(); + if let Some((_v, count)) = state_store.read_untracked().store.borrow_mut().get_mut(&self.key) { + *count += 1; + } + Self { + key: self.key.clone(), + _ty: PhantomData, + } + } } -// fn got_chat(chat: Chat) -> QueryScope<JID, MacawChat> { -// let fetcher = move |_| { -// let chat = (&chat).clone(); -// async { -// MacawChat { -// chat -// } -// }}; -// create_query(fetcher, QueryOptions::default()) -// } +impl<K: Eq + std::hash::Hash + 'static, V: 'static> Drop for StateCleaner<K, V> { + fn drop(&mut self) { + let state_store = use_context::<RwSignal<StateStore<K, V>, LocalStorage>>().unwrap(); + state_store.read_untracked().remove(&self.key) + } +} #[derive(Clone)] struct MacawChat { - chat: ReadSignal<Option<Option<Result<Chat, String>>>>, - user: ReadSignal<Option<Option<Result<User, String>>>>, + chat: StateListener<JID, Store<Chat>>, + user: StateListener<JID, Store<User>>, } 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()); + let chat_state_store: RwSignal<StateStore<JID, Store<Chat>>, LocalStorage> = use_context().expect("no chat state store"); + let user_state_store: RwSignal<StateStore<JID, Store<User>>, LocalStorage> = use_context().expect("no user state store"); + let user = user_state_store.read_untracked().store(user.jid.clone(), Store::new(user)); + let chat = chat_state_store.read_untracked().store(chat.correspondent.clone(), Store::new(chat)); 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()), + chat, + user, } } } impl Deref for MacawChat { - type Target = ReadSignal<Option<Option<Result<Chat, String>>>>; + type Target = StateListener<JID, Store<Chat>>; fn deref(&self) -> &Self::Target { &self.chat @@ -336,30 +426,25 @@ impl DerefMut for MacawChat { #[derive(Clone)] struct MacawMessage { - message: ReadSignal<Option<Option<Result<Message, String>>>>, - user: ReadSignal<Option<Option<Result<User, String>>>>, + message: StateListener<Uuid, Store<Message>>, + user: StateListener<JID, Store<User>>, } 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()); + let message_state_store: RwSignal<StateStore<Uuid, Store<Message>>, LocalStorage> = use_context().expect("no message state store"); + let user_state_store: RwSignal<StateStore<JID, Store<User>>, LocalStorage> = use_context().expect("no user state store"); + let message = message_state_store.read_untracked().store(message.id, Store::new(message)); + let user = user_state_store.read_untracked().store(user.jid.clone(), Store::new(user)); Self { - message: ReadSignal::from_stream_unsync(message.data.to_stream()), - user: ReadSignal::from_stream_unsync(user.data.to_stream()), + message, + user, } } } impl Deref for MacawMessage { - type Target = ReadSignal<Option<Option<Result<Message, String>>>>; + type Target = StateListener<Uuid, Store<Message>>; fn deref(&self) -> &Self::Target { &self.message @@ -372,73 +457,77 @@ impl DerefMut for MacawMessage { } } +struct MacawUser { + user: StateListener<JID, Store<User>>, +} + +impl Deref for MacawUser { + type Target = StateListener<JID, Store<User>>; + + fn deref(&self) -> &Self::Target { + &self.user + } +} + +impl DerefMut for MacawUser { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.user + } +} + +#[component] fn ChatsList() -> impl IntoView { - let chats: LocalResource<std::result::Result<(ReadSignal<IndexMap<JID, (MacawChat, MacawMessage)>, LocalStorage>, WriteSignal<IndexMap<JID, (MacawChat, MacawMessage)>, LocalStorage>), String>> = LocalResource::new(move || async move || -> Result<_, _> { - let client: Client = use_context::<RwSignal<Client, LocalStorage>>().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::<IndexMap<JID, _>>(); - let (chats, set_chats) = signal_local(chats); - Ok((chats, set_chats)) - }()); + let client = use_context::<Client>().expect("client not in context"); + let (chats, set_chats) = signal(IndexMap::new()); + + let load_chats = LocalResource::new(move || async move { + let client = use_context::<Client>().expect("client not in context"); + let chats = client.get_chats_ordered_with_latest_messages_and_users().await.map_err(|e| e.to_string()); + match chats { + Ok(c) => { + let chats = c.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::<IndexMap<JID, _>>(); + set_chats.set(chats); + }, + Err(_) => { + // TODO: show error message at top of chats list + }, + } + }); // TODO: filter new messages signal let new_messages_signal: ReadSignal<Option<(JID, MacawMessage)>> = use_context().unwrap(); - OnceResource::new(async move { + spawn_local(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"); - } - } + load_chats.await; + 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_chats.write().shift_remove(&to) { + debug!("chat existed"); + set_chats.write().insert_before(0, to, (chat, new_message)); + debug!("done setting"); + } else { + debug!("the chat didn't exist"); + let chat = client.get_chat(to.clone()).await.unwrap(); + let user = client.get_user(to.clone()).await.unwrap(); + let chat = MacawChat::got_chat_and_user(chat, user); + set_chats.write().insert_before(0, to, (chat, new_message)); + debug!("done setting"); } - debug!("set the new message"); } - Err(_) => {}, } + debug!("set the new message"); }); view! { <div class="chats-list panel"> <h2>Chats</h2> <div> - {move || { - if let Some(chats) = &*chats.read() { - match &**chats { - Ok((chats, _)) => { - let chats = chats.clone(); - view! { - <For - each=move || chats.get() - key=|chat| chat.0.clone() - let(chat) - > - <p>{chat.0.to_string()}</p> - </For> - } - .into_any() - } - Err(e) => { - view! { <div class="error">{format!("{}", e)}</div> }.into_any() - } - } - } else { - None::<String>.into_any() - } - }} + <For each=move || chats.get() key=|chat| chat.0.clone() let(chat)> + <p>{chat.0.to_string()}</p> + </For> </div> </div> } |