summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar cel 🌸 <cel@bunny.garden>2025-05-15 09:09:37 +0100
committerLibravatar cel 🌸 <cel@bunny.garden>2025-05-15 09:09:37 +0100
commit62aaa8cb8583d9189358a6c15ca69257b342c4ea (patch)
treeab63c80e8d38df4e4ef3c3b8a41bccde82d4ffdd /src
parentcf6e64da8dfb78dd103727066fa494d79912fe2a (diff)
downloadmacaw-web-62aaa8cb8583d9189358a6c15ca69257b342c4ea.tar.gz
macaw-web-62aaa8cb8583d9189358a6c15ca69257b342c4ea.tar.bz2
macaw-web-62aaa8cb8583d9189358a6c15ca69257b342c4ea.zip
feat: dock hover and pinning focus
Diffstat (limited to '')
-rw-r--r--src/lib.rs124
1 files changed, 111 insertions, 13 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 194a8d9..5961204 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -621,6 +621,15 @@ fn Macaw(
let user_presences = Store::new(UserPresences::new());
provide_context(user_presences);
+ let client_user = LocalResource::new(move || {
+ async move {
+ let client = use_context::<Client>().expect("client not in context");
+ let user = client.get_user((*client.jid).clone()).await.unwrap();
+ MacawUser::got_user(user)
+ }
+ });
+ provide_context(client_user);
+
// TODO: get cached contacts on login before getting the updated contacts
OnceResource::new(async move {
@@ -717,16 +726,22 @@ pub enum SidebarOpen {
Chats,
}
-pub fn toggle_open(state: &mut Option<SidebarOpen>, open: SidebarOpen) {
+/// 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
+ *state = None;
+ false
} else {
- *state = Some(open)
+ *state = Some(open);
+ true
}
}
- None => *state = Some(open),
+ None => {
+ *state = Some(open);
+ true
+ },
}
}
@@ -736,19 +751,41 @@ pub fn Sidebar() -> impl IntoView {
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">
+ <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::Chats) on:click=move |_| {
- set_open.update(|state| toggle_open(state, SidebarOpen::Roster))
+ <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>
<img src="/assets/caw.png" />
</div>
- <div class="chats-tab dock-item" class:focused=move || *open.read() == Some(SidebarOpen::Chats) class:hovering=move || *hovered.read() == Some(SidebarOpen::Chats) on:click=move |_| {
- set_open.update(|state| toggle_open(state, SidebarOpen::Chats))
+ <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" />
@@ -757,15 +794,45 @@ pub fn Sidebar() -> impl IntoView {
<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! {
- <RosterList />
+ <div class="sidebar-drawer">
+ <RosterList />
+ </div>
}.into_any(),
SidebarOpen::Chats => view! {
- <ChatsList />
+ <div class="sidebar-drawer">
+ <ChatsList />
+ </div>
}.into_any(),
}
} else {
@@ -776,6 +843,26 @@ pub fn Sidebar() -> impl IntoView {
}
#[component]
+pub fn PersonalStatus() -> impl IntoView {
+ let user: LocalResource<MacawUser> = use_context().expect("no local user in context");
+
+ let (open, set_open) = signal(false);
+ move || if let Some(user) = user.get() {
+ let user: Store<User> = <ArcStore<filamento::user::User> as Clone>::clone(&(*user.user)).into();
+ view! {
+ <div class="dock-item" class:focused=move || *open.read() on:click=move |_| {
+ set_open.update(|state| *state = !*state)
+ }>
+ <AvatarWithPresence user=user />
+ <div class="dock-pill"></div>
+ </div>
+ }.into_any()
+ } else {
+ view! {}.into_any()
+ }
+}
+
+#[component]
pub fn OpenChatsPanelView() -> impl IntoView {
let open_chats: Store<OpenChatsPanel> = use_context().expect("no open chats panel in context");
@@ -1605,12 +1692,23 @@ impl DerefMut for MacawMessage {
}
}
+#[derive(Clone)]
struct MacawUser {
- user: StateListener<JID, Store<User>>,
+ user: StateListener<JID, ArcStore<User>>,
+}
+
+impl MacawUser {
+ fn got_user(user: User) -> Self {
+
+ let user_state_store: StateStore<JID, ArcStore<User>> =
+ use_context().expect("no user state store");
+ let user = user_state_store.store(user.jid.clone(), ArcStore::new(user));
+ Self { user }
+ }
}
impl Deref for MacawUser {
- type Target = StateListener<JID, Store<User>>;
+ type Target = StateListener<JID, ArcStore<User>>;
fn deref(&self) -> &Self::Target {
&self.user