summaryrefslogblamecommitdiffstats
path: root/src/components/sidebar.rs
blob: 0be31fd26a36b7bb911abb29fe407ba54f56615e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                         


                       
                           

                        
                                                                                    

  
                                           




                      





                      







































                                                                                


                                




                                   

                                                                    



                                                                                            
                                              
                                                             
                                                    
                                                       











                                                            

           






                                           

                                       
















                                                                                           


                                                         








                                                                                           


                                  
















                                                                                          



                                                        
                                        



                                      



                                                                                 





























                                                                                                     
                              

                                   


                                       



              
use std::collections::{HashMap, HashSet};

use jid::BareJID;
use leptos::prelude::*;
use reactive_stores::Store;

use crate::components::{
    chats_list::ChatsList, personal_status::PersonalStatus, roster_list::RosterList,
};

#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub enum SidebarOpen {
    Roster,
    Chats,
}

#[derive(Store)]
pub struct Drawer {
    open: SidebarOpen,
    hovering: bool,
}

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 open = Memo::new(move |_| open.get());
    let (hovered, set_hovered) = signal(None::<SidebarOpen>);
    let hovered = Memo::new(move |_| hovered.get());
    let (just_closed, set_just_closed) = signal(false);
    let just_closed = Memo::new(move |_| just_closed.get());

    let pages = Memo::new(move |_| {
        let mut pages = HashSet::new();
        if let Some(hovered) = *hovered.read() {
            pages.insert(hovered);
        }
        if let Some(opened) = *open.read() {
            pages.insert(opened);
        }
        pages
    });

    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 !just_closed.get() {
                    view! {
                        <For each=move || pages.get() key=|page| *page let(page)>
                            {move || match page {
                                SidebarOpen::Roster => {
                                    view! {
                                        <div
                                            class:sidebar-drawer=true
                                            class:sidebar-hovering-drawer=move || {
                                                !(*open.read() == Some(SidebarOpen::Roster))
                                                    && (*hovered.read() == Some(SidebarOpen::Roster))
                                            }
                                        >
                                            <RosterList />
                                        </div>
                                    }
                                        .into_any()
                                }
                                SidebarOpen::Chats => {
                                    view! {
                                        <div
                                            class:sidebar-drawer=true
                                            class:sidebar-hovering-drawer=move || {
                                                !(*open.read() == Some(SidebarOpen::Chats))
                                                    && (*hovered.read() == Some(SidebarOpen::Chats))
                                            }
                                        >
                                            <ChatsList />
                                        </div>
                                    }
                                        .into_any()
                                }
                            }}
                        </For>
                    }
                        .into_any()
                } else {
                    view! {}.into_any()
                }
            }}
        </div>
    }
}