summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-06-07 17:36:22 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-06-07 17:36:22 +0100
commit36e87b708ed3a2b0d6f2e932509b0fdf10fe0089 (patch)
tree5f3fc5a3d863f52a82a204a7746532202103d7cc
parent4a5aa6579f5184c443ffbe80ce93e0daa0926826 (diff)
downloadmacaw-web-main.tar.gz
macaw-web-main.tar.bz2
macaw-web-main.zip
fix: avatar flashing from avatar loadHEADmain
Diffstat (limited to '')
-rw-r--r--src/chat.rs4
-rw-r--r--src/components/avatar.rs9
-rw-r--r--src/components/chat_header.rs3
-rw-r--r--src/components/chats_list.rs22
-rw-r--r--src/components/chats_list/chats_list_item.rs3
-rw-r--r--src/components/message.rs3
-rw-r--r--src/components/message_history_buffer.rs13
-rw-r--r--src/components/new_chat.rs15
-rw-r--r--src/components/personal_status.rs12
-rw-r--r--src/components/roster_list.rs7
-rw-r--r--src/components/roster_list/roster_list_item.rs62
-rw-r--r--src/contact.rs4
-rw-r--r--src/message.rs4
-rw-r--r--src/state_store.rs68
-rw-r--r--src/user.rs45
-rw-r--r--src/views/macaw.rs39
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())
+ });
}
}
}