refactor panel open flow

This commit is contained in:
2026-09-23 08:53:20 +07:00
parent 6510e45e4e
commit 7ba289440b
9 changed files with 122 additions and 191 deletions
+5 -30
View File
@@ -9,7 +9,7 @@ use fuzzy_matcher::FuzzyMatcher;
use fuzzy_matcher::skim::SkimMatcherV2; use fuzzy_matcher::skim::SkimMatcherV2;
use gpui::{ use gpui::{
App, AppContext, Context, Entity, EventEmitter, Global, SharedString, Subscription, Task, App, AppContext, Context, Entity, EventEmitter, Global, SharedString, Subscription, Task,
WeakEntity, Window, WeakEntity,
}; };
use instant::Duration; use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
@@ -37,15 +37,8 @@ impl Global for GlobalChatRegistry {}
/// Chat event. /// Chat event.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChatEvent { pub enum ChatEvent {
/// An event to open a room by its ID
OpenRoom(u64),
/// An event to close a room by its ID
CloseRoom(u64),
/// An event to notify UI about a new chat request
Ping, Ping,
/// No Inbox Relays found, the app is not ready to subscribe messages
InboxRelayNotFound, InboxRelayNotFound,
/// An error occurred
Error(String), Error(String),
} }
@@ -543,34 +536,16 @@ impl ChatRegistry {
cx.notify(); cx.notify();
} }
/// Emit an open room event. /// Track a room so it is listed and can be looked up by id.
/// pub fn track_room(&mut self, room: &Entity<Room>, cx: &mut Context<Self>) {
/// If the room is new, add it to the registry.
pub fn emit_room(&mut self, room: &Entity<Room>, window: &mut Window, cx: &mut Context<Self>) {
// Get the room's ID.
let id = room.read(cx).id; let id = room.read(cx).id;
// If the room is new, add it to the registry and index.
if let hash_map::Entry::Vacant(e) = self.room_index.entry(id) { if let hash_map::Entry::Vacant(e) = self.room_index.entry(id) {
let entity = room.to_owned(); let entity = room.to_owned();
e.insert(entity.clone()); e.insert(entity.clone());
self.rooms.insert(0, entity); self.rooms.insert(0, entity);
} cx.notify();
// Emit the open room event deferred to avoid re-entrant reads
cx.defer_in(window, move |_this, _window, cx| {
cx.emit(ChatEvent::OpenRoom(id));
});
}
/// Close a room.
pub fn close_room(&mut self, id: u64, window: &mut Window, cx: &mut Context<Self>) {
if self.room_index.contains_key(&id) {
self.room_index.remove(&id);
self.rooms.retain(|r| r.read(cx).id != id);
cx.defer_in(window, move |_this, _window, cx| {
cx.emit(ChatEvent::CloseRoom(id));
});
} }
} }
-2
View File
@@ -107,8 +107,6 @@ impl SubscriptionKey {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CommunityEvent { pub enum CommunityEvent {
Updated(CommunityId), Updated(CommunityId),
Open(CommunityId),
Close(CommunityId),
Channel(CommunityId, ChannelId), Channel(CommunityId, ChannelId),
/// History exists here that no held key can open. /// History exists here that no held key can open.
Unreadable(CommunityId), Unreadable(CommunityId),
+1 -22
View File
@@ -7,7 +7,7 @@ pub use concord::cord03::{ChatMessage, ReplyRef};
use concord::state::CommunityState; use concord::state::CommunityState;
pub use concord::{ChannelId, CommunityId, Epoch}; pub use concord::{ChannelId, CommunityId, Epoch};
use futures::future::{Either, select}; use futures::future::{Either, select};
use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Subscription, Task, Window}; use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Subscription, Task};
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::NostrRegistry; use state::NostrRegistry;
@@ -225,27 +225,6 @@ impl CommunityRegistry {
self.index.get(id).cloned() self.index.get(id).cloned()
} }
/// Ask the workspace to open a community's panel.
pub fn emit_community(
&mut self,
community: &Entity<Community>,
window: &mut Window,
cx: &mut Context<Self>,
) {
let id = community.read(cx).id();
cx.defer_in(window, move |_this, _window, cx| {
cx.emit(CommunityEvent::Open(id));
});
}
/// Ask the workspace to close a community's panel.
pub fn emit_close(&mut self, id: CommunityId, window: &mut Window, cx: &mut Context<Self>) {
cx.defer_in(window, move |_this, _window, cx| {
cx.emit(CommunityEvent::Close(id));
});
}
/// Create a community owned by the current account and begin tracking it. /// Create a community owned by the current account and begin tracking it.
pub fn create(&mut self, metadata: CommunityMetadata, cx: &mut Context<Self>) { pub fn create(&mut self, metadata: CommunityMetadata, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
+9 -11
View File
@@ -145,21 +145,19 @@ impl CommunityPanel {
window, window,
|_this, _community, event, window, cx| { |_this, _community, event, window, cx| {
match event { match event {
CommunityEvent::Updated(_)
| CommunityEvent::Unreadable(_)
| CommunityEvent::Failed(_) => {
cx.defer_in(window, |this, window, cx| this.reload(window, cx));
}
CommunityEvent::Channel(..) => { CommunityEvent::Channel(..) => {
cx.defer_in(window, |this, window, cx| this.load(window, cx)); cx.defer_in(window, |this, window, cx| {
this.load(window, cx);
});
} }
CommunityEvent::Error(error) => { CommunityEvent::Error(error) => {
window.push_notification( window.push_notification(Notification::error(error.clone()), cx);
Notification::error(error.clone()).autohide(false), }
cx, _ => {
); cx.defer_in(window, |this, window, cx| {
this.reload(window, cx);
});
} }
CommunityEvent::Open(_) | CommunityEvent::Close(_) => {}
}; };
}, },
)); ));
+13
View File
@@ -95,6 +95,19 @@ pub fn add_panel(
area.add_panel_view(Arc::new(panel), placement, None, window, cx); area.add_panel_view(Arc::new(panel), placement, None, window, cx);
} }
/// Add a panel to a dock reached through a weak handle.
pub fn add_panel_to(
dock: &WeakEntity<DockArea>,
panel: PanelHandle,
placement: DockPlacement,
window: &mut Window,
cx: &mut App,
) {
let _ = dock.update(cx, |area, cx| {
add_panel(area, panel, placement, window, cx);
});
}
/// The panel in any region of `area` whose logical id is `key`. /// The panel in any region of `area` whose logical id is `key`.
fn find_panel(area: &DockArea, key: &SharedString, cx: &App) -> Option<(PanelId, NodeId, usize)> { fn find_panel(area: &DockArea, key: &SharedString, cx: &App) -> Option<(PanelId, NodeId, usize)> {
let placements = [ let placements = [
+22 -7
View File
@@ -2,17 +2,18 @@ use chat::{ChatRegistry, Room, RoomKind};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled,
Subscription, Window, div, px, Subscription, WeakEntity, Window, div, px,
}; };
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::NostrRegistry; use state::NostrRegistry;
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
use ui::dock::{DockArea, DockPlacement, PanelHandle};
use ui::input::{Input, InputEvent, InputState}; use ui::input::{Input, InputEvent, InputState};
use ui::{StyledExt, WindowExtension, v_flex}; use ui::{StyledExt, WindowExtension, v_flex};
pub fn open(window: &mut Window, cx: &mut App) { pub fn open(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) {
let view = cx.new(|cx| NewChat::new(window, cx)); let view = cx.new(|cx| NewChat::new(dock, window, cx));
window.open_modal(cx, move |this, _window, _cx| { window.open_modal(cx, move |this, _window, _cx| {
this.width(px(420.)).title("New chat").child(view.clone()) this.width(px(420.)).title("New chat").child(view.clone())
@@ -20,6 +21,9 @@ pub fn open(window: &mut Window, cx: &mut App) {
} }
pub struct NewChat { pub struct NewChat {
/// The dock a started chat opens in
dock: WeakEntity<DockArea>,
/// Public key input /// Public key input
input: Entity<InputState>, input: Entity<InputState>,
@@ -31,7 +35,7 @@ pub struct NewChat {
} }
impl NewChat { impl NewChat {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { fn new(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let input = cx.new(|cx| InputState::new(window, cx).placeholder("npub")); let input = cx.new(|cx| InputState::new(window, cx).placeholder("npub"));
let subscription = cx.subscribe_in(&input, window, |this, _input, event, window, cx| { let subscription = cx.subscribe_in(&input, window, |this, _input, event, window, cx| {
@@ -41,6 +45,7 @@ impl NewChat {
}); });
Self { Self {
dock,
input, input,
error: None, error: None,
_subscription: Some(subscription), _subscription: Some(subscription),
@@ -56,6 +61,8 @@ impl NewChat {
}; };
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let chat = ChatRegistry::global(cx);
let Some(current_user) = nostr.read(cx).current_user() else { let Some(current_user) = nostr.read(cx).current_user() else {
self.set_error("You are not signed in", cx); self.set_error("You are not signed in", cx);
return; return;
@@ -70,12 +77,20 @@ impl NewChat {
.organize(&current_user) .organize(&current_user)
.kind(RoomKind::Ongoing); .kind(RoomKind::Ongoing);
let chat = ChatRegistry::global(cx); let room = chat.update(cx, |chat, cx| {
chat.update(cx, |chat, cx| {
let room = cx.new(|_| room); let room = cx.new(|_| room);
chat.emit_room(&room, window, cx); chat.track_room(&room, cx);
room
}); });
ui::dock::add_panel_to(
&self.dock,
PanelHandle::new(chat_ui::init(room.downgrade(), window, cx)),
DockPlacement::Center,
window,
cx,
);
window.close_modal(cx); window.close_modal(cx);
} }
+13 -86
View File
@@ -6,13 +6,11 @@ use anyhow::Error;
use auto_update::AutoUpdater; use auto_update::AutoUpdater;
use chat::{ChatEvent, ChatRegistry}; use chat::{ChatEvent, ChatRegistry};
use common::download_dir; use common::download_dir;
use community::{CommunityEvent, CommunityRegistry};
use community_ui::CommunityPanel;
use device::{DeviceEvent, DeviceRegistry}; use device::{DeviceEvent, DeviceRegistry};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
Action, AnyElement, App, AppContext, Context, Entity, InteractiveElement, IntoElement, Action, AnyElement, App, AppContext, Context, Entity, InteractiveElement, IntoElement,
ParentElement, Render, SharedString, Styled, Subscription, Task, WeakEntity, Window, div, px, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, div, px,
}; };
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::{PersonRegistry, shorten_pubkey}; use person::{PersonRegistry, shorten_pubkey};
@@ -21,7 +19,7 @@ use smallvec::{SmallVec, smallvec};
use state::{NostrRegistry, StateEvent}; use state::{NostrRegistry, StateEvent};
use theme::{ActiveTheme, SIDEBAR_WIDTH, Theme, ThemeRegistry}; use theme::{ActiveTheme, SIDEBAR_WIDTH, Theme, ThemeRegistry};
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
use ui::dock::{self, ClosePanel, DockArea, DockLayout, DockPlacement, Panel, PanelHandle}; use ui::dock::{self, DockArea, DockLayout, DockPlacement, Panel, PanelHandle};
use ui::menu::{DropdownMenu, PopupMenuItem}; use ui::menu::{DropdownMenu, PopupMenuItem};
use ui::notification::{Notification, NotificationKind}; use ui::notification::{Notification, NotificationKind};
use ui::{Icon, IconName, Root, Sizable, WindowExtension, h_flex, v_flex}; use ui::{Icon, IconName, Root, Sizable, WindowExtension, h_flex, v_flex};
@@ -72,8 +70,6 @@ enum Command {
pub struct Workspace { pub struct Workspace {
dock: Entity<DockArea>, dock: Entity<DockArea>,
title_bar_chrome: Rc<dock::TitleBarChrome>, title_bar_chrome: Rc<dock::TitleBarChrome>,
/// The community panel currently docked, if any
community_panel: Option<WeakEntity<CommunityPanel>>,
/// Async tasks /// Async tasks
tasks: Vec<Task<Result<(), Error>>>, tasks: Vec<Task<Result<(), Error>>>,
/// Event subscriptions /// Event subscriptions
@@ -83,12 +79,11 @@ pub struct Workspace {
impl Workspace { impl Workspace {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let communities = CommunityRegistry::global(cx);
let device = DeviceRegistry::global(cx); let device = DeviceRegistry::global(cx);
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let sidebar = cx.new(|cx| Sidebar::new(window, cx));
let (dock, title_bar_chrome) = dock::dock_area("coop", window, cx); let (dock, title_bar_chrome) = dock::dock_area("coop", window, cx);
let sidebar = cx.new(|cx| Sidebar::new(window, dock.downgrade(), cx));
let mut subscriptions = smallvec![]; let mut subscriptions = smallvec![];
@@ -164,7 +159,7 @@ impl Workspace {
subscriptions.push( subscriptions.push(
// Observe all events emitted by the chat registry // Observe all events emitted by the chat registry
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| { cx.subscribe_in(&chat, window, move |_this, _chat, ev, window, cx| {
match ev { match ev {
ChatEvent::InboxRelayNotFound => { ChatEvent::InboxRelayNotFound => {
const MSG: &str = "Messaging Relays not found. Cannot receive messages."; const MSG: &str = "Messaging Relays not found. Cannot receive messages.";
@@ -187,28 +182,6 @@ impl Workspace {
cx, cx,
); );
} }
ChatEvent::OpenRoom(id) => {
if let Some(room) = chat.read(cx).room(id, cx) {
this.add_panel_to_dock(
chat_ui::init(room, window, cx),
DockPlacement::Center,
window,
cx,
);
}
}
ChatEvent::CloseRoom(..) => {
this.dock.update(cx, |area, cx| {
// Force focus to the tab panel
ui::dock::focus_tab_panel(area, window, cx);
// Dispatch the close panel action
cx.defer_in(window, |_, window, cx| {
window.dispatch_action(Box::new(ClosePanel), cx);
window.close_all_modals(cx);
});
});
}
ChatEvent::Error(error) => { ChatEvent::Error(error) => {
window.push_notification(Notification::error(error).autohide(false), cx); window.push_notification(Notification::error(error).autohide(false), cx);
} }
@@ -217,71 +190,24 @@ impl Workspace {
}), }),
); );
subscriptions.push(
// Observe all events emitted by the community registry
cx.subscribe_in(
&communities,
window,
move |this, communities, event, window, cx| match event {
CommunityEvent::Open(id) => {
if let Some(community) = communities.read(cx).community(id) {
let panel = community_ui::init(community, window, cx);
this.community_panel = Some(panel.downgrade());
this.add_panel_to_dock(panel, DockPlacement::Center, window, cx);
}
}
CommunityEvent::Close(_) => {
let Some(panel) = this
.community_panel
.take()
.and_then(|panel| panel.upgrade())
else {
return;
};
this.dock.update(cx, |area, cx| {
ui::dock::add_panel(
area,
PanelHandle::new(panel),
DockPlacement::Center,
window,
cx,
);
ui::dock::focus_tab_panel(area, window, cx);
cx.defer_in(window, |_, window, cx| {
window.dispatch_action(Box::new(ClosePanel), cx);
});
});
}
_ => {}
},
),
);
cx.defer_in(window, move |this, window, cx| { cx.defer_in(window, move |this, window, cx| {
let sidebar = PanelHandle::new(sidebar); let sidebar = PanelHandle::new(sidebar);
this.dock.update(cx, |area, cx| {
let left = DockLayout::tabs().panel_view(Arc::new(sidebar), cx);
area.set_dock(DockPlacement::Left, left, window, cx);
area.set_dock_size(DockPlacement::Left, SIDEBAR_WIDTH, window, cx);
});
let greeter = PanelHandle::new(greeter::init(window, cx)); let greeter = PanelHandle::new(greeter::init(window, cx));
this.dock.update(cx, |this, cx| {
let left = DockLayout::tabs().panel_view(Arc::new(sidebar), cx);
let center = DockLayout::v_split() let center = DockLayout::v_split()
.child(DockLayout::tabs().panel_view(Arc::new(greeter), cx), None); .child(DockLayout::tabs().panel_view(Arc::new(greeter), cx), None);
this.dock.update(cx, |area, cx| { this.set_dock(DockPlacement::Left, left, window, cx);
area.set_center(center, window, cx); this.set_dock_size(DockPlacement::Left, SIDEBAR_WIDTH, window, cx);
this.set_center(center, window, cx);
}); });
}); });
Self { Self {
dock, dock,
title_bar_chrome, title_bar_chrome,
community_panel: None,
tasks: vec![], tasks: vec![],
_subscriptions: subscriptions, _subscriptions: subscriptions,
} }
@@ -365,10 +291,11 @@ impl Workspace {
self.add_panel_to_dock(browse::init(window, cx), DockPlacement::Center, window, cx); self.add_panel_to_dock(browse::init(window, cx), DockPlacement::Center, window, cx);
} }
Command::ShowSearch => { Command::ShowSearch => {
self.add_panel_to_dock(search::init(window, cx), DockPlacement::Center, window, cx); let panel = search::init(self.dock.downgrade(), window, cx);
self.add_panel_to_dock(panel, DockPlacement::Center, window, cx);
} }
Command::NewChat => { Command::NewChat => {
new_chat::open(window, cx); new_chat::open(self.dock.downgrade(), window, cx);
} }
Command::NewCommunity => { Command::NewCommunity => {
new_community::open(window, cx); new_community::open(window, cx);
+25 -9
View File
@@ -7,8 +7,8 @@ use common::DebouncedDelay;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AnyElement, App, AppContext, Context, ElementId, Entity, EventEmitter, FocusHandle, Focusable, AnyElement, App, AppContext, Context, ElementId, Entity, EventEmitter, FocusHandle, Focusable,
IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, Window, div, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Task, WeakEntity,
uniform_list, Window, div, uniform_list,
}; };
use instant::Duration; use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
@@ -17,7 +17,7 @@ use smallvec::{SmallVec, smallvec};
use state::{FIND_DELAY, NostrRegistry}; use state::{FIND_DELAY, NostrRegistry};
use theme::ActiveTheme; use theme::ActiveTheme;
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
use ui::dock::{Panel, PanelEvent}; use ui::dock::{DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
use ui::input::{Input, InputEvent, InputState}; use ui::input::{Input, InputEvent, InputState};
use ui::notification::Notification; use ui::notification::Notification;
use ui::{Icon, IconName, Selectable, Sizable, StyledExt, WindowExtension, h_flex, v_flex}; use ui::{Icon, IconName, Selectable, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
@@ -26,13 +26,15 @@ use crate::sidebar::{TreeRow, TreeRowKind};
const INPUT_PLACEHOLDER: &str = "Find or start a conversation"; const INPUT_PLACEHOLDER: &str = "Find or start a conversation";
pub fn init(window: &mut Window, cx: &mut App) -> Entity<SearchPanel> { pub fn init(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut App) -> Entity<SearchPanel> {
cx.new(|cx| SearchPanel::new(window, cx)) cx.new(|cx| SearchPanel::new(dock, window, cx))
} }
pub struct SearchPanel { pub struct SearchPanel {
name: SharedString, name: SharedString,
focus_handle: FocusHandle, focus_handle: FocusHandle,
/// The dock a started chat opens in
dock: WeakEntity<DockArea>,
/// Find input state /// Find input state
find_input: Entity<InputState>, find_input: Entity<InputState>,
@@ -63,7 +65,7 @@ pub struct SearchPanel {
} }
impl SearchPanel { impl SearchPanel {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { fn new(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut Context<Self>) -> Self {
let contact_list = cx.new(|_| None); let contact_list = cx.new(|_| None);
let selected_pkeys = cx.new(|_| HashSet::new()); let selected_pkeys = cx.new(|_| HashSet::new());
let find_results = cx.new(|_| None); let find_results = cx.new(|_| None);
@@ -107,6 +109,7 @@ impl SearchPanel {
Self { Self {
name: "Search".into(), name: "Search".into(),
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
dock,
find_input, find_input,
find_debouncer: DebouncedDelay::new(), find_debouncer: DebouncedDelay::new(),
find_results, find_results,
@@ -285,6 +288,7 @@ impl SearchPanel {
fn create_room(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn create_room(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let async_chat = chat.downgrade(); let async_chat = chat.downgrade();
let dock = self.dock.clone();
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let Some(public_key) = nostr.read(cx).current_user() else { let Some(public_key) = nostr.read(cx).current_user() else {
@@ -295,14 +299,26 @@ impl SearchPanel {
let receivers = self.get_selected(cx); let receivers = self.get_selected(cx);
self.tasks.push(cx.spawn_in(window, async move |this, cx| { self.tasks.push(cx.spawn_in(window, async move |this, cx| {
// Create a new room and emit it // Create a new room and register it
async_chat.update_in(cx, |this, _window, cx| { let room = async_chat.update_in(cx, |chat, _window, cx| {
let room = cx.new(|_| { let room = cx.new(|_| {
Room::new(public_key, receivers) Room::new(public_key, receivers)
.organize(&public_key) .organize(&public_key)
.kind(RoomKind::Ongoing) .kind(RoomKind::Ongoing)
}); });
this.emit_room(&room, _window, cx); chat.track_room(&room, cx);
room
})?;
// Open it in the dock
cx.update(|window, cx| {
ui::dock::add_panel_to(
&dock,
PanelHandle::new(chat_ui::init(room.downgrade(), window, cx)),
DockPlacement::Center,
window,
cx,
);
})?; })?;
// Reset the find panel // Reset the find panel
+32 -22
View File
@@ -2,7 +2,7 @@ use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
use auto_update::AutoUpdater; use auto_update::AutoUpdater;
use chat::{ChatEvent, ChatRegistry, RoomKind}; use chat::{ChatEvent, ChatRegistry, Room, RoomKind};
use common::TimestampExt; use common::TimestampExt;
use community::{ChannelId, Community, CommunityEvent, CommunityRegistry}; use community::{ChannelId, Community, CommunityEvent, CommunityRegistry};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
@@ -20,7 +20,7 @@ use state::NostrRegistry;
use theme::{ActiveTheme, TABBAR_HEIGHT}; use theme::{ActiveTheme, TABBAR_HEIGHT};
use ui::avatar::Avatar; use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
use ui::dock::{Panel, PanelEvent}; use ui::dock::{DockArea, DockPlacement, Panel, PanelEvent, PanelHandle};
use ui::indicator::Indicator; use ui::indicator::Indicator;
use ui::menu::{DropdownMenu, PopupMenuItem}; use ui::menu::{DropdownMenu, PopupMenuItem};
use ui::nav_item::NavItem; use ui::nav_item::NavItem;
@@ -46,6 +46,8 @@ pub struct Sidebar {
scroll_handles: [UniformListScrollHandle; 3], scroll_handles: [UniformListScrollHandle; 3],
/// Scroll state of the channel and member lists /// Scroll state of the channel and member lists
community_scroll: ScrollHandle, community_scroll: ScrollHandle,
/// The dock the sidebar opens its panels in
dock: WeakEntity<DockArea>,
active_tab: SidebarTab, active_tab: SidebarTab,
/// The community the sidebar is browsing, if any /// The community the sidebar is browsing, if any
community: Option<WeakEntity<Community>>, community: Option<WeakEntity<Community>>,
@@ -59,7 +61,7 @@ pub struct Sidebar {
} }
impl Sidebar { impl Sidebar {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(window: &mut Window, dock: WeakEntity<DockArea>, cx: &mut Context<Self>) -> Self {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let communities = CommunityRegistry::global(cx); let communities = CommunityRegistry::global(cx);
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
@@ -98,6 +100,7 @@ impl Sidebar {
UniformListScrollHandle::new(), UniformListScrollHandle::new(),
], ],
community_scroll: ScrollHandle::default(), community_scroll: ScrollHandle::default(),
dock,
active_tab: SidebarTab::Recents, active_tab: SidebarTab::Recents,
community: None, community: None,
channels_open: true, channels_open: true,
@@ -130,25 +133,34 @@ impl Sidebar {
settings.record_recent_community(id, cx); settings.record_recent_community(id, cx);
}); });
CommunityRegistry::global(cx).update(cx, |registry, cx| {
registry.emit_community(&community, window, cx);
});
self.community = Some(community.downgrade()); self.community = Some(community.downgrade());
ui::dock::add_panel_to(
&self.dock,
PanelHandle::new(community_ui::init(community, window, cx)),
DockPlacement::Center,
window,
cx,
);
cx.notify(); cx.notify();
} }
fn close_community(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn open_room(&mut self, room: Entity<Room>, window: &mut Window, cx: &mut Context<Self>) {
let Some(community) = self.community.take() else { ui::dock::add_panel_to(
return; &self.dock,
}; PanelHandle::new(chat_ui::init(room.downgrade(), window, cx)),
DockPlacement::Center,
if let Ok(id) = community.read_with(cx, |community, _cx| community.id()) { window,
CommunityRegistry::global(cx).update(cx, |registry, cx| { cx,
registry.emit_close(id, window, cx); );
});
} }
/// Leave the community view, returning the sidebar to its tab list.
fn reset_community(&mut self, cx: &mut Context<Self>) {
if self.community.take().is_none() {
return;
}
cx.notify(); cx.notify();
} }
@@ -250,8 +262,8 @@ impl Sidebar {
.tooltip("Back") .tooltip("Back")
.ghost() .ghost()
.small() .small()
.on_click(cx.listener(|this, _event, window, cx| { .on_click(cx.listener(|this, _event, _window, cx| {
this.close_community(window, cx); this.reset_community(cx);
})), })),
); );
} }
@@ -407,10 +419,8 @@ fn render_rows(range: Range<usize>, rows: &[SidebarRow], cx: &Context<Sidebar>)
let created_at = room.read(cx).created_at.to_ago(); let created_at = room.read(cx).created_at.to_ago();
let room_clone = room.clone(); let room_clone = room.clone();
let handler = cx.listener(move |_this, _event, window, cx| { let handler = cx.listener(move |this, _event, window, cx| {
ChatRegistry::global(cx).update(cx, |chat, cx| { this.open_room(room_clone.clone(), window, cx);
chat.emit_room(&room_clone, window, cx);
});
}); });
TreeRow::new( TreeRow::new(