diff options
Diffstat (limited to '')
| -rw-r--r-- | src/components/roster_list.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/components/roster_list.rs b/src/components/roster_list.rs new file mode 100644 index 0000000..b018d45 --- /dev/null +++ b/src/components/roster_list.rs @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2025 cel <cel@bunny.garden> +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashSet; + +use contact_request_manager::AddContact; +use jid::BareJID; +use js_sys::{wasm_bindgen::UnwrapThrowExt, Object, Reflect, JSON}; +use leptos::{html::Div, prelude::*}; +use overlay_scrollbars::OverlayScrollbars; +use reactive_stores::Store; +use roster_list_item::RosterListItem; +use tracing::debug; + +use crate::{ + components::icon::IconComponent, + icon::Icon, + open_chats::{OpenChatsPanel, OpenChatsPanelStoreFields}, + roster::{Roster, RosterStoreFields}, +}; + +mod contact_request_manager; +mod roster_list_item; + +#[component] +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); + + let roster_list: NodeRef<Div> = NodeRef::new(); + let roster_list_viewport: NodeRef<Div> = NodeRef::new(); + + let _scrollbars = Effect::new(move |_| { + if let Some(buffer) = roster_list.get() { + if let Some(viewport) = roster_list_viewport.get() { + let elements_obj = Object::new(); + Reflect::set(&elements_obj, &"viewport".into(), &viewport.into()).unwrap_throw(); + let element_obj = Object::new(); + Reflect::set(&elements_obj, &"elements".into(), &elements_obj).unwrap_throw(); + Reflect::set(&element_obj, &"target".into(), &buffer.into()).unwrap_throw(); + // let element = Object::define_property(&Object::define_property(&Object::new(), &"target".into(), &buffer.into()), &"elements".into(), &Object::define_property(&Object::new(), &"viewport".into(), &viewport.into())); + debug!( + "scrollable element: {}", + JSON::stringify(&element_obj.clone().into()).unwrap_throw() + ); + OverlayScrollbars(element_obj, Object::new()); + } + } + }); + + let add_contact: NodeRef<Div> = NodeRef::new(); + let add_contact_viewport: NodeRef<Div> = NodeRef::new(); + + let _scrollbars = Effect::new(move |_| { + if let Some(buffer) = add_contact.get() { + if let Some(viewport) = add_contact_viewport.get() { + let scrollbars_obj = Object::new(); + Reflect::set(&scrollbars_obj, &"theme".into(), &"os-macaw".into()).unwrap_throw(); + Reflect::set(&scrollbars_obj, &"move".into(), &"leave".into()).unwrap_throw(); + let options_obj = Object::new(); + Reflect::set(&options_obj, &"scrollbars".into(), &scrollbars_obj).unwrap_throw(); + + let elements_obj = Object::new(); + Reflect::set(&elements_obj, &"viewport".into(), &viewport.into()).unwrap_throw(); + let element_obj = Object::new(); + Reflect::set(&elements_obj, &"elements".into(), &elements_obj).unwrap_throw(); + Reflect::set(&element_obj, &"target".into(), &buffer.into()).unwrap_throw(); + // let element = Object::define_property(&Object::define_property(&Object::new(), &"target".into(), &buffer.into()), &"elements".into(), &Object::define_property(&Object::new(), &"viewport".into(), &viewport.into())); + debug!( + "scrollable element: {}", + JSON::stringify(&element_obj.clone().into()).unwrap_throw() + ); + OverlayScrollbars(element_obj, options_obj); + } + } + }); + + // TODO: filter new messages signal + view! { + <div class="roster-list panel"> + <div class="header"> + <h2>Roster</h2> + <div class="add-contact header-icon" class:open=open_add_contact> + <IconComponent + icon=Icon::AddContact24 + on:click=move |_| set_open_add_contact.update(|state| *state = !*state) + /> + {move || { + if !requests.read().is_empty() { + view! { <div class="badge"></div> }.into_any() + } else { + view! {}.into_any() + } + }} + </div> + </div> + {move || { + if *open_add_contact.read() { + view! { + <div class="overlay-scroll add-contact-panel" node_ref=add_contact> + <div class="roster-add-contact" node_ref=add_contact_viewport> + <AddContact /> + </div> + </div> + } + .into_any() + } else { + view! {}.into_any() + } + }} + <div class="overlay-scroll" node_ref=roster_list> + <div class="roster-list-roster" node_ref=roster_list_viewport> + <For + each=move || roster.contacts().get() + key=|contact| contact.0.clone() + let(contact) + > + <RosterListItem contact=contact.1 /> + </For> + </div> + </div> + </div> + } +} |
