diff options
author | 2025-06-01 16:10:26 +0100 | |
---|---|---|
committer | 2025-06-01 17:27:40 +0100 | |
commit | 6ee4190a26f32bfa953302ee363ad3bb6c384ebb (patch) | |
tree | 2c3182c29d5780a0ad9c9770b5e546312bea49b4 /src/components/sidebar.rs | |
parent | f76c80c1d23177ab00c81240ee3a75d3bcda0e3b (diff) | |
download | macaw-web-6ee4190a26f32bfa953302ee363ad3bb6c384ebb.tar.gz macaw-web-6ee4190a26f32bfa953302ee363ad3bb6c384ebb.tar.bz2 macaw-web-6ee4190a26f32bfa953302ee363ad3bb6c384ebb.zip |
refactor: reorganise code
Diffstat (limited to 'src/components/sidebar.rs')
-rw-r--r-- | src/components/sidebar.rs | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/components/sidebar.rs b/src/components/sidebar.rs new file mode 100644 index 0000000..ca753ef --- /dev/null +++ b/src/components/sidebar.rs @@ -0,0 +1,176 @@ +use std::collections::HashSet; + +use jid::BareJID; +use leptos::prelude::*; + +use crate::components::{ + personal_status::PersonalStatus, + chats_list::ChatsList, + roster_list::RosterList, +}; + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum SidebarOpen { + Roster, + Chats, +} + +pub enum Open { + /// Currently on screen + Focused, + /// Open in background somewhere (e.g. in another chat tab) + Open, + /// Closed + Closed, +} + +impl Open { + pub fn is_focused(&self) -> bool { + match self { + Open::Focused => true, + Open::Open => false, + Open::Closed => false, + } + } + + pub fn is_open(&self) -> bool { + match self { + Open::Focused => true, + Open::Open => true, + Open::Closed => false, + } + } +} + +/// returns whether the state was changed to open (true) or closed (false) +pub fn toggle_open(state: &mut Option<SidebarOpen>, open: SidebarOpen) -> bool { + match state { + Some(opened) => { + if *opened == open { + *state = None; + false + } else { + *state = Some(open); + true + } + } + None => { + *state = Some(open); + true + }, + } +} + +#[component] +pub fn Sidebar() -> impl IntoView { + let requests: ReadSignal<HashSet<BareJID>> = use_context().expect("no pending subscriptions in context"); + + // for what has been clicked open (in the background) + let (open, set_open) = signal(None::<SidebarOpen>); + // for what is just in the hovered state (not clicked to be pinned open yet necessarily) + let (hovered, set_hovered) = signal(None::<SidebarOpen>); + let (just_closed, set_just_closed) = signal(false); + + view! { + <div class="sidebar" on:mouseleave=move |_| { + set_hovered.set(None); + set_just_closed.set(false); + }> + <div class="dock panel"> + <div class="shortcuts"> + <div class="roster-tab dock-item" class:focused=move || *open.read() == Some(SidebarOpen::Roster) class:hovering=move || *hovered.read() == Some(SidebarOpen::Roster) + on:mouseenter=move |_| { + set_just_closed.set(false); + set_hovered.set(Some(SidebarOpen::Roster)) + } + on:click=move |_| { + set_open.update(|state| { + if !toggle_open(state, SidebarOpen::Roster) { + set_just_closed.set(true); + } + }) + }> + <div class="dock-pill"></div> + <div class="dock-icon"> + <div class="icon-with-badge"> + <img src="/assets/caw.png" /> + {move || { + let len = requests.read().len(); + if len > 0 { + view! { + <div class="badge">{len}</div> + }.into_any() + } else { + view! {}.into_any() + } + }} + </div> + </div> + </div> + <div class="chats-tab dock-item" class:focused=move || *open.read() == Some(SidebarOpen::Chats) class:hovering=move || *hovered.read() == Some(SidebarOpen::Chats) + on:mouseenter=move |_| { + set_just_closed.set(false); + set_hovered.set(Some(SidebarOpen::Chats)) + } + on:click=move |_| { + set_open.update(|state| { + if !toggle_open(state, SidebarOpen::Chats) { + set_just_closed.set(true); + } + }) + }> + <div class="dock-pill"></div> + <img src="/assets/bubble.png" /> + </div> + </div> + <div class="pins"> + </div> + <div class="personal"> + <PersonalStatus /> + </div> + </div> + {move || if let Some(hovered) = *hovered.read() { + if Some(hovered) != *open.read() { + if !just_closed.get() { + match hovered { + SidebarOpen::Roster => view! { + <div class="sidebar-drawer sidebar-hovering-drawer"> + <RosterList /> + </div> + }.into_any(), + SidebarOpen::Chats => view! { + <div class="sidebar-drawer sidebar-hovering-drawer"> + <ChatsList /> + </div> + }.into_any(), + } + } else { + + view! {}.into_any() + } + } else { + view! {}.into_any() + } + } else { + view! {}.into_any() + }} + {move || if let Some(opened) = *open.read() { + match opened { + SidebarOpen::Roster => view! { + <div class="sidebar-drawer"> + <RosterList /> + </div> + }.into_any(), + SidebarOpen::Chats => view! { + <div class="sidebar-drawer"> + <ChatsList /> + </div> + }.into_any(), + } + } else { + view! {}.into_any() + }} + </div> + } +} + |