diff options
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"> |