add requests panel

This commit is contained in:
2026-09-28 10:06:29 +07:00
parent d3c3263c58
commit 9b06428f44
+93 -32
View File
@@ -92,6 +92,10 @@ pub struct Sidebar {
selected_contacts: HashSet<PublicKey>, selected_contacts: HashSet<PublicKey>,
/// Scroll state of the contact picker's list /// Scroll state of the contact picker's list
contacts_scroll: UniformListScrollHandle, 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, channels_open: bool,
admins_open: bool, admins_open: bool,
members_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( subscriptions.push(cx.subscribe_in(
&communities, &communities,
window, window,
@@ -152,6 +161,8 @@ impl Sidebar {
contacts: None, contacts: None,
selected_contacts: HashSet::new(), selected_contacts: HashSet::new(),
contacts_scroll: UniformListScrollHandle::new(), contacts_scroll: UniformListScrollHandle::new(),
requests_open: false,
requests_scroll: UniformListScrollHandle::new(),
channels_open: true, channels_open: true,
admins_open: true, admins_open: true,
members_open: true, members_open: true,
@@ -167,6 +178,7 @@ impl Sidebar {
} }
self.active_tab = tab; self.active_tab = tab;
self.close_contacts(); self.close_contacts();
self.close_requests();
cx.notify(); cx.notify();
} }
@@ -182,8 +194,28 @@ impl Sidebar {
if !self.contacts_open { if !self.contacts_open {
self.selected_contacts.clear(); self.selected_contacts.clear();
} else if self.contacts.is_none() { } else {
self.load_contacts(window, cx); 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>) {
self.requests_open = !self.requests_open;
if self.requests_open {
self.close_contacts();
self.new_requests = false;
} }
cx.notify(); cx.notify();
@@ -435,24 +467,28 @@ impl Sidebar {
fn render_tabs(&mut self, cx: &mut Context<Self>) -> AnyElement { fn render_tabs(&mut self, cx: &mut Context<Self>) -> AnyElement {
let sidebar = cx.entity().downgrade(); let sidebar = cx.entity().downgrade();
let active_tab = self.active_tab; 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)); 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 create_button = contacts_open && !self.selected_contacts.is_empty();
let (list_id, scroll_handle) = if contacts_open { let (list_id, scroll_handle, list_title) = if contacts_open {
("sidebar-contacts", &self.contacts_scroll) ("sidebar-contacts", &self.contacts_scroll, "Contacts")
} else if requests_open {
("sidebar-requests", &self.requests_scroll, "Requests")
} else { } else {
( (
active_tab.list_id(), active_tab.list_id(),
&self.scroll_handles[active_tab.index()], &self.scroll_handles[active_tab.index()],
active_tab.list_title(),
) )
}; };
let list_title = if contacts_open { let request_count = ChatRegistry::global(cx)
"Contacts" .read(cx)
} else { .count(&RoomKind::Request, cx);
active_tab.list_title()
};
v_flex() v_flex()
.size_full() .size_full()
@@ -488,32 +524,49 @@ impl Sidebar {
.px_2() .px_2()
.gap_1() .gap_1()
.child( .child(
NavItem::new( NavItem::new("chat", "New Chat", Icon::new(IconName::Message).small())
"new-chat", .on_click(|_event, window, cx| {
"New Chat", window.dispatch_action(Box::new(Command::NewChat), cx)
Icon::new(IconName::Message).small(), }),
)
.on_click(|_event, window, cx| {
window.dispatch_action(Box::new(Command::NewChat), cx)
}),
) )
.child( .child(
NavItem::new("reqs", "Requests", Icon::new(IconName::Invite).small()) NavItem::new("reqs", "Requests", Icon::new(IconName::Invite).small())
.when(self.new_requests, |this| { .when(request_count > 0, |this| {
this.suffix(div().size_1().rounded_full().bg(cx.theme().cursor)) 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({ .when(requests_open, |this| {
let sidebar = sidebar.clone(); this.bg(cx.theme().ghost_element_active)
move |_event, window, cx| { })
if let Err(error) = sidebar.update(cx, |this, cx| { .on_click(cx.listener(|this, _event, _window, cx| {
this.new_requests = false; this.toggle_requests(cx);
cx.notify(); })),
}) {
log::error!("Failed to clear new requests: {error}");
}
window.dispatch_action(Box::new(Command::ShowRequests), cx);
}
}),
) )
.child( .child(
NavItem::new("contacts", "Contacts", Icon::new(IconName::Book).small()) NavItem::new("contacts", "Contacts", Icon::new(IconName::Book).small())
@@ -743,6 +796,14 @@ impl Sidebar {
.into_iter() .into_iter()
.map(|public_key| SidebarRow::Contact { public_key }) .map(|public_key| SidebarRow::Contact { public_key })
.collect(), .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 => { SidebarTab::Inbox => {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
chat.read(cx) chat.read(cx)