diff options
Diffstat (limited to '')
-rw-r--r-- | src/chat.rs | 4 | ||||
-rw-r--r-- | src/components/avatar.rs | 9 | ||||
-rw-r--r-- | src/components/chat_header.rs | 3 | ||||
-rw-r--r-- | src/components/chats_list.rs | 22 | ||||
-rw-r--r-- | src/components/chats_list/chats_list_item.rs | 3 | ||||
-rw-r--r-- | src/components/message.rs | 3 | ||||
-rw-r--r-- | src/components/message_history_buffer.rs | 13 | ||||
-rw-r--r-- | src/components/new_chat.rs | 15 | ||||
-rw-r--r-- | src/components/personal_status.rs | 12 | ||||
-rw-r--r-- | src/components/roster_list.rs | 7 | ||||
-rw-r--r-- | src/components/roster_list/roster_list_item.rs | 62 | ||||
-rw-r--r-- | src/contact.rs | 4 | ||||
-rw-r--r-- | src/message.rs | 4 | ||||
-rw-r--r-- | src/state_store.rs | 68 | ||||
-rw-r--r-- | src/user.rs | 45 | ||||
-rw-r--r-- | src/views/macaw.rs | 39 |
16 files changed, 193 insertions, 120 deletions
diff --git a/src/chat.rs b/src/chat.rs index 29e2641..f4202f6 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -59,11 +59,11 @@ pub struct ArcMacawChat { } impl ArcMacawChat { - pub fn got_chat_and_user(chat: Chat, user: User) -> Self { + pub async 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 = ArcMacawUser::got_user(user); + let user = ArcMacawUser::got_user(user).await; Self { chat, user } } } diff --git a/src/components/avatar.rs b/src/components/avatar.rs index 9265ef7..292173e 100644 --- a/src/components/avatar.rs +++ b/src/components/avatar.rs @@ -2,13 +2,12 @@ use filamento::{presence::PresenceType, user::User}; use leptos::prelude::*; use reactive_stores::Store; -use crate::{components::icon::{show_to_icon, IconComponent}, icon::Icon, user::get_avatar, user_presences::UserPresences}; +use crate::{components::icon::{show_to_icon, IconComponent}, icon::Icon, user::{get_avatar, MacawUser}, user_presences::UserPresences}; #[component] -pub fn AvatarWithPresence(user: Store<User>) -> impl IntoView { - let avatar = LocalResource::new(move || get_avatar(user)); +pub fn AvatarWithPresence(user: MacawUser) -> impl IntoView { let user_presences: Store<UserPresences> = use_context().expect("no user presences in context"); - let presence = move || user_presences.write().get_user_presences(&user.read().jid).read().presence(); + let presence = move || user_presences.write().get_user_presences(&user.get().read().jid).read().presence(); let show_icon = move || presence().map(|(_, presence)| { match presence.presence { PresenceType::Online(online) => if let Some(show) = online.show { @@ -22,7 +21,7 @@ pub fn AvatarWithPresence(user: Store<User>) -> impl IntoView { view! { <div class="avatar-with-presence"> - <img class="avatar" src=move || avatar.get() /> + <img class="avatar" src=move || user.avatar().get() /> {move || if let Some(icon) = show_icon() { view!{ <IconComponent icon=icon class:presence-show-icon=true /> diff --git a/src/components/chat_header.rs b/src/components/chat_header.rs index 51906aa..fe4e8d9 100644 --- a/src/components/chat_header.rs +++ b/src/components/chat_header.rs @@ -12,9 +12,8 @@ pub fn ChatViewHeader(chat: MacawChat) -> impl IntoView { view! { <div class="chat-view-header panel"> {move || { - let user = chat.user.get().into(); view! { - <AvatarWithPresence user /> + <AvatarWithPresence user=chat.user /> } }} <div class="user-info"> diff --git a/src/components/chats_list.rs b/src/components/chats_list.rs index a5ecc9b..f958ebe 100644 --- a/src/components/chats_list.rs +++ b/src/components/chats_list.rs @@ -20,18 +20,14 @@ pub fn ChatsList() -> impl IntoView { .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(), - ( - ArcMacawChat::got_chat_and_user(chat, chat_user), - ArcMacawMessage::got_message_and_user(message, message_user), - ), - ) - }) - .collect::<IndexMap<BareJID, _>>(); + let mut chats = IndexMap::new(); + for ((chat, chat_user), (message, message_user)) in c { + chats.insert(chat.correspondent.clone(), ( + ArcMacawChat::got_chat_and_user(chat, chat_user).await, + ArcMacawMessage::got_message_and_user(message, message_user).await, + + )); + } set_chats.set(chats); } Err(_) => { @@ -64,7 +60,7 @@ pub fn ChatsList() -> impl IntoView { let chat = client.get_chat(to.clone()).await.unwrap(); let user = client.get_user(to.clone()).await.unwrap(); debug!("before got chat"); - let chat = ArcMacawChat::got_chat_and_user(chat, user); + let chat = ArcMacawChat::got_chat_and_user(chat, user).await; debug!("after got chat"); chats.insert_before(0, to, (chat, new_message)); debug!("done setting"); diff --git a/src/components/chats_list/chats_list_item.rs b/src/components/chats_list/chats_list_item.rs index 98e014e..ae01288 100644 --- a/src/components/chats_list/chats_list_item.rs +++ b/src/components/chats_list/chats_list_item.rs @@ -54,9 +54,8 @@ pub fn ChatsListItem(chat: MacawChat, message: MacawMessage) -> impl IntoView { view! { <div class="chats-list-item" class:open=move || open() class:focused=move || focused() on:click=open_chat> {move || { - let user = chat.user.get().into(); view! { - <AvatarWithPresence user /> + <AvatarWithPresence user=chat.user /> } }} <div class="item-info"> diff --git a/src/components/message.rs b/src/components/message.rs index f55c5f7..78ebeb6 100644 --- a/src/components/message.rs +++ b/src/components/message.rs @@ -8,7 +8,6 @@ use super::icon::Delivery; #[component] pub fn Message(message: MacawMessage, major: bool, r#final: bool) -> impl IntoView { - let avatar = LocalResource::new(move || get_avatar(message.user.get().into())); let name = move || get_name(message.user.get().into(), false); // TODO: chrono-humanize? @@ -19,7 +18,7 @@ pub fn Message(message: MacawMessage, major: bool, r#final: bool) -> impl IntoVi <div class:final=r#final class="chat-message major"> <div class="left"> <Transition fallback=|| view! { <img class="avatar" src=NO_AVATAR /> } > - <img class="avatar" src=move || avatar.get() /> + <img class="avatar" src=move || message.user.avatar().get() /> </Transition> </div> <div class="middle"> diff --git a/src/components/message_history_buffer.rs b/src/components/message_history_buffer.rs index 4f4561c..dc93054 100644 --- a/src/components/message_history_buffer.rs +++ b/src/components/message_history_buffer.rs @@ -24,15 +24,10 @@ pub fn MessageHistoryBuffer(chat: MacawChat) -> impl IntoView { .map_err(|e| e.to_string()); match messages { Ok(m) => { - let messages = m - .into_iter() - .map(|(message, message_user)| { - ( - message.id, - ArcMacawMessage::got_message_and_user(message, message_user), - ) - }) - .collect::<IndexMap<Uuid, _>>(); + let mut messages = IndexMap::new(); + for (message, message_user) in m { + messages.insert(message.id, ArcMacawMessage::got_message_and_user(message, message_user).await); + } load_set_messages.set(messages); } Err(err) => { diff --git a/src/components/new_chat.rs b/src/components/new_chat.rs index b706ca9..2f9b943 100644 --- a/src/components/new_chat.rs +++ b/src/components/new_chat.rs @@ -6,7 +6,7 @@ use leptos::{html::Input, prelude::*}; use reactive_stores::{ArcStore, Store}; use thiserror::Error; -use crate::{chat::{ArcMacawChat, MacawChat}, client::Client, open_chats::OpenChatsPanel, state_store::StateStore, user::{ArcMacawUser, MacawUser}}; +use crate::{chat::{ArcMacawChat, MacawChat}, client::Client, open_chats::OpenChatsPanel, state_store::StateStore, user::{fetch_avatar, ArcMacawUser, MacawUser}}; #[derive(Clone, Debug, Error)] pub enum NewChatError { @@ -41,7 +41,7 @@ pub fn NewChatWidget(set_open_new_chat: WriteSignal<bool>) -> impl IntoView { let chat_state_store: StateStore<BareJID, ArcStore<Chat>> = use_context().expect("no chat state store"); - let user_state_store: StateStore<BareJID, ArcStore<User>> = + let user_state_store: StateStore<BareJID, (ArcStore<User>, ArcRwSignal<String>)> = use_context().expect("no user state store"); let open_chat = Action::new_local(move |_| { @@ -77,7 +77,16 @@ 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 = user_state_store.store(user.jid.clone(), ArcStore::new(user)); + let old_user = user_state_store.get_listener(user.jid.clone()); + let user = if let Some(old_user) = old_user { + old_user.update(|(old_user, _avatar)| { old_user.set(user); }); + old_user + } else { + let avatar = fetch_avatar(user.avatar.as_deref()).await; + let avatar = ArcRwSignal::new(avatar); + user_state_store.store(user.jid.clone(), (ArcStore::new(user), avatar)) + }; let user = ArcMacawUser { user }; let chat = chat_state_store.store(chat.correspondent.clone(), ArcStore::new(chat)); ArcMacawChat { chat, user } diff --git a/src/components/personal_status.rs b/src/components/personal_status.rs index 8439756..eb7f6e1 100644 --- a/src/components/personal_status.rs +++ b/src/components/personal_status.rs @@ -16,7 +16,7 @@ pub fn PersonalStatus() -> impl IntoView { debug!("set open to true"); set_open.update(|state| *state = !*state) }> - <AvatarWithPresence user=user.get().into() /> + <AvatarWithPresence user /> <div class="dock-pill"></div> </div> {move || { @@ -25,7 +25,7 @@ pub fn PersonalStatus() -> impl IntoView { if open { view! { <Overlay set_open> - <PersonalStatusMenu user=user.get().into() set_open/> + <PersonalStatusMenu user set_open/> </Overlay> }.into_any() } else { @@ -38,7 +38,7 @@ pub fn PersonalStatus() -> impl IntoView { } #[component] -pub fn PersonalStatusMenu(user: Store<User>, set_open: WriteSignal<bool>) -> impl IntoView { +pub fn PersonalStatusMenu(user: MacawUser, set_open: WriteSignal<bool>) -> impl IntoView { let set_app: WriteSignal<AppState> = use_context().unwrap(); let show_settings: RwSignal<Option<SettingsPage>> = use_context().unwrap(); let user_presences: Store<UserPresences> = use_context().expect("no user presence store"); @@ -46,7 +46,7 @@ pub fn PersonalStatusMenu(user: Store<User>, set_open: WriteSignal<bool>) -> imp let client = use_context::<Client>().expect("client not in context"); let client1 = client.clone(); let (show_value, set_show_value) = signal({ - let show = match user_presences.write().get_user_presences(&user.jid().read()).write().resource_presence(client.resource.read().clone().unwrap_or_default()).presence { + let show = match user_presences.write().get_user_presences(&user.get().jid().read()).write().resource_presence(client.resource.read().clone().unwrap_or_default()).presence { PresenceType::Online(online) => match online.show { Some(s) => match s { Show::Away => 3, @@ -129,8 +129,8 @@ pub fn PersonalStatusMenu(user: Store<User>, set_open: WriteSignal<bool>) -> imp <div class="user"> <AvatarWithPresence user=user /> <div class="user-info"> - <div class="nick">{move || get_name(user, false)}</div> - <div class="jid">{move || user.jid().with(|jid| jid.to_string())}</div> + <div class="nick">{move || get_name(user.get().into(), false)}</div> + <div class="jid">{move || user.get().jid().with(|jid| jid.to_string())}</div> </div> </div> <div class="status-edit"> diff --git a/src/components/roster_list.rs b/src/components/roster_list.rs index a398ffe..310b703 100644 --- a/src/components/roster_list.rs +++ b/src/components/roster_list.rs @@ -6,7 +6,7 @@ use leptos::prelude::*; use reactive_stores::Store; use roster_list_item::RosterListItem; -use crate::{components::icon::IconComponent, icon::Icon, roster::{Roster, RosterStoreFields}}; +use crate::{components::icon::IconComponent, icon::Icon, open_chats::{OpenChatsPanel, OpenChatsPanelStoreFields}, roster::{Roster, RosterStoreFields}}; mod contact_request_manager; mod roster_list_item; @@ -15,8 +15,13 @@ mod roster_list_item; pub fn RosterList() -> impl IntoView { let requests: ReadSignal<HashSet<BareJID>> = use_context().expect("no pending subscriptions in context"); + let open_chats: Store<OpenChatsPanel> = + use_context().expect("no open chats panel store in context"); + let roster: Store<Roster> = use_context().expect("no roster in context"); let (open_add_contact, set_open_add_contact) = signal(false); + let open_chat = Memo::new(move |_| open_chats.chat_view().get()); + provide_context(open_chat); // TODO: filter new messages signal view! { diff --git a/src/components/roster_list/roster_list_item.rs b/src/components/roster_list/roster_list_item.rs index 842f66c..538e664 100644 --- a/src/components/roster_list/roster_list_item.rs +++ b/src/components/roster_list/roster_list_item.rs @@ -1,11 +1,12 @@ use std::ops::Deref; use filamento::{chat::Chat, roster::{Contact, ContactStoreFields}, user::{User, UserStoreFields}}; +use jid::BareJID; use leptos::prelude::*; use reactive_stores::{ArcStore, Store}; use tracing::debug; -use crate::{chat::{ArcMacawChat, MacawChat}, components::{avatar::AvatarWithPresence, sidebar::Open}, contact::MacawContact, open_chats::{OpenChatsPanel, OpenChatsPanelStoreFields}, user::get_name}; +use crate::{chat::{ArcMacawChat, MacawChat}, client::Client, components::{avatar::AvatarWithPresence, sidebar::Open}, contact::MacawContact, open_chats::{OpenChatsPanel, OpenChatsPanelStoreFields}, state_store::StateStore, user::{fetch_avatar, get_name, ArcMacawUser}}; #[component] pub fn RosterListItem(contact: MacawContact) -> impl IntoView { @@ -14,21 +15,51 @@ pub fn RosterListItem(contact: MacawContact) -> impl IntoView { let open_chats: Store<OpenChatsPanel> = use_context().expect("no open chats panel store in context"); - // TODO: why can this not be in the closure????? - // TODO: not good, as overwrites preexisting chat state with possibly incorrect one... - let chat = Chat { - correspondent: contact.user.get().jid().get(), - have_chatted: false, - }; - let chat = ArcMacawChat::got_chat_and_user(chat, contact.user.get().get()); + let client = use_context::<Client>().expect("client not in context"); - let open_chat = move |_| { - debug!("opening chat"); - open_chats.update(|open_chats| open_chats.open(Clone::clone(&chat))); - }; + let chat_state_store: StateStore<BareJID, ArcStore<Chat>> = + use_context().expect("no chat state store"); + let user_state_store: StateStore<BareJID, (ArcStore<User>, ArcRwSignal<String>)> = + use_context().expect("no user state store"); + + let open_chat = Action::new_local(move |_| { + let client= client.clone(); + async move { + let to = contact.user.get().jid().get(); + let (chat, user) = match client.get_chat_and_user(to).await { + Ok(c) => c, + Err(e) => { + // TODO: error + // set_error.set(Some(e.into())); + // set_new_chat_pending.set(false); + return; + }, + }; + + let chat = { + // let user = MacawUser::got_user(user); + // let user = user_state_store.store(user.jid.clone(), ArcStore::new(user)); + let old_user = user_state_store.get_listener(user.jid.clone()); + let user = if let Some(old_user) = old_user { + old_user.update(|(old_user, _avatar)| { old_user.set(user); }); + old_user + } else { + let avatar = fetch_avatar(user.avatar.as_deref()).await; + let avatar = ArcRwSignal::new(avatar); + user_state_store.store(user.jid.clone(), (ArcStore::new(user), avatar)) + }; + let user = ArcMacawUser { user }; + let chat = chat_state_store.store(chat.correspondent.clone(), ArcStore::new(chat)); + ArcMacawChat { chat, user } + }; + open_chats.update(|open_chats| open_chats.open(chat.clone())); + } + }); + + let current_open_chat: Memo<Option<BareJID>> = use_context().expect("no open chat memo in context"); let open = move || { - if let Some(open_chat) = &*open_chats.chat_view().read() { + if let Some(open_chat) = &*current_open_chat.read() { debug!("got open chat: {:?}", open_chat); if *open_chat == *contact.user.get().jid().read() { return Open::Focused; @@ -47,11 +78,10 @@ pub fn RosterListItem(contact: MacawContact) -> impl IntoView { let open = move || open().is_open(); view! { - <div class="roster-list-item" class:open=move || open() class:focused=move || focused() on:click=open_chat> + <div class="roster-list-item" class:open=move || open() class:focused=move || focused() on:click=move |_| { open_chat.dispatch(()); }> {move || { - let user = contact.user.get().into(); view! { - <AvatarWithPresence user /> + <AvatarWithPresence user=contact.user /> } }} <div class="item-info"> diff --git a/src/contact.rs b/src/contact.rs index 9adec16..e0e0d78 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -41,9 +41,9 @@ pub struct ArcMacawContact { } impl ArcMacawContact { - pub fn got_contact_and_user(contact: Contact, user: User) -> Self { + pub async fn got_contact_and_user(contact: Contact, user: User) -> Self { let contact = Store::new(contact); - let user = ArcMacawUser::got_user(user); + let user = ArcMacawUser::got_user(user).await; Self { contact, user } } } diff --git a/src/message.rs b/src/message.rs index 878085e..23ca6be 100644 --- a/src/message.rs +++ b/src/message.rs @@ -49,11 +49,11 @@ pub struct ArcMacawMessage { } impl ArcMacawMessage { - pub fn got_message_and_user(message: Message, user: User) -> Self { + pub async 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 = ArcMacawUser::got_user(user); + let user = ArcMacawUser::got_user(user).await; Self { message, user } } } diff --git a/src/state_store.rs b/src/state_store.rs index ac90e40..7e6e6a1 100644 --- a/src/state_store.rs +++ b/src/state_store.rs @@ -112,35 +112,51 @@ where 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(); - debug!("store state: {:?}", store); - if let Some((v, count)) = store.get_mut(&key) { - debug!("updating old value already in store"); - v.set(value); - *count += 1; - StateListener { - value: v.clone(), - cleaner: StateCleaner { - key, - state_store: self.clone(), - }, - } - } else { - let v = ArcRwSignal::new(value); - store.insert(key.clone(), (v.clone(), 1)); - debug!("inserting new value: {:?}", store); - StateListener { - value: v.into(), - cleaner: StateCleaner { - key, - state_store: self.clone(), - }, - } + let store = self.inner.try_get_value().unwrap(); + let mut store = store.store.write().unwrap(); + debug!("store state: {:?}", store); + if let Some((v, count)) = store.get_mut(&key) { + debug!("updating old value already in store"); + v.set(value); + *count += 1; + StateListener { + value: v.clone(), + cleaner: StateCleaner { + key, + state_store: self.clone(), + }, + } + } else { + let v = ArcRwSignal::new(value); + store.insert(key.clone(), (v.clone(), 1)); + debug!("inserting new value: {:?}", store); + StateListener { + value: v.into(), + cleaner: StateCleaner { + key, + state_store: self.clone(), + }, } } } + + pub fn get_listener(&self, key: K) -> Option<StateListener<K, V>> { + let store = self.inner.try_get_value().unwrap(); + let mut store = store.store.write().unwrap(); + debug!("store state: {:?}", store); + if let Some((v, count)) = store.get_mut(&key) { + *count += 1; + Some(StateListener { + value: v.clone(), + cleaner: StateCleaner { + key, + state_store: self.clone(), + }, + }) + } else { + None + } + } } impl<K, V> StateStore<K, V> diff --git a/src/user.rs b/src/user.rs index e62ebea..fb722c6 100644 --- a/src/user.rs +++ b/src/user.rs @@ -12,11 +12,16 @@ pub struct MacawUser { pub user: ArenaItem<ArcMacawUser>, // TODO: just store avatar src in user // pub avatar: String, + // pub avatar: RwSignal<String>, } impl MacawUser { pub fn get(&self) -> ArcStore<User> { - self.try_get_value().unwrap().get() + self.try_get_value().unwrap().get().0 + } + + pub fn avatar(&self) -> ArcRwSignal<String> { + self.try_get_value().unwrap().get().1 } } @@ -38,6 +43,7 @@ impl From<ArcMacawUser> for MacawUser { fn from(value: ArcMacawUser) -> Self { Self { user: ArenaItem::new_with_storage(value), + // avatar: value.avatar.into(), } } } @@ -50,21 +56,29 @@ impl From<MacawUser> for ArcMacawUser { #[derive(Clone)] pub struct ArcMacawUser { - pub user: StateListener<BareJID, ArcStore<User>>, + pub user: StateListener<BareJID, (ArcStore<User>, ArcRwSignal<String>)>, } impl ArcMacawUser { - pub fn got_user(user: User) -> Self { - - let user_state_store: StateStore<BareJID, ArcStore<User>> = + pub async fn got_user(user: User) -> Self { + let user_state_store: StateStore<BareJID, (ArcStore<User>, ArcRwSignal<String>)> = use_context().expect("no user state store"); - let user = user_state_store.store(user.jid.clone(), ArcStore::new(user)); - Self { user } + let old_user = user_state_store.get_listener(user.jid.clone()); + let user = if let Some(old_user) = old_user { + old_user.update(|(old_user, _avatar)| { old_user.set(user); }); + old_user + } else { + let avatar = fetch_avatar(user.avatar.as_deref()).await; + let avatar = ArcRwSignal::new(avatar); + user_state_store.store(user.jid.clone(), (ArcStore::new(user), avatar)) + }; + let user = ArcMacawUser { user }; + user } } impl Deref for ArcMacawUser { - type Target = StateListener<BareJID, ArcStore<User>>; + type Target = StateListener<BareJID, (ArcStore<User>, ArcRwSignal<String>)>; fn deref(&self) -> &Self::Target { &self.user @@ -79,6 +93,19 @@ impl DerefMut for ArcMacawUser { pub const NO_AVATAR: &str = "/assets/no-avatar.png"; +pub async fn fetch_avatar(id: Option<&str>) -> String { + if let Some(avatar) = id { + let client = use_context::<Client>().expect("client not in context"); + if let Some(data) = client.file_store.get_src(avatar).await { + data + } else { + NO_AVATAR.to_string() + } + } else { + NO_AVATAR.to_string() + } +} + 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"); @@ -87,8 +114,6 @@ pub async fn get_avatar(user: Store<User>) -> String { } else { NO_AVATAR.to_string() } - // TODO: enable avatar fetching - // format!("/files/{}", avatar) } else { NO_AVATAR.to_string() } diff --git a/src/views/macaw.rs b/src/views/macaw.rs index 328e3d2..4a5b794 100644 --- a/src/views/macaw.rs +++ b/src/views/macaw.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use filamento::{chat::{Chat, Message, MessageStoreFields}, user::User, UpdateMessage}; use jid::BareJID; @@ -10,7 +10,7 @@ use tokio::sync::mpsc::Receiver; use tracing::debug; use uuid::Uuid; -use crate::{client::Client, components::sidebar::Sidebar, contact::{ArcMacawContact, MacawContact}, message::{ArcMacawMessage, MacawMessage}, message_subscriptions::MessageSubscriptions, open_chats::OpenChatsPanel, roster::{Roster, RosterStoreFields}, state_store::StateStore, user::{ArcMacawUser, MacawUser}, user_presences::{Presences, UserPresences}}; +use crate::{client::Client, components::sidebar::Sidebar, contact::{ArcMacawContact, MacawContact}, message::{ArcMacawMessage, MacawMessage}, message_subscriptions::MessageSubscriptions, open_chats::OpenChatsPanel, roster::{Roster, RosterStoreFields}, state_store::StateStore, user::{fetch_avatar, ArcMacawUser, MacawUser}, user_presences::{Presences, UserPresences}}; use super::AppState; @@ -25,6 +25,7 @@ pub fn Macaw( mut updates: Receiver<UpdateMessage>, set_app: WriteSignal<AppState>, ) -> impl IntoView { + let (updates, set_updates) = signal(Some(updates)); provide_context(set_app); provide_context(client); @@ -38,7 +39,7 @@ pub fn Macaw( 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(); + let users_store: StateStore<BareJID, (ArcStore<User>, ArcRwSignal<String>)> = StateStore::new(); provide_context(users_store); let open_chats = Store::new(OpenChatsPanel::default()); @@ -53,7 +54,7 @@ pub fn Macaw( async move { let client = use_context::<Client>().expect("client not in context"); let user = client.get_user((*client.jid).clone()).await.unwrap(); - ArcMacawUser::got_user(user).into() + ArcMacawUser::got_user(user).await.into() } }); provide_context(client_user); @@ -65,19 +66,15 @@ pub fn Macaw( // TODO: get cached contacts on login before getting the updated contacts - OnceResource::new(async move { + LocalResource::new(move || async move { + let mut updates = set_updates.write().take().expect("main loop ran twice"); while let Some(update) = updates.recv().await { match update { UpdateMessage::Online(online, items) => { - let contacts = items - .into_iter() - .map(|(contact, user)| { - ( - contact.user_jid.clone(), - ArcMacawContact::got_contact_and_user(contact, user).into(), - ) - }) - .collect(); + let mut contacts = HashMap::new(); + for (contact, user) in items { + contacts.insert(contact.user_jid.clone(), ArcMacawContact::got_contact_and_user(contact, user).await.into()); + } roster.contacts().set(contacts); } UpdateMessage::Offline(offline) => { @@ -85,13 +82,13 @@ pub fn Macaw( user_presences.write().clear(); } UpdateMessage::RosterUpdate(contact, user) => { + let new_contact = ArcMacawContact::got_contact_and_user(contact.clone(), user).await.into(); roster.contacts().update(|roster| { if let Some(macaw_contact) = roster.get_mut(&contact.user_jid) { macaw_contact.set(contact); } else { let jid = contact.user_jid.clone(); - let contact = ArcMacawContact::got_contact_and_user(contact, user).into(); - roster.insert(jid, contact); + roster.insert(jid, new_contact); } }); } @@ -116,7 +113,7 @@ pub fn Macaw( } UpdateMessage::Message { to, from, message } => { debug!("before got message"); - let new_message = ArcMacawMessage::got_message_and_user(message, from); + let new_message = ArcMacawMessage::got_message_and_user(message, from).await; debug!("after got message"); spawn_local(async move { message_subscriptions @@ -136,12 +133,16 @@ pub fn Macaw( set_subscription_requests.update(|req| { req.insert(jid); }); } UpdateMessage::NickChanged { jid, nick } => { - users_store.modify(&jid, |user| { + users_store.modify(&jid, |(user, _avatar)| { user.update(|user| *&mut user.nick = nick.clone()) }); } UpdateMessage::AvatarChanged { jid, id } => { - users_store.modify(&jid, |user| *&mut user.write().avatar = id.clone()); + let new_avatar = fetch_avatar(id.as_deref()).await; + users_store.modify(&jid, |(user, avatar)| { + *&mut user.write().avatar = id.clone(); + *&mut avatar.set(new_avatar.clone()) + }); } } } |