From 9b06428f4407113aa872fe3d4d5345dfa3c03664 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Mon, 28 Sep 2026 10:06:29 +0700 Subject: [PATCH] add requests panel --- crates/workspace/src/sidebar/mod.rs | 125 +++++++++++++++++++++------- 1 file changed, 93 insertions(+), 32 deletions(-) diff --git a/crates/workspace/src/sidebar/mod.rs b/crates/workspace/src/sidebar/mod.rs index 0bc0edbd..36584358 100644 --- a/crates/workspace/src/sidebar/mod.rs +++ b/crates/workspace/src/sidebar/mod.rs @@ -92,6 +92,10 @@ pub struct Sidebar { selected_contacts: HashSet, /// Scroll state of the contact picker's list contacts_scroll: UniformListScrollHandle, + /// Whether the inbox list is showing chat requests instead of rooms + requests_open: bool, + /// Scroll state of the requests list + requests_scroll: UniformListScrollHandle, channels_open: bool, admins_open: bool, members_open: bool, @@ -117,6 +121,11 @@ impl Sidebar { }), ); + // Keep the room and request counts in sync with the chat registry. + subscriptions.push(cx.observe(&chat, |_this, _chat, cx| { + cx.notify(); + })); + subscriptions.push(cx.subscribe_in( &communities, window, @@ -152,6 +161,8 @@ impl Sidebar { contacts: None, selected_contacts: HashSet::new(), contacts_scroll: UniformListScrollHandle::new(), + requests_open: false, + requests_scroll: UniformListScrollHandle::new(), channels_open: true, admins_open: true, members_open: true, @@ -167,6 +178,7 @@ impl Sidebar { } self.active_tab = tab; self.close_contacts(); + self.close_requests(); cx.notify(); } @@ -182,8 +194,28 @@ impl Sidebar { if !self.contacts_open { self.selected_contacts.clear(); - } else if self.contacts.is_none() { - self.load_contacts(window, cx); + } else { + self.close_requests(); + if self.contacts.is_none() { + self.load_contacts(window, cx); + } + } + + cx.notify(); + } + + /// Leave the requests list in the inbox. + fn close_requests(&mut self) { + self.requests_open = false; + } + + /// Show or hide the chat requests in the inbox list. + fn toggle_requests(&mut self, cx: &mut Context) { + self.requests_open = !self.requests_open; + + if self.requests_open { + self.close_contacts(); + self.new_requests = false; } cx.notify(); @@ -435,24 +467,28 @@ impl Sidebar { fn render_tabs(&mut self, cx: &mut Context) -> AnyElement { let sidebar = cx.entity().downgrade(); let active_tab = self.active_tab; - let contacts_open = self.contacts_open && active_tab == SidebarTab::Inbox; + let requests_open = self.requests_open && active_tab == SidebarTab::Inbox; + let contacts_open = self.contacts_open && active_tab == SidebarTab::Inbox && !requests_open; let rows = Rc::new(self.rows_for(active_tab, cx)); + + // Whether to show the create button for the active tab let create_button = contacts_open && !self.selected_contacts.is_empty(); - let (list_id, scroll_handle) = if contacts_open { - ("sidebar-contacts", &self.contacts_scroll) + let (list_id, scroll_handle, list_title) = if contacts_open { + ("sidebar-contacts", &self.contacts_scroll, "Contacts") + } else if requests_open { + ("sidebar-requests", &self.requests_scroll, "Requests") } else { ( active_tab.list_id(), &self.scroll_handles[active_tab.index()], + active_tab.list_title(), ) }; - let list_title = if contacts_open { - "Contacts" - } else { - active_tab.list_title() - }; + let request_count = ChatRegistry::global(cx) + .read(cx) + .count(&RoomKind::Request, cx); v_flex() .size_full() @@ -488,32 +524,49 @@ impl Sidebar { .px_2() .gap_1() .child( - NavItem::new( - "new-chat", - "New Chat", - Icon::new(IconName::Message).small(), - ) - .on_click(|_event, window, cx| { - window.dispatch_action(Box::new(Command::NewChat), cx) - }), + NavItem::new("chat", "New Chat", Icon::new(IconName::Message).small()) + .on_click(|_event, window, cx| { + window.dispatch_action(Box::new(Command::NewChat), cx) + }), ) .child( NavItem::new("reqs", "Requests", Icon::new(IconName::Invite).small()) - .when(self.new_requests, |this| { - this.suffix(div().size_1().rounded_full().bg(cx.theme().cursor)) + .when(request_count > 0, |this| { + this.suffix( + h_flex() + .gap_1() + .items_center() + .child( + h_flex() + .py_0p5() + .px_1() + .min_w_6() + .justify_center() + .text_size(px(10.)) + .text_color(cx.theme().text_muted) + .text_center() + .rounded(cx.theme().radius) + .bg(cx.theme().elevated_surface_background) + .child(SharedString::from( + request_count.to_string(), + )), + ) + .when(self.new_requests && !requests_open, |this| { + this.child( + div() + .size_1() + .rounded_full() + .bg(cx.theme().cursor), + ) + }), + ) }) - .on_click({ - let sidebar = sidebar.clone(); - move |_event, window, cx| { - if let Err(error) = sidebar.update(cx, |this, cx| { - this.new_requests = false; - cx.notify(); - }) { - log::error!("Failed to clear new requests: {error}"); - } - window.dispatch_action(Box::new(Command::ShowRequests), cx); - } - }), + .when(requests_open, |this| { + this.bg(cx.theme().ghost_element_active) + }) + .on_click(cx.listener(|this, _event, _window, cx| { + this.toggle_requests(cx); + })), ) .child( NavItem::new("contacts", "Contacts", Icon::new(IconName::Book).small()) @@ -743,6 +796,14 @@ impl Sidebar { .into_iter() .map(|public_key| SidebarRow::Contact { public_key }) .collect(), + SidebarTab::Inbox if self.requests_open => { + let chat = ChatRegistry::global(cx); + chat.read(cx) + .rooms(&RoomKind::Request, cx) + .into_iter() + .map(|room| SidebarRow::Room { room }) + .collect() + } SidebarTab::Inbox => { let chat = ChatRegistry::global(cx); chat.read(cx)