diff options
| author | 2025-06-07 17:36:22 +0100 | |
|---|---|---|
| committer | 2025-06-07 17:36:22 +0100 | |
| commit | 36e87b708ed3a2b0d6f2e932509b0fdf10fe0089 (patch) | |
| tree | 5f3fc5a3d863f52a82a204a7746532202103d7cc /src/components | |
| parent | 4a5aa6579f5184c443ffbe80ce93e0daa0926826 (diff) | |
| download | macaw-web-36e87b708ed3a2b0d6f2e932509b0fdf10fe0089.tar.gz macaw-web-36e87b708ed3a2b0d6f2e932509b0fdf10fe0089.tar.bz2 macaw-web-36e87b708ed3a2b0d6f2e932509b0fdf10fe0089.zip  | |
fix: avatar flashing from avatar load
Diffstat (limited to 'src/components')
| -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 | 
10 files changed, 90 insertions, 59 deletions
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">  | 
