diff options
| -rw-r--r-- | Cargo.lock | 30 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/chat.rs | 35 | ||||
| -rw-r--r-- | src/components/chats_list.rs | 2 | ||||
| -rw-r--r-- | src/components/message_history_buffer.rs | 2 | ||||
| -rw-r--r-- | src/components/new_chat.rs | 69 | ||||
| -rw-r--r-- | src/components/personal_status.rs | 2 | ||||
| -rw-r--r-- | src/components/roster_list/contact_request_manager.rs | 3 | ||||
| -rw-r--r-- | src/message.rs | 34 | ||||
| -rw-r--r-- | src/state_store.rs | 237 | ||||
| -rw-r--r-- | src/user.rs | 44 | ||||
| -rw-r--r-- | src/views/macaw.rs | 51 | 
12 files changed, 218 insertions, 292 deletions
@@ -1510,6 +1510,26 @@ dependencies = [  ]  [[package]] +name = "leptos-fetch" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "094fee918af2bd8c6d67e9765db107e2b5d6306ca7f03bd63314eb5920e10142" +dependencies = [ + "chrono", + "chrono-humanize", + "codee", + "futures", + "leptos", + "parking_lot", + "paste", + "rustc_version", + "send_wrapper", + "task-local", + "tracing", + "web-sys", +] + +[[package]]  name = "leptos_config"  version = "0.8.2"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1715,6 +1735,7 @@ dependencies = [   "indexmap",   "jid",   "leptos", + "leptos-fetch",   "reactive_stores",   "serde",   "stylance", @@ -3075,6 +3096,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"  checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"  [[package]] +name = "task-local" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2c821daee0efdf6414970c8185a1c22e259a7ed87b2fd9f7d3c5f5503fd2863" +dependencies = [ + "pin-project-lite", +] + +[[package]]  name = "tempfile"  version = "3.20.0"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -16,6 +16,7 @@ futures = "0.3.31"  indexmap = "2.9.0"  jid = { path = "../luz/jid" }  leptos = { version = "0.8.2", features = ["csr"] } +leptos-fetch = "0.4.2"  # leptos-use = "0.15.7"  # leptos_meta = "0.8.2"  # reactive_graph = "0.2.2" diff --git a/src/chat.rs b/src/chat.rs index 6785b06..a1ced32 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2,30 +2,49 @@ use std::ops::{Deref, DerefMut};  use filamento::{chat::Chat, user::User};  use jid::BareJID; +use leptos_fetch::QueryClient;  use reactive_stores::ArcStore;  use leptos::prelude::*; -use crate::{state_store::{StateListener, StateStore}, user::MacawUser}; +use crate::{client::Client, user::MacawUser}; + +async fn get_chat(jid: BareJID) -> ArcStore<Chat> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_chat(jid).await.unwrap()) +}  #[derive(Clone)]  pub struct MacawChat { -    pub chat: StateListener<BareJID, ArcStore<Chat>>, +    pub chat: ArcStore<Chat>,      pub user: MacawUser,      // user: StateListener<BareJID, ArcStore<User>>,  }  impl MacawChat {      pub fn got_chat_and_user(chat: Chat, user: User) -> Self { -        let chat_state_store: StateStore<BareJID, ArcStore<Chat>> = -            use_context().expect("no chat state store"); -        let chat = chat_state_store.store(chat.correspondent.clone(), ArcStore::new(chat)); -        let user = MacawUser::got_user(user); -        Self { chat, user } +        let query_client: QueryClient = expect_context(); + +        let jid = chat.correspondent.clone(); +        let chat_store = query_client.subscribe_value_local(get_chat, move || jid.clone()); +        if let Some(chat_store) = chat_store.get() { +            chat_store.set(chat); +            let user = MacawUser::got_user(user); +            Self { chat: chat_store, user } +        } else { +            let jid = chat.correspondent.clone(); +            let chat_store = ArcStore::new(chat); +            query_client.set_query_local(get_chat, jid, chat_store.clone()); +            let user = MacawUser::got_user(user); +            Self { +                chat: chat_store, +                user, +            } +        }      }  }  impl Deref for MacawChat { -    type Target = StateListener<BareJID, ArcStore<Chat>>; +    type Target = ArcStore<Chat>;      fn deref(&self) -> &Self::Target {          &self.chat diff --git a/src/components/chats_list.rs b/src/components/chats_list.rs index b8cf34c..d520f56 100644 --- a/src/components/chats_list.rs +++ b/src/components/chats_list.rs @@ -73,7 +73,7 @@ pub fn ChatsList() -> impl IntoView {          debug!("set the new message");      });      on_cleanup(move || { -        if let Some(sub_id) = sub_id.get() { +        if let Some(sub_id) = sub_id.get_untracked() {              new_messages_signal.write().unsubscribe_all(sub_id);          }      }); diff --git a/src/components/message_history_buffer.rs b/src/components/message_history_buffer.rs index 36439a8..632209d 100644 --- a/src/components/message_history_buffer.rs +++ b/src/components/message_history_buffer.rs @@ -123,7 +123,7 @@ pub fn MessageHistoryBuffer(chat: MacawChat) -> impl IntoView {          if let Some(sub_id) = sub_id.get() {              new_messages_signal                  .write() -                .unsubscribe_chat(sub_id, chat_chat.correspondent().get()); +                .unsubscribe_chat(sub_id, chat_chat.correspondent().get_untracked());          }      }); diff --git a/src/components/new_chat.rs b/src/components/new_chat.rs index 8047afb..6d64c96 100644 --- a/src/components/new_chat.rs +++ b/src/components/new_chat.rs @@ -3,10 +3,11 @@ use std::str::FromStr;  use filamento::{chat::Chat, error::{CommandError, DatabaseError}, user::User};  use jid::{BareJID, JID};  use leptos::{html::Input, prelude::*}; +use leptos_fetch::QueryClient;  use reactive_stores::{ArcStore, Store};  use thiserror::Error; -use crate::{chat::MacawChat, client::Client, open_chats::OpenChatsPanel, state_store::StateStore, user::MacawUser}; +use crate::{chat::MacawChat, client::Client, open_chats::OpenChatsPanel, user::MacawUser};  #[derive(Clone, Debug, Error)]  pub enum NewChatError { @@ -18,6 +19,17 @@ pub enum NewChatError {      Db(#[from] CommandError<DatabaseError>),  } +// TODO: remove +async fn get_chat(jid: BareJID) -> ArcStore<Chat> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_chat(jid).await.unwrap()) +} + +async fn get_user(jid: BareJID) -> ArcStore<User> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_user(jid).await.unwrap()) +} +  #[component]  pub fn NewChatWidget(set_open_new_chat: WriteSignal<bool>) -> impl IntoView {      let jid = RwSignal::new("".to_string()); @@ -39,10 +51,7 @@ pub fn NewChatWidget(set_open_new_chat: WriteSignal<bool>) -> impl IntoView {          use_context().expect("no open chats panel store in context");      let client = use_context::<Client>().expect("client not in context"); -    let chat_state_store: StateStore<BareJID, ArcStore<Chat>> = -        use_context().expect("no chat state store"); -    let user_state_store: StateStore<BareJID, ArcStore<User>> = -        use_context().expect("no user state store"); +    let query_client: QueryClient = expect_context();      let open_chat = Action::new_local(move |_| {          let client = client.clone(); @@ -77,10 +86,52 @@ pub fn NewChatWidget(set_open_new_chat: WriteSignal<bool>) -> impl IntoView {              let chat = {                  // let user = MacawUser::got_user(user); -                let user = user_state_store.store(user.jid.clone(), ArcStore::new(user)); -                let user = MacawUser { user }; -                let chat = chat_state_store.store(chat.correspondent.clone(), ArcStore::new(chat)); -                MacawChat { chat, user } +                let jid = chat.correspondent.clone(); +                let chat_store = query_client.subscribe_value_local(get_chat, move || jid.clone()); +                if let Some(chat_store) = chat_store.get() { +                    chat_store.set(chat); +                    let user = { +                        let jid = user.jid.clone(); +                        let user_store = query_client.subscribe_value_local(get_user, move || jid.clone()); +                        if let Some(user_store) = user_store.get() { +                            user_store.set(user); +                            MacawUser { user: user_store } +                        } else { +                            let jid = user.jid.clone(); +                            let user_store = ArcStore::new(user); +                            query_client.set_query_local(get_user, jid, user_store.clone()); +                            MacawUser { +                                user: user_store, +                            } +                        } +                         +                    }; +                    MacawChat { chat: chat_store, user } +                } else { +                    let jid = chat.correspondent.clone(); +                    let chat_store = ArcStore::new(chat); +                    query_client.set_query_local(get_chat, jid, chat_store.clone()); +                    let user = { +                        let jid = user.jid.clone(); +                        let user_store = query_client.subscribe_value_local(get_user, move || jid.clone()); +                        if let Some(user_store) = user_store.get() { +                            user_store.set(user); +                            MacawUser { user: user_store } +                        } else { +                            let jid = user.jid.clone(); +                            let user_store = ArcStore::new(user); +                            query_client.set_query_local(get_user, jid, user_store.clone()); +                            MacawUser { +                                user: user_store, +                            } +                        } +                         +                    }; +                    MacawChat { +                        chat: chat_store, +                        user, +                    } +                }              };              open_chats.update(|open_chats| open_chats.open(chat.clone()));              set_open_new_chat.set(false); diff --git a/src/components/personal_status.rs b/src/components/personal_status.rs index f830a1b..81a27b5 100644 --- a/src/components/personal_status.rs +++ b/src/components/personal_status.rs @@ -11,7 +11,7 @@ pub fn PersonalStatus() -> impl IntoView {      let (open, set_open) = signal(false);      move || if let Some(user) = user.get() { -        let user: Store<User> = <ArcStore<filamento::user::User> as Clone>::clone(&(*user.user)).into(); +        let user: Store<User> = <ArcStore<filamento::user::User> as Clone>::clone(&user.user).into();          view! {              <div class="dock-item" class:focused=move || *open.read()  on:click=move |_| {                  debug!("set open to true"); diff --git a/src/components/roster_list/contact_request_manager.rs b/src/components/roster_list/contact_request_manager.rs index 174e677..31dff91 100644 --- a/src/components/roster_list/contact_request_manager.rs +++ b/src/components/roster_list/contact_request_manager.rs @@ -109,7 +109,8 @@ pub fn AddContact() -> impl IntoView {          let jid = jid.clone();          async move {              // TODO: error -            client.accept_buddy_request(jid).await; +            client.accept_buddy_request(jid.clone()).await; +            set_requests.write().remove(&jid);          }      }); diff --git a/src/message.rs b/src/message.rs index e5caed1..a47c75f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,30 +1,48 @@  use std::ops::{Deref, DerefMut};  use filamento::{chat::Message, user::User}; +use leptos_fetch::QueryClient;  use reactive_stores::ArcStore;  use uuid::Uuid;  use leptos::prelude::*; -use crate::{state_store::{StateListener, StateStore}, user::MacawUser}; +use crate::{client::Client, user::MacawUser}; + +async fn get_message(id: Uuid) -> ArcStore<Message> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_message(id).await.unwrap()) +}  #[derive(Clone)]  pub struct MacawMessage { -    pub message: StateListener<Uuid, ArcStore<Message>>, +    pub message: ArcStore<Message>,      pub user: MacawUser,  }  impl MacawMessage {      pub fn got_message_and_user(message: Message, user: User) -> Self { -        let message_state_store: StateStore<Uuid, ArcStore<Message>> = -            use_context().expect("no message state store"); -        let message = message_state_store.store(message.id, ArcStore::new(message)); -        let user = MacawUser::got_user(user); -        Self { message, user } +        let query_client: QueryClient = expect_context(); + +        let id = message.id; +        let message_store = query_client.subscribe_value_local(get_message, move || id); +        if let Some(message_store) = message_store.get() { +            message_store.set(message); +            let user = MacawUser::got_user(user); +            Self { message: message_store, user } +        } else { +            let message_store = ArcStore::new(message); +            query_client.set_query_local(get_message, id, message_store.clone()); +            let user = MacawUser::got_user(user); +            Self { +                message: message_store, +                user, +            } +        }      }  }  impl Deref for MacawMessage { -    type Target = StateListener<Uuid, ArcStore<Message>>; +    type Target = ArcStore<Message>;      fn deref(&self) -> &Self::Target {          &self.message diff --git a/src/state_store.rs b/src/state_store.rs index 2536cda..8b13789 100644 --- a/src/state_store.rs +++ b/src/state_store.rs @@ -1,238 +1 @@ -use std::{collections::HashMap, ops::{Deref, DerefMut}, sync::{Arc, RwLock}}; -use leptos::prelude::*; - -// TODO: get rid of this -// V has to be an arc signal -#[derive(Debug)] -pub struct ArcStateStore<K, V> { -    store: Arc<RwLock<HashMap<K, (V, usize)>>>, -} - -impl<K, V> PartialEq for ArcStateStore<K, V> { -    fn eq(&self, other: &Self) -> bool { -        Arc::ptr_eq(&self.store, &other.store) -    } -} - -impl<K, V> Clone for ArcStateStore<K, V> { -    fn clone(&self) -> Self { -        Self { -            store: Arc::clone(&self.store), -        } -    } -} - -impl<K, V> Eq for ArcStateStore<K, V> {} - -impl<K, V> ArcStateStore<K, V> { -    pub fn new() -> Self { -        Self { -            store: Arc::new(RwLock::new(HashMap::new())), -        } -    } -} - -#[derive(Debug)] -pub struct StateStore<K, V, S = SyncStorage> { -    inner: ArenaItem<ArcStateStore<K, V>, S>, -} - -impl<K, V, S> Dispose for StateStore<K, V, S> { -    fn dispose(self) { -        self.inner.dispose() -    } -} - -impl<K, V> StateStore<K, V> -where -    K: Send + Sync + 'static, -    V: Send + Sync + 'static, -{ -    pub fn new() -> Self { -        Self::new_with_storage() -    } -} - -impl<K, V, S> StateStore<K, V, S> -where -    K: 'static, -    V: 'static, -    S: Storage<ArcStateStore<K, V>>, -{ -    pub fn new_with_storage() -> Self { -        Self { -            inner: ArenaItem::new_with_storage(ArcStateStore::new()), -        } -    } -} - -impl<K, V> StateStore<K, V, LocalStorage> -where -    K: 'static, -    V: 'static, -{ -    pub fn new_local() -> Self { -        Self::new_with_storage() -    } -} - -impl< -    K: std::marker::Send + std::marker::Sync + 'static, -    V: std::marker::Send + std::marker::Sync + 'static, -> From<ArcStateStore<K, V>> for StateStore<K, V> -{ -    fn from(value: ArcStateStore<K, V>) -> Self { -        Self { -            inner: ArenaItem::new_with_storage(value), -        } -    } -} - -impl<K: 'static, V: 'static> FromLocal<ArcStateStore<K, V>> for StateStore<K, V, LocalStorage> { -    fn from_local(value: ArcStateStore<K, V>) -> Self { -        Self { -            inner: ArenaItem::new_with_storage(value), -        } -    } -} - -impl<K, V, S> Copy for StateStore<K, V, S> {} - -impl<K, V, S> Clone for StateStore<K, V, S> { -    fn clone(&self) -> Self { -        *self -    } -} - -impl<K: Eq + std::hash::Hash + Clone, V: Clone> StateStore<K, V> -where -    K: Send + Sync + 'static, -    V: Send + Sync + 'static, -{ -    pub fn store(&self, key: K, value: V) -> StateListener<K, V> { -        { -            let store = self.inner.try_get_value().unwrap(); -            let mut store = store.store.write().unwrap(); -            if let Some((v, count)) = store.get_mut(&key) { -                *v = value.clone(); -                *count += 1; -            } else { -                store.insert(key.clone(), (value.clone(), 1)); -            } -        }; -        StateListener { -            value, -            cleaner: StateCleaner { -                key, -                state_store: self.clone(), -            }, -        } -    } -} - -impl<K, V> StateStore<K, V> -where -    K: Eq + std::hash::Hash + Send + Sync + 'static, -    V: Send + Sync + 'static, -{ -    pub fn update(&self, key: &K, value: V) { -        let store = self.inner.try_get_value().unwrap(); -        let mut store = store.store.write().unwrap(); -        if let Some((v, _)) = store.get_mut(key) { -            *v = value; -        } -    } - -    pub fn modify(&self, key: &K, modify: impl Fn(&mut V)) { -        let store = self.inner.try_get_value().unwrap(); -        let mut store = store.store.write().unwrap(); -        if let Some((v, _)) = store.get_mut(key) { -            modify(v); -        } -    } - -    fn remove(&self, key: &K) { -        // let store = self.inner.try_get_value().unwrap(); -        // let mut store = store.store.write().unwrap(); -        // if let Some((_v, count)) = store.get_mut(key) { -        //     *count -= 1; -        //     if *count == 0 { -        //         store.remove(key); -        //         debug!("dropped item from store"); -        //     } -        // } -    } -} - -#[derive(Clone)] -pub struct StateListener<K, V> -where -    K: Eq + std::hash::Hash + 'static + std::marker::Send + std::marker::Sync, -    V: 'static + std::marker::Send + std::marker::Sync, -{ -    value: V, -    cleaner: StateCleaner<K, V>, -} - -impl< -    K: std::cmp::Eq + std::hash::Hash + std::marker::Send + std::marker::Sync, -    V: std::marker::Send + std::marker::Sync, -> Deref for StateListener<K, V> -{ -    type Target = V; - -    fn deref(&self) -> &Self::Target { -        &self.value -    } -} - -impl<K: std::cmp::Eq + std::hash::Hash + Send + Sync, V: Send + Sync> DerefMut -    for StateListener<K, V> -{ -    fn deref_mut(&mut self) -> &mut Self::Target { -        &mut self.value -    } -} - -struct ArcStateCleaner<K, V> { -    key: K, -    state_store: ArcStateStore<K, V>, -} - -struct StateCleaner<K, V> -where -    K: Eq + std::hash::Hash + Send + Sync + 'static, -    V: Send + Sync + 'static, -{ -    key: K, -    state_store: StateStore<K, V>, -} - -impl<K, V> Clone for StateCleaner<K, V> -where -    K: Eq + std::hash::Hash + Clone + Send + Sync, -    V: Send + Sync, -{ -    fn clone(&self) -> Self { -        { -            let store = self.state_store.inner.try_get_value().unwrap(); -            let mut store = store.store.write().unwrap(); -            if let Some((_v, count)) = store.get_mut(&self.key) { -                *count += 1; -            } -        } -        Self { -            key: self.key.clone(), -            state_store: self.state_store.clone(), -        } -    } -} - -impl<K: Eq + std::hash::Hash + Send + Sync + 'static, V: Send + Sync + 'static> Drop -    for StateCleaner<K, V> -{ -    fn drop(&mut self) { -        self.state_store.remove(&self.key); -    } -} diff --git a/src/user.rs b/src/user.rs index f55c0dd..d92ba4f 100644 --- a/src/user.rs +++ b/src/user.rs @@ -2,28 +2,46 @@ use std::ops::{Deref, DerefMut};  use filamento::user::{User, UserStoreFields};  use jid::BareJID; +use leptos_fetch::QueryClient;  use reactive_stores::{ArcStore, Store};  use leptos::prelude::*; -use crate::{client::Client, roster::{Roster, RosterStoreFields}, state_store::{StateListener, StateStore}}; +use crate::{client::Client, roster::{Roster, RosterStoreFields}}; + +async fn get_user(jid: BareJID) -> ArcStore<User> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_user(jid).await.unwrap()) +}  #[derive(Clone)]  pub struct MacawUser { -    pub user: StateListener<BareJID, ArcStore<User>>, +    pub user: ArcStore<User>,  }  impl MacawUser {      pub fn got_user(user: User) -> Self { -         -        let user_state_store: StateStore<BareJID, ArcStore<User>> = -            use_context().expect("no user state store"); -        let user = user_state_store.store(user.jid.clone(), ArcStore::new(user)); -        Self { user } +        // let avatar = fetch_avatar(&user); +        // user.avatar = avatar; +        let query_client: QueryClient = expect_context(); + +        let jid = user.jid.clone(); +        let user_store = query_client.subscribe_value_local(get_user, move || jid.clone()); +        if let Some(user_store) = user_store.get() { +            user_store.set(user); +            Self { user: user_store } +        } else { +            let jid = user.jid.clone(); +            let user_store = ArcStore::new(user); +            query_client.set_query_local(get_user, jid, user_store.clone()); +            Self { +                user: user_store, +            } +        }      }  }  impl Deref for MacawUser { -    type Target = StateListener<BareJID, ArcStore<User>>; +    type Target = ArcStore<User>;      fn deref(&self) -> &Self::Target {          &self.user @@ -38,6 +56,15 @@ impl DerefMut for MacawUser {  pub const NO_AVATAR: &str = "/assets/no-avatar.png"; +pub async fn fetch_avatar(user: &User) -> Option<String> { +    if let Some(avatar) = &user.avatar { +        let client = use_context::<Client>().expect("client not in context"); +        client.file_store.get_src(avatar).await +    } else { +        None +    } +} +  pub async fn get_avatar(user: Store<User>) -> String {      if let Some(avatar) = &user.read().avatar {          let client = use_context::<Client>().expect("client not in context"); @@ -46,6 +73,7 @@ pub async fn get_avatar(user: Store<User>) -> String {          } else {              NO_AVATAR.to_string()          } +        // avatar.clone()          // TODO: enable avatar fetching          // format!("/files/{}", avatar)      } else { diff --git a/src/views/macaw.rs b/src/views/macaw.rs index 18e0ad3..e6ec66c 100644 --- a/src/views/macaw.rs +++ b/src/views/macaw.rs @@ -1,8 +1,9 @@  use std::collections::HashSet; -use filamento::{chat::{Chat, Message, MessageStoreFields}, user::User, UpdateMessage}; +use filamento::{chat::{Chat, Message, MessageStoreFields}, user::{User, UserStoreFields}, UpdateMessage};  use jid::BareJID;  use leptos::{prelude::*, task::spawn_local}; +use leptos_fetch::QueryClient;  use open_chats_panel::OpenChatsPanelView;  use reactive_stores::{ArcStore, Store};  use settings::{Settings, SettingsPage}; @@ -10,13 +11,25 @@ use tokio::sync::mpsc::Receiver;  use tracing::debug;  use uuid::Uuid; -use crate::{client::Client, components::sidebar::Sidebar, contact::MacawContact, message::MacawMessage, message_subscriptions::MessageSubscriptions, open_chats::OpenChatsPanel, roster::{Roster, RosterStoreFields}, state_store::StateStore, user::MacawUser, user_presences::{Presences, UserPresences}}; +use crate::{client::Client, components::sidebar::Sidebar, contact::MacawContact, message::MacawMessage, message_subscriptions::MessageSubscriptions, open_chats::OpenChatsPanel, roster::{Roster, RosterStoreFields}, user::MacawUser, user_presences::{Presences, UserPresences}};  use super::AppState;  pub mod settings;  mod open_chats_panel; +// TODO: remove +async fn get_message(id: Uuid) -> ArcStore<Message> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_message(id).await.unwrap()) +} + +async fn get_user(jid: BareJID) -> ArcStore<User> { +    let client: Client = use_context().expect("no client in context"); +    ArcStore::new(client.get_user(jid).await.unwrap()) +} + +  #[component]  pub fn Macaw(      // TODO: logout @@ -34,12 +47,8 @@ pub fn Macaw(      let message_subscriptions = RwSignal::new(MessageSubscriptions::new());      provide_context(message_subscriptions); -    let messages_store: StateStore<Uuid, ArcStore<Message>> = StateStore::new(); -    provide_context(messages_store); -    let chats_store: StateStore<BareJID, ArcStore<Chat>> = StateStore::new(); -    provide_context(chats_store); -    let users_store: StateStore<BareJID, ArcStore<User>> = StateStore::new(); -    provide_context(users_store); +    QueryClient::new().provide(); +    let query_client: QueryClient = expect_context();      let open_chats = Store::new(OpenChatsPanel::default());      provide_context(open_chats); @@ -102,7 +111,7 @@ pub fn Macaw(                  }                  UpdateMessage::Presence { from, presence } => {                      let bare_jid = from.to_bare(); -                    if let Some(presences) = user_presences.read().user_presences.get(&bare_jid) { +                    if let Some(presences) = user_presences.read_untracked().user_presences.get(&bare_jid) {                          if let Some(resource) = from.resourcepart() {                              presences.write().update_presence(resource.clone(), presence);                          } @@ -127,22 +136,28 @@ pub fn Macaw(                      debug!("after set message");                  }                  UpdateMessage::MessageDelivery { id, chat, delivery } => { -                    messages_store.modify(&id, |message| { -                        <ArcStore<filamento::chat::Message> as Clone>::clone(&message) -                            .delivery() -                            .set(Some(delivery)) -                    }); +                    query_client.update_query(get_message, id, |message| { +                        if let Some(message) = message { +                            <ArcStore<filamento::chat::Message> as Clone>::clone(&message).delivery().set(Some(delivery)) +                        } +                    })                  }                  UpdateMessage::SubscriptionRequest(jid) => {                      set_subscription_requests.update(|req| { req.insert(jid); });                  }                  UpdateMessage::NickChanged { jid, nick } => { -                    users_store.modify(&jid, |user| { -                        user.update(|user| *&mut user.nick = nick.clone()) -                    }); +                    query_client.update_query(get_user, jid, |user| { +                        if let Some(user) = user { +                            <ArcStore<filamento::user::User> as Clone>::clone(&user).nick().set(nick) +                        } +                    })                  }                  UpdateMessage::AvatarChanged { jid, id } => { -                    users_store.modify(&jid, |user| *&mut user.write().avatar = id.clone()); +                    query_client.update_query(get_user, jid, |user| { +                        if let Some(user) = user { +                            <ArcStore<filamento::user::User> as Clone>::clone(&user).avatar().set(id) +                        } +                    })                  }              }          }  | 
