diff options
Diffstat (limited to 'src/components/roster_list/roster_list_item.rs')
| -rw-r--r-- | src/components/roster_list/roster_list_item.rs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/components/roster_list/roster_list_item.rs b/src/components/roster_list/roster_list_item.rs new file mode 100644 index 0000000..a6fd714 --- /dev/null +++ b/src/components/roster_list/roster_list_item.rs @@ -0,0 +1,122 @@ +// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +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}, + client::Client, + components::{avatar::AvatarWithPresence, sidebar::Open}, + contact::MacawContact, + open_chats::{OpenChatsPanel, OpenChatsPanelStoreFields}, + state_store::StateStore, + user::{ArcMacawUser, fetch_avatar, get_name}, +}; + +#[component] +pub fn RosterListItem(contact: MacawContact) -> impl IntoView { + let name = move || get_name(contact.user.get().into(), false); + + let open_chats: Store<OpenChatsPanel> = + 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>, 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) = &*current_open_chat.read() { + debug!("got open chat: {:?}", open_chat); + if *open_chat == *contact.user.get().jid().read() { + return Open::Focused; + } + } + if let Some(_backgrounded_chat) = open_chats + .chats() + .read() + .get(contact.user.get().jid().read().deref()) + { + return Open::Open; + } + Open::Closed + }; + let focused = move || open().is_focused(); + let open = move || open().is_open(); + + view! { + <div + class="roster-list-item" + class:open=move || open() + class:focused=move || focused() + on:click=move |_| { + open_chat.dispatch(()); + } + > + {move || { + view! { <AvatarWithPresence user=contact.user /> } + }} + <div class="item-info"> + <div class="main-info"> + <p class="name"> + {name} + <span class="jid">- {move || contact.user_jid().read().to_string()}</span> + </p> + </div> + <div class="sub-info">{move || contact.subscription().read().to_string()}</div> + </div> + </div> + } +} |
