clean up the sidebar
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M2.75 12C2.75 17.1086 6.89137 21.25 12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.25 13L12 16.25L8.75 13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 7.75V15.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 571 B |
@@ -19,9 +19,6 @@ pub enum PanelEvent {
|
|||||||
|
|
||||||
pub trait Panel: EventEmitter<PanelEvent> + Render + Focusable {
|
pub trait Panel: EventEmitter<PanelEvent> + Render + Focusable {
|
||||||
/// The name of the panel used to serialize, deserialize and identify the panel.
|
/// The name of the panel used to serialize, deserialize and identify the panel.
|
||||||
///
|
|
||||||
/// This is used to identify the panel when deserializing the panel.
|
|
||||||
/// Once you have defined a panel id, this must not be changed.
|
|
||||||
fn panel_id(&self) -> SharedString;
|
fn panel_id(&self) -> SharedString;
|
||||||
|
|
||||||
/// The title of the panel
|
/// The title of the panel
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ impl<T: IconNamed> From<T> for Icon {
|
|||||||
pub enum IconName {
|
pub enum IconName {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
|
ArrowDownCircle,
|
||||||
Boom,
|
Boom,
|
||||||
Book,
|
Book,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
@@ -99,6 +100,7 @@ impl IconNamed for IconName {
|
|||||||
match self {
|
match self {
|
||||||
Self::ArrowLeft => "icons/arrow-left.svg",
|
Self::ArrowLeft => "icons/arrow-left.svg",
|
||||||
Self::ArrowRight => "icons/arrow-right.svg",
|
Self::ArrowRight => "icons/arrow-right.svg",
|
||||||
|
Self::ArrowDownCircle => "icons/arrow-down-circle.svg",
|
||||||
Self::Boom => "icons/boom.svg",
|
Self::Boom => "icons/boom.svg",
|
||||||
Self::Book => "icons/book.svg",
|
Self::Book => "icons/book.svg",
|
||||||
Self::ChevronDown => "icons/chevron-down.svg",
|
Self::ChevronDown => "icons/chevron-down.svg",
|
||||||
|
|||||||
+4
-181
@@ -1,4 +1,3 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ::settings::AppSettings;
|
use ::settings::AppSettings;
|
||||||
@@ -7,22 +6,19 @@ use auto_update::AutoUpdater;
|
|||||||
use chat::{ChatEvent, ChatRegistry};
|
use chat::{ChatEvent, ChatRegistry};
|
||||||
use common::download_dir;
|
use common::download_dir;
|
||||||
use device::{DeviceEvent, DeviceRegistry};
|
use device::{DeviceEvent, DeviceRegistry};
|
||||||
use gpui::prelude::FluentBuilder;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, AnyElement, App, AppContext, Context, Entity, InteractiveElement, IntoElement,
|
Action, App, AppContext, Context, Entity, InteractiveElement, IntoElement, ParentElement,
|
||||||
ParentElement, Render, SharedString, Styled, Subscription, Task, Window, div, px,
|
Render, SharedString, Styled, Subscription, Task, Window, div, px,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::{PersonRegistry, shorten_pubkey};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use smallvec::{SmallVec, smallvec};
|
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, DockArea, DockLayout, DockPlacement, Panel, PanelHandle};
|
use ui::dock::{self, DockArea, DockLayout, DockPlacement, Panel, PanelHandle};
|
||||||
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::{IconName, Root, Sizable, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
use crate::dialogs::restore::RestoreEncryption;
|
use crate::dialogs::restore::RestoreEncryption;
|
||||||
use crate::dialogs::{new_chat, new_community, settings};
|
use crate::dialogs::{new_chat, new_community, settings};
|
||||||
@@ -69,7 +65,6 @@ enum Command {
|
|||||||
|
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
dock: Entity<DockArea>,
|
dock: Entity<DockArea>,
|
||||||
title_bar_chrome: Rc<dock::TitleBarChrome>,
|
|
||||||
/// Async tasks
|
/// Async tasks
|
||||||
tasks: Vec<Task<Result<(), Error>>>,
|
tasks: Vec<Task<Result<(), Error>>>,
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
@@ -82,7 +77,7 @@ impl Workspace {
|
|||||||
let device = DeviceRegistry::global(cx);
|
let device = DeviceRegistry::global(cx);
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
|
||||||
let (dock, title_bar_chrome) = dock::dock_area("coop", window, cx);
|
let (dock, _) = dock::dock_area("coop", window, cx);
|
||||||
let sidebar = cx.new(|cx| Sidebar::new(window, dock.downgrade(), cx));
|
let sidebar = cx.new(|cx| Sidebar::new(window, dock.downgrade(), cx));
|
||||||
|
|
||||||
let mut subscriptions = smallvec![];
|
let mut subscriptions = smallvec![];
|
||||||
@@ -207,7 +202,6 @@ impl Workspace {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
dock,
|
dock,
|
||||||
title_bar_chrome,
|
|
||||||
tasks: vec![],
|
tasks: vec![],
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
@@ -510,174 +504,6 @@ impl Workspace {
|
|||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn titlebar_right(_window: &mut Window, cx: &mut App) -> AnyElement {
|
|
||||||
let auto_updater = AutoUpdater::try_global(cx);
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
let nip4e_enabled = AppSettings::get_nip4e(cx);
|
|
||||||
let nostr = NostrRegistry::global(cx);
|
|
||||||
|
|
||||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
|
||||||
return div().into_any_element();
|
|
||||||
};
|
|
||||||
|
|
||||||
let persons = PersonRegistry::global(cx);
|
|
||||||
let profile = persons.read(cx).get(&public_key, cx);
|
|
||||||
let announcement = profile.announcement();
|
|
||||||
|
|
||||||
let updater_status = auto_updater.as_ref().and_then(|updater| {
|
|
||||||
let updater = updater.read(cx);
|
|
||||||
(!updater.idle()).then(|| updater.status())
|
|
||||||
});
|
|
||||||
|
|
||||||
let staged_update = auto_updater
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|updater| updater.read(cx).staged());
|
|
||||||
|
|
||||||
h_flex()
|
|
||||||
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
|
||||||
.gap_2()
|
|
||||||
.when_some(updater_status, |this, status| {
|
|
||||||
this.child(div().text_xs().italic().child(status))
|
|
||||||
})
|
|
||||||
.when(staged_update, |this| {
|
|
||||||
this.child(
|
|
||||||
Button::new("restart-to-update")
|
|
||||||
.label("Restart to Update")
|
|
||||||
.tooltip("Quit and relaunch into the installed update")
|
|
||||||
.small()
|
|
||||||
.ghost()
|
|
||||||
.on_click(|_event, _window, cx| {
|
|
||||||
if let Some(auto_updater) = AutoUpdater::try_global(cx) {
|
|
||||||
auto_updater.update(cx, |this, cx| this.restart(cx));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.when(nip4e_enabled, |this| {
|
|
||||||
this.child(
|
|
||||||
Button::new("key")
|
|
||||||
.icon(IconName::UserKey)
|
|
||||||
.tooltip("Decoupled encryption key")
|
|
||||||
.small()
|
|
||||||
.ghost()
|
|
||||||
.dropdown_menu(move |this, _window, _cx| {
|
|
||||||
this.min_w(px(260.))
|
|
||||||
.label("Encryption Key")
|
|
||||||
.when_some(announcement.as_ref(), |this, announcement| {
|
|
||||||
let name = announcement.client_name();
|
|
||||||
let pkey = shorten_pubkey(announcement.public_key(), 8);
|
|
||||||
|
|
||||||
this.item(PopupMenuItem::element(move |_window, cx| {
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.text_sm()
|
|
||||||
.child(
|
|
||||||
Icon::new(IconName::Device)
|
|
||||||
.small()
|
|
||||||
.text_color(cx.theme().icon_muted),
|
|
||||||
)
|
|
||||||
.child(name.clone())
|
|
||||||
}))
|
|
||||||
.item(
|
|
||||||
PopupMenuItem::element(move |_window, cx| {
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.text_sm()
|
|
||||||
.child(
|
|
||||||
Icon::new(IconName::UserKey)
|
|
||||||
.small()
|
|
||||||
.text_color(cx.theme().icon_muted),
|
|
||||||
)
|
|
||||||
.child(SharedString::from(pkey.clone()))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Backup",
|
|
||||||
IconName::Shield,
|
|
||||||
Box::new(Command::BackupEncryption),
|
|
||||||
)
|
|
||||||
.menu_with_icon(
|
|
||||||
"Restore from secret key",
|
|
||||||
IconName::Usb,
|
|
||||||
Box::new(Command::ImportEncryption),
|
|
||||||
)
|
|
||||||
.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Reload",
|
|
||||||
IconName::Refresh,
|
|
||||||
Box::new(Command::RefreshEncryption),
|
|
||||||
)
|
|
||||||
.menu_with_icon(
|
|
||||||
"Reset",
|
|
||||||
IconName::Warning,
|
|
||||||
Box::new(Command::ResetEncryption),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.child(
|
|
||||||
Button::new("inbox")
|
|
||||||
.icon(IconName::Inbox)
|
|
||||||
.small()
|
|
||||||
.ghost()
|
|
||||||
.dropdown_menu(move |this, _window, cx| {
|
|
||||||
let urls: Vec<(SharedString, SharedString)> = profile
|
|
||||||
.messaging_relays()
|
|
||||||
.iter()
|
|
||||||
.map(|url| {
|
|
||||||
(
|
|
||||||
SharedString::from(url.to_string()),
|
|
||||||
chat.read(cx).count_messages(url).to_string().into(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Header
|
|
||||||
let menu = this.min_w(px(260.)).label("Messaging Relays");
|
|
||||||
|
|
||||||
// Content
|
|
||||||
let menu = urls.into_iter().fold(menu, |this, (url, count)| {
|
|
||||||
this.item(PopupMenuItem::element(move |_window, cx| {
|
|
||||||
h_flex()
|
|
||||||
.px_1()
|
|
||||||
.w_full()
|
|
||||||
.text_sm()
|
|
||||||
.justify_between()
|
|
||||||
.child(url.clone())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.child(count.clone()),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Footer
|
|
||||||
menu.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Manage gossip relays",
|
|
||||||
IconName::Relay,
|
|
||||||
Box::new(Command::ShowRelayList),
|
|
||||||
)
|
|
||||||
.menu_with_icon(
|
|
||||||
"Manage messaging relays",
|
|
||||||
IconName::Relay,
|
|
||||||
Box::new(Command::ShowMessaging),
|
|
||||||
)
|
|
||||||
.separator()
|
|
||||||
.menu_with_icon(
|
|
||||||
"Reload",
|
|
||||||
IconName::Refresh,
|
|
||||||
Box::new(Command::RefreshMessagingRelays),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for Workspace {
|
impl Render for Workspace {
|
||||||
@@ -685,9 +511,6 @@ impl Render for Workspace {
|
|||||||
let modal_layer = Root::render_modal_layer(window, cx);
|
let modal_layer = Root::render_modal_layer(window, cx);
|
||||||
let notification_layer = Root::render_notification_layer(window, cx);
|
let notification_layer = Root::render_notification_layer(window, cx);
|
||||||
|
|
||||||
// Render the title bar chrome
|
|
||||||
self.title_bar_chrome.set_trailing(Self::titlebar_right);
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.id("workspace")
|
.id("workspace")
|
||||||
.on_action(cx.listener(Self::on_command))
|
.on_action(cx.listener(Self::on_command))
|
||||||
|
|||||||
+363
-315
@@ -7,10 +7,10 @@ use common::TimestampExt;
|
|||||||
use community::{ChannelId, Community, CommunityEvent, CommunityRegistry};
|
use community::{ChannelId, Community, CommunityEvent, CommunityRegistry};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Context, ElementId, Entity, EventEmitter, FocusHandle, Focusable,
|
AnyElement, App, Context, Div, ElementId, Entity, EventEmitter, FocusHandle, Focusable,
|
||||||
InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, ScrollHandle, SharedString,
|
InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, ScrollHandle, SharedString,
|
||||||
StatefulInteractiveElement, Styled, StyledImage, Subscription, UniformListScrollHandle,
|
Stateful, StatefulInteractiveElement, Styled, StyledImage, Subscription,
|
||||||
WeakEntity, Window, div, img, px, retain_all, uniform_list,
|
UniformListScrollHandle, WeakEntity, Window, div, img, px, retain_all, uniform_list,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
@@ -32,8 +32,8 @@ use ui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::Command;
|
use crate::Command;
|
||||||
|
use crate::dialogs::import;
|
||||||
|
|
||||||
mod onboarding;
|
|
||||||
mod tab;
|
mod tab;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
||||||
@@ -51,11 +51,9 @@ pub struct Sidebar {
|
|||||||
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>>,
|
||||||
/// Expanded state of the community's sections
|
|
||||||
channels_open: bool,
|
channels_open: bool,
|
||||||
admins_open: bool,
|
admins_open: bool,
|
||||||
members_open: bool,
|
members_open: bool,
|
||||||
/// Whether there are new chat requests
|
|
||||||
new_requests: bool,
|
new_requests: bool,
|
||||||
_subscriptions: SmallVec<[Subscription; 4]>,
|
_subscriptions: SmallVec<[Subscription; 4]>,
|
||||||
}
|
}
|
||||||
@@ -64,7 +62,6 @@ impl Sidebar {
|
|||||||
pub fn new(window: &mut Window, dock: WeakEntity<DockArea>, 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 mut subscriptions = smallvec![];
|
let mut subscriptions = smallvec![];
|
||||||
|
|
||||||
@@ -82,16 +79,11 @@ impl Sidebar {
|
|||||||
window,
|
window,
|
||||||
|_this, _communities, event, window, cx| {
|
|_this, _communities, event, window, cx| {
|
||||||
if let CommunityEvent::Error(error) = event {
|
if let CommunityEvent::Error(error) = event {
|
||||||
window
|
window.push_notification(Notification::error(error.clone()), cx);
|
||||||
.push_notification(Notification::error(error.clone()).autohide(false), cx);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
subscriptions.push(cx.observe(&nostr, |_this, _nostr, cx| cx.notify()));
|
|
||||||
|
|
||||||
subscriptions.push(cx.observe(&communities, |_this, _communities, cx| cx.notify()));
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
scroll_handles: [
|
scroll_handles: [
|
||||||
@@ -115,7 +107,6 @@ impl Sidebar {
|
|||||||
if self.active_tab == tab {
|
if self.active_tab == tab {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.active_tab = tab;
|
self.active_tab = tab;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
@@ -164,11 +155,11 @@ impl Sidebar {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_user(&self, window: &mut Window, cx: &mut Context<Self>) -> AnyElement {
|
fn render_user(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let current_user = nostr.read(cx).current_user();
|
let current_user = nostr.read(cx).current_user();
|
||||||
|
|
||||||
let mut row = h_flex()
|
h_flex()
|
||||||
.id("sidebar-user")
|
.id("sidebar-user")
|
||||||
.w_full()
|
.w_full()
|
||||||
.h(TABBAR_HEIGHT)
|
.h(TABBAR_HEIGHT)
|
||||||
@@ -178,16 +169,15 @@ impl Sidebar {
|
|||||||
.px_2()
|
.px_2()
|
||||||
.when(cfg!(target_os = "macos"), |this| {
|
.when(cfg!(target_os = "macos"), |this| {
|
||||||
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
this.pl(px(TRAFFIC_LIGHT_PADDING))
|
||||||
});
|
})
|
||||||
|
.when_some(current_user.as_ref(), |this, public_key| {
|
||||||
if let Some(public_key) = current_user.as_ref() {
|
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let profile = persons.read(cx).get(public_key, cx);
|
let profile = persons.read(cx).get(public_key, cx);
|
||||||
let avatar = profile.avatar();
|
let avatar = profile.avatar();
|
||||||
let avatar_seed = profile.avatar_seed();
|
let avatar_seed = profile.avatar_seed();
|
||||||
let name = profile.name();
|
let name = profile.name();
|
||||||
|
|
||||||
row = row.child(
|
this.child(
|
||||||
Button::new("current-user")
|
Button::new("current-user")
|
||||||
.child(
|
.child(
|
||||||
Avatar::new(avatar.clone())
|
Avatar::new(avatar.clone())
|
||||||
@@ -217,7 +207,11 @@ impl Sidebar {
|
|||||||
.child(name.clone())
|
.child(name.clone())
|
||||||
}))
|
}))
|
||||||
.separator()
|
.separator()
|
||||||
.menu_with_icon("Inbox", IconName::Inbox, Box::new(Command::ShowInbox))
|
.menu_with_icon(
|
||||||
|
"Inbox",
|
||||||
|
IconName::Inbox,
|
||||||
|
Box::new(Command::ShowInbox),
|
||||||
|
)
|
||||||
.menu_with_icon(
|
.menu_with_icon(
|
||||||
"Search",
|
"Search",
|
||||||
IconName::Search,
|
IconName::Search,
|
||||||
@@ -238,7 +232,11 @@ impl Sidebar {
|
|||||||
IconName::UserKey,
|
IconName::UserKey,
|
||||||
Box::new(Command::ShowBackup),
|
Box::new(Command::ShowBackup),
|
||||||
)
|
)
|
||||||
.menu_with_icon("Themes", IconName::Sun, Box::new(Command::ToggleTheme))
|
.menu_with_icon(
|
||||||
|
"Themes",
|
||||||
|
IconName::Sun,
|
||||||
|
Box::new(Command::ToggleTheme),
|
||||||
|
)
|
||||||
.when(AutoUpdater::is_available(cx), |this| {
|
.when(AutoUpdater::is_available(cx), |this| {
|
||||||
this.separator().menu_with_icon(
|
this.separator().menu_with_icon(
|
||||||
"Check for Updates",
|
"Check for Updates",
|
||||||
@@ -252,11 +250,14 @@ impl Sidebar {
|
|||||||
Box::new(Command::ShowSettings),
|
Box::new(Command::ShowSettings),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
);
|
)
|
||||||
}
|
})
|
||||||
|
.child(div().flex_1())
|
||||||
if self.community.is_some() {
|
.when_some(AutoUpdater::try_global(cx), |this, updater| {
|
||||||
row = row.child(div().flex_1()).child(
|
this.child(self.render_updater(updater, cx))
|
||||||
|
})
|
||||||
|
.when(self.community.is_some(), |this| {
|
||||||
|
this.child(
|
||||||
Button::new("sidebar-back")
|
Button::new("sidebar-back")
|
||||||
.icon(IconName::ArrowLeft)
|
.icon(IconName::ArrowLeft)
|
||||||
.tooltip("Back")
|
.tooltip("Back")
|
||||||
@@ -265,300 +266,42 @@ impl Sidebar {
|
|||||||
.on_click(cx.listener(|this, _event, _window, cx| {
|
.on_click(cx.listener(|this, _event, _window, cx| {
|
||||||
this.reset_community(cx);
|
this.reset_community(cx);
|
||||||
})),
|
})),
|
||||||
);
|
)
|
||||||
}
|
|
||||||
|
|
||||||
title_bar_drag_handlers(row, window, cx).into_any_element()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recent_communities(cx: &App) -> Vec<Entity<Community>> {
|
|
||||||
const LIMIT: usize = 3;
|
|
||||||
|
|
||||||
let registry = CommunityRegistry::global(cx);
|
|
||||||
let communities = registry.read(cx).communities();
|
|
||||||
let recent = AppSettings::get_recent_communities(cx);
|
|
||||||
|
|
||||||
let mut rows: Vec<Entity<Community>> = recent
|
|
||||||
.iter()
|
|
||||||
.filter_map(|id| {
|
|
||||||
communities
|
|
||||||
.iter()
|
|
||||||
.find(|community| community.read(cx).id().to_hex() == *id)
|
|
||||||
})
|
})
|
||||||
.take(LIMIT)
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if rows.is_empty() {
|
|
||||||
rows = communities.iter().take(LIMIT).cloned().collect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows
|
fn render_updater(&self, updater: Entity<AutoUpdater>, cx: &mut App) -> AnyElement {
|
||||||
}
|
let status = updater.read(cx).status();
|
||||||
|
let staged = updater.read(cx).staged();
|
||||||
|
|
||||||
fn rows_for(tab: SidebarTab, cx: &App) -> Vec<SidebarRow> {
|
h_flex()
|
||||||
match tab {
|
|
||||||
SidebarTab::Recents => {
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
let rooms = chat.read(cx).rooms(&RoomKind::Ongoing, cx);
|
|
||||||
let registry = CommunityRegistry::global(cx);
|
|
||||||
let community_count = registry.read(cx).communities().len();
|
|
||||||
let communities = recent_communities(cx);
|
|
||||||
|
|
||||||
if communities.is_empty() && rooms.is_empty() {
|
|
||||||
return vec![SidebarRow::Hint {
|
|
||||||
text: "Nothing recent yet".into(),
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rows = Vec::new();
|
|
||||||
|
|
||||||
if !communities.is_empty() {
|
|
||||||
rows.push(SidebarRow::Section {
|
|
||||||
label: "Communities".into(),
|
|
||||||
count: community_count,
|
|
||||||
});
|
|
||||||
rows.extend(
|
|
||||||
communities
|
|
||||||
.into_iter()
|
|
||||||
.map(|community| SidebarRow::Community { community }),
|
|
||||||
);
|
|
||||||
rows.push(SidebarRow::Action {
|
|
||||||
label: "Show all communities".into(),
|
|
||||||
tab: SidebarTab::Communities,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rooms.is_empty() {
|
|
||||||
rows.push(SidebarRow::Section {
|
|
||||||
label: "Chats".into(),
|
|
||||||
count: rooms.len(),
|
|
||||||
});
|
|
||||||
rows.extend(
|
|
||||||
rooms
|
|
||||||
.into_iter()
|
|
||||||
.take(5)
|
|
||||||
.map(|room| SidebarRow::Room { room }),
|
|
||||||
);
|
|
||||||
rows.push(SidebarRow::Action {
|
|
||||||
label: "Show all chats".into(),
|
|
||||||
tab: SidebarTab::Chats,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
rows
|
|
||||||
}
|
|
||||||
SidebarTab::Chats => {
|
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
let chat = chat.read(cx);
|
|
||||||
let messages = chat.rooms(&RoomKind::Ongoing, cx);
|
|
||||||
|
|
||||||
let mut rows = vec![SidebarRow::Section {
|
|
||||||
label: "Chats".into(),
|
|
||||||
count: messages.len(),
|
|
||||||
}];
|
|
||||||
|
|
||||||
if messages.is_empty() {
|
|
||||||
rows.push(SidebarRow::Hint {
|
|
||||||
text: "No conversations yet".into(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
rows.extend(messages.into_iter().map(|room| SidebarRow::Room { room }));
|
|
||||||
}
|
|
||||||
|
|
||||||
rows
|
|
||||||
}
|
|
||||||
SidebarTab::Communities => {
|
|
||||||
let registry = CommunityRegistry::global(cx);
|
|
||||||
let communities = registry.read(cx).communities();
|
|
||||||
|
|
||||||
let mut rows = vec![SidebarRow::Section {
|
|
||||||
label: "Communities".into(),
|
|
||||||
count: communities.len(),
|
|
||||||
}];
|
|
||||||
|
|
||||||
if communities.is_empty() {
|
|
||||||
rows.push(SidebarRow::Hint {
|
|
||||||
text: "No communities yet".into(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
rows.extend(
|
|
||||||
communities
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.map(|community| SidebarRow::Community { community }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_rows(range: Range<usize>, rows: &[SidebarRow], cx: &Context<Sidebar>) -> Vec<AnyElement> {
|
|
||||||
rows.get(range.clone())
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(offset, row)| {
|
|
||||||
let index = range.start + offset;
|
|
||||||
|
|
||||||
match row {
|
|
||||||
SidebarRow::Section { label, count } => TreeRow::new(
|
|
||||||
ElementId::NamedInteger("tree-row".into(), index as u64),
|
|
||||||
TreeRowKind::Section,
|
|
||||||
label.clone(),
|
|
||||||
)
|
|
||||||
.count(*count)
|
|
||||||
.into_any_element(),
|
|
||||||
SidebarRow::Room { room } => {
|
|
||||||
let name = room.read(cx).display_name(cx);
|
|
||||||
let picture = room.read(cx).display_image(cx);
|
|
||||||
let seed = room.read(cx).display_image_seed(cx);
|
|
||||||
let created_at = room.read(cx).created_at.to_ago();
|
|
||||||
let room_clone = room.clone();
|
|
||||||
|
|
||||||
let handler = cx.listener(move |this, _event, window, cx| {
|
|
||||||
this.open_room(room_clone.clone(), window, cx);
|
|
||||||
});
|
|
||||||
|
|
||||||
TreeRow::new(
|
|
||||||
ElementId::NamedInteger("tree-row".into(), index as u64),
|
|
||||||
TreeRowKind::Room,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
.avatar(seed)
|
|
||||||
.picture(picture)
|
|
||||||
.created_at(created_at)
|
|
||||||
.on_click(handler)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
SidebarRow::Community { community } => {
|
|
||||||
let name = community.read(cx).name();
|
|
||||||
let seed = community.read(cx).id().to_hex();
|
|
||||||
let picture = community.read(cx).icon();
|
|
||||||
let community = community.clone();
|
|
||||||
|
|
||||||
TreeRow::new(
|
|
||||||
ElementId::NamedInteger("tree-row".into(), index as u64),
|
|
||||||
TreeRowKind::Community,
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
.avatar(seed)
|
|
||||||
.picture(picture)
|
|
||||||
.on_click(cx.listener(move |this, _event, window, cx| {
|
|
||||||
this.open_community(community.clone(), window, cx);
|
|
||||||
}))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
SidebarRow::Action { label, tab } => {
|
|
||||||
let tab = *tab;
|
|
||||||
|
|
||||||
TreeRow::new(
|
|
||||||
ElementId::NamedInteger("tree-row".into(), index as u64),
|
|
||||||
TreeRowKind::Action,
|
|
||||||
label.clone(),
|
|
||||||
)
|
|
||||||
.icon(IconName::ArrowRight)
|
|
||||||
.on_click(cx.listener(move |this, _event, _window, cx| {
|
|
||||||
this.select_tab(tab, cx);
|
|
||||||
}))
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
SidebarRow::Hint { text } => TreeRow::new(
|
|
||||||
ElementId::NamedInteger("tree-row".into(), index as u64),
|
|
||||||
TreeRowKind::Hint,
|
|
||||||
text.clone(),
|
|
||||||
)
|
|
||||||
.into_any_element(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Panel for Sidebar {
|
|
||||||
fn panel_id(&self) -> SharedString {
|
|
||||||
"Sidebar".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn title(&self, _cx: &App) -> AnyElement {
|
|
||||||
SharedString::from("Sidebar").into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn closable(&self, _cx: &App) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn zoomable(&self, _cx: &App) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventEmitter<PanelEvent> for Sidebar {}
|
|
||||||
|
|
||||||
impl Focusable for Sidebar {
|
|
||||||
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
|
||||||
self.focus_handle.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for Sidebar {
|
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
||||||
let (logged_in, ready) = {
|
|
||||||
let nostr = NostrRegistry::global(cx);
|
|
||||||
(
|
|
||||||
nostr.read(cx).current_user().is_some(),
|
|
||||||
nostr.read(cx).ready(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if !logged_in {
|
|
||||||
if !ready {
|
|
||||||
return v_flex()
|
|
||||||
.size_full()
|
|
||||||
.bg(cx.theme().surface_background)
|
|
||||||
.border_r_1()
|
|
||||||
.border_color(cx.theme().border_variant)
|
|
||||||
.into_any_element();
|
|
||||||
}
|
|
||||||
return onboarding::render(window, cx).into_any_element();
|
|
||||||
}
|
|
||||||
|
|
||||||
let community = self
|
|
||||||
.community
|
|
||||||
.clone()
|
|
||||||
.and_then(|community| community.upgrade());
|
|
||||||
|
|
||||||
if community.is_none() {
|
|
||||||
self.community = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
v_flex()
|
|
||||||
.image_cache(retain_all("sidebar"))
|
|
||||||
.size_full()
|
|
||||||
.relative()
|
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.bg(cx.theme().surface_background)
|
.text_xs()
|
||||||
.border_r_1()
|
.child(status)
|
||||||
.border_color(cx.theme().border_variant)
|
.when(staged, |this| {
|
||||||
.child(self.render_user(window, cx))
|
this.child(
|
||||||
.map(|this| match community {
|
Button::new("restart-to-update")
|
||||||
Some(community) => this.child(self.render_community(community, cx)),
|
.icon(IconName::ArrowDownCircle)
|
||||||
None => this.child(self.render_tabs(cx)),
|
.tooltip("Quit and relaunch into the installed update")
|
||||||
|
.small()
|
||||||
|
.ghost()
|
||||||
|
.on_click(move |_, _window, cx| {
|
||||||
|
updater.update(cx, |this, cx| {
|
||||||
|
this.restart(cx);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Sidebar {
|
|
||||||
fn render_tabs(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
fn render_tabs(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
||||||
let chat = ChatRegistry::global(cx);
|
let chat = ChatRegistry::global(cx);
|
||||||
let loading = chat.read(cx).loading();
|
let loading = chat.read(cx).loading();
|
||||||
|
|
||||||
let sidebar = cx.entity().downgrade();
|
let sidebar = cx.entity().downgrade();
|
||||||
let active_tab = self.active_tab;
|
let active_tab = self.active_tab;
|
||||||
let rows = Rc::new(rows_for(active_tab, cx));
|
let rows = Rc::new(self.rows_for(active_tab, cx));
|
||||||
let scroll_handle = &self.scroll_handles[active_tab.index()];
|
let scroll_handle = &self.scroll_handles[active_tab.index()];
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
@@ -653,8 +396,8 @@ impl Sidebar {
|
|||||||
uniform_list(
|
uniform_list(
|
||||||
active_tab.list_id(),
|
active_tab.list_id(),
|
||||||
rows.len(),
|
rows.len(),
|
||||||
cx.processor(move |_this, range, _window, cx| {
|
cx.processor(move |this, range, _window, cx| {
|
||||||
render_rows(range, rows.as_slice(), cx)
|
this.render_rows(range, rows.as_slice(), cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.track_scroll(scroll_handle)
|
.track_scroll(scroll_handle)
|
||||||
@@ -742,7 +485,7 @@ impl Sidebar {
|
|||||||
.pb_2()
|
.pb_2()
|
||||||
.track_scroll(&self.community_scroll)
|
.track_scroll(&self.community_scroll)
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.child(section_row(
|
.child(self.section_row(
|
||||||
"channels",
|
"channels",
|
||||||
"Channels",
|
"Channels",
|
||||||
channels.len(),
|
channels.len(),
|
||||||
@@ -752,10 +495,10 @@ impl Sidebar {
|
|||||||
))
|
))
|
||||||
.when(self.channels_open, |this| {
|
.when(self.channels_open, |this| {
|
||||||
this.children(channels.into_iter().map(|(id, name, private)| {
|
this.children(channels.into_iter().map(|(id, name, private)| {
|
||||||
channel_row(id, name, private, active == Some(id), &community, cx)
|
self.channel_row(id, name, private, active == Some(id), &community, cx)
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.child(section_row(
|
.child(self.section_row(
|
||||||
"admins",
|
"admins",
|
||||||
"Admins",
|
"Admins",
|
||||||
admins.len(),
|
admins.len(),
|
||||||
@@ -767,10 +510,10 @@ impl Sidebar {
|
|||||||
this.children(
|
this.children(
|
||||||
admins
|
admins
|
||||||
.iter()
|
.iter()
|
||||||
.map(|public_key| member_row("community-admin", *public_key, cx)),
|
.map(|public_key| self.member_row("community-admin", *public_key, cx)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.child(section_row(
|
.child(self.section_row(
|
||||||
"members",
|
"members",
|
||||||
"Members",
|
"Members",
|
||||||
members.len(),
|
members.len(),
|
||||||
@@ -782,7 +525,7 @@ impl Sidebar {
|
|||||||
this.children(
|
this.children(
|
||||||
members
|
members
|
||||||
.iter()
|
.iter()
|
||||||
.map(|public_key| member_row("member", *public_key, cx)),
|
.map(|public_key| self.member_row("member", *public_key, cx)),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -806,9 +549,219 @@ impl Sidebar {
|
|||||||
.child(Scrollbar::vertical(&self.community_scroll))
|
.child(Scrollbar::vertical(&self.community_scroll))
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recent_communities(&self, cx: &App) -> Vec<Entity<Community>> {
|
||||||
|
const LIMIT: usize = 3;
|
||||||
|
|
||||||
|
let registry = CommunityRegistry::global(cx);
|
||||||
|
let communities = registry.read(cx).communities();
|
||||||
|
let recent = AppSettings::get_recent_communities(cx);
|
||||||
|
|
||||||
|
let mut rows: Vec<Entity<Community>> = recent
|
||||||
|
.iter()
|
||||||
|
.filter_map(|id| {
|
||||||
|
communities
|
||||||
|
.iter()
|
||||||
|
.find(|community| community.read(cx).id().to_hex() == *id)
|
||||||
|
})
|
||||||
|
.take(LIMIT)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if rows.is_empty() {
|
||||||
|
rows = communities.iter().take(LIMIT).cloned().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rows_for(&self, tab: SidebarTab, cx: &App) -> Vec<SidebarRow> {
|
||||||
|
match tab {
|
||||||
|
SidebarTab::Recents => {
|
||||||
|
let chat = ChatRegistry::global(cx);
|
||||||
|
let rooms = chat.read(cx).rooms(&RoomKind::Ongoing, cx);
|
||||||
|
let registry = CommunityRegistry::global(cx);
|
||||||
|
let community_count = registry.read(cx).communities().len();
|
||||||
|
let communities = self.recent_communities(cx);
|
||||||
|
|
||||||
|
if communities.is_empty() && rooms.is_empty() {
|
||||||
|
return vec![SidebarRow::Hint {
|
||||||
|
text: "Nothing recent yet".into(),
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut rows = Vec::new();
|
||||||
|
|
||||||
|
if !communities.is_empty() {
|
||||||
|
rows.push(SidebarRow::Section {
|
||||||
|
label: "Communities".into(),
|
||||||
|
count: community_count,
|
||||||
|
});
|
||||||
|
rows.extend(
|
||||||
|
communities
|
||||||
|
.into_iter()
|
||||||
|
.map(|community| SidebarRow::Community { community }),
|
||||||
|
);
|
||||||
|
rows.push(SidebarRow::Action {
|
||||||
|
label: "Show all communities".into(),
|
||||||
|
tab: SidebarTab::Communities,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rooms.is_empty() {
|
||||||
|
rows.push(SidebarRow::Section {
|
||||||
|
label: "Chats".into(),
|
||||||
|
count: rooms.len(),
|
||||||
|
});
|
||||||
|
rows.extend(
|
||||||
|
rooms
|
||||||
|
.into_iter()
|
||||||
|
.take(5)
|
||||||
|
.map(|room| SidebarRow::Room { room }),
|
||||||
|
);
|
||||||
|
rows.push(SidebarRow::Action {
|
||||||
|
label: "Show all chats".into(),
|
||||||
|
tab: SidebarTab::Chats,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
SidebarTab::Chats => {
|
||||||
|
let chat = ChatRegistry::global(cx);
|
||||||
|
let chat = chat.read(cx);
|
||||||
|
let messages = chat.rooms(&RoomKind::Ongoing, cx);
|
||||||
|
|
||||||
|
let mut rows = vec![SidebarRow::Section {
|
||||||
|
label: "Chats".into(),
|
||||||
|
count: messages.len(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
if messages.is_empty() {
|
||||||
|
rows.push(SidebarRow::Hint {
|
||||||
|
text: "No conversations yet".into(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rows.extend(messages.into_iter().map(|room| SidebarRow::Room { room }));
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
SidebarTab::Communities => {
|
||||||
|
let registry = CommunityRegistry::global(cx);
|
||||||
|
let communities = registry.read(cx).communities();
|
||||||
|
|
||||||
|
let mut rows = vec![SidebarRow::Section {
|
||||||
|
label: "Communities".into(),
|
||||||
|
count: communities.len(),
|
||||||
|
}];
|
||||||
|
|
||||||
|
if communities.is_empty() {
|
||||||
|
rows.push(SidebarRow::Hint {
|
||||||
|
text: "No communities yet".into(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
rows.extend(
|
||||||
|
communities
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|community| SidebarRow::Community { community }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_rows(
|
||||||
|
&self,
|
||||||
|
range: Range<usize>,
|
||||||
|
rows: &[SidebarRow],
|
||||||
|
cx: &Context<Sidebar>,
|
||||||
|
) -> Vec<AnyElement> {
|
||||||
|
rows.get(range.clone())
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(offset, row)| {
|
||||||
|
let index = range.start + offset;
|
||||||
|
|
||||||
|
match row {
|
||||||
|
SidebarRow::Section { label, count } => TreeRow::new(
|
||||||
|
ElementId::NamedInteger("tree-row".into(), index as u64),
|
||||||
|
TreeRowKind::Section,
|
||||||
|
label.clone(),
|
||||||
|
)
|
||||||
|
.count(*count)
|
||||||
|
.into_any_element(),
|
||||||
|
SidebarRow::Room { room } => {
|
||||||
|
let name = room.read(cx).display_name(cx);
|
||||||
|
let picture = room.read(cx).display_image(cx);
|
||||||
|
let seed = room.read(cx).display_image_seed(cx);
|
||||||
|
let created_at = room.read(cx).created_at.to_ago();
|
||||||
|
let room_clone = room.clone();
|
||||||
|
|
||||||
|
let handler = cx.listener(move |this, _event, window, cx| {
|
||||||
|
this.open_room(room_clone.clone(), window, cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
TreeRow::new(
|
||||||
|
ElementId::NamedInteger("tree-row".into(), index as u64),
|
||||||
|
TreeRowKind::Room,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.avatar(seed)
|
||||||
|
.picture(picture)
|
||||||
|
.created_at(created_at)
|
||||||
|
.on_click(handler)
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
SidebarRow::Community { community } => {
|
||||||
|
let name = community.read(cx).name();
|
||||||
|
let seed = community.read(cx).id().to_hex();
|
||||||
|
let picture = community.read(cx).icon();
|
||||||
|
let community = community.clone();
|
||||||
|
|
||||||
|
TreeRow::new(
|
||||||
|
ElementId::NamedInteger("tree-row".into(), index as u64),
|
||||||
|
TreeRowKind::Community,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
.avatar(seed)
|
||||||
|
.picture(picture)
|
||||||
|
.on_click(cx.listener(move |this, _event, window, cx| {
|
||||||
|
this.open_community(community.clone(), window, cx);
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
SidebarRow::Action { label, tab } => {
|
||||||
|
let tab = *tab;
|
||||||
|
|
||||||
|
TreeRow::new(
|
||||||
|
ElementId::NamedInteger("tree-row".into(), index as u64),
|
||||||
|
TreeRowKind::Action,
|
||||||
|
label.clone(),
|
||||||
|
)
|
||||||
|
.icon(IconName::ArrowRight)
|
||||||
|
.on_click(cx.listener(move |this, _event, _window, cx| {
|
||||||
|
this.select_tab(tab, cx);
|
||||||
|
}))
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
SidebarRow::Hint { text } => TreeRow::new(
|
||||||
|
ElementId::NamedInteger("tree-row".into(), index as u64),
|
||||||
|
TreeRowKind::Hint,
|
||||||
|
text.clone(),
|
||||||
|
)
|
||||||
|
.into_any_element(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn section_row(
|
fn section_row(
|
||||||
|
&self,
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
count: usize,
|
count: usize,
|
||||||
@@ -835,6 +788,7 @@ fn section_row(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn channel_row(
|
fn channel_row(
|
||||||
|
&self,
|
||||||
id: ChannelId,
|
id: ChannelId,
|
||||||
name: String,
|
name: String,
|
||||||
private: bool,
|
private: bool,
|
||||||
@@ -870,7 +824,7 @@ fn channel_row(
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn member_row(prefix: &str, public_key: PublicKey, cx: &App) -> AnyElement {
|
fn member_row(&self, prefix: &str, public_key: PublicKey, cx: &App) -> AnyElement {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let person = persons.read(cx).get(&public_key, cx);
|
let person = persons.read(cx).get(&public_key, cx);
|
||||||
|
|
||||||
@@ -890,3 +844,97 @@ fn member_row(prefix: &str, public_key: PublicKey, cx: &App) -> AnyElement {
|
|||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Panel for Sidebar {
|
||||||
|
fn panel_id(&self) -> SharedString {
|
||||||
|
"Sidebar".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(&self, _cx: &App) -> AnyElement {
|
||||||
|
SharedString::from("Sidebar").into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closable(&self, _cx: &App) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zoomable(&self, _cx: &App) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<PanelEvent> for Sidebar {}
|
||||||
|
|
||||||
|
impl Focusable for Sidebar {
|
||||||
|
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
||||||
|
self.focus_handle.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Sidebar {
|
||||||
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let logged_in = { nostr.read(cx).current_user().is_some() };
|
||||||
|
|
||||||
|
let community = self
|
||||||
|
.community
|
||||||
|
.clone()
|
||||||
|
.and_then(|community| community.upgrade());
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.image_cache(retain_all("sidebar"))
|
||||||
|
.size_full()
|
||||||
|
.relative()
|
||||||
|
.gap_2()
|
||||||
|
.bg(cx.theme().surface_background)
|
||||||
|
.border_r_1()
|
||||||
|
.border_color(cx.theme().border_variant)
|
||||||
|
.when(!logged_in, |this| {
|
||||||
|
this.relative()
|
||||||
|
.child(title_bar_drag_handlers(
|
||||||
|
div()
|
||||||
|
.id("onboarding-drag")
|
||||||
|
.absolute()
|
||||||
|
.top_0()
|
||||||
|
.left_0()
|
||||||
|
.h(TABBAR_HEIGHT)
|
||||||
|
.w_full(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.size_full()
|
||||||
|
.justify_end()
|
||||||
|
.gap_4()
|
||||||
|
.p_4()
|
||||||
|
.child(
|
||||||
|
v_flex().child(div().font_semibold().child("Coop")).child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().text_muted)
|
||||||
|
.child("TODO"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("import-identity")
|
||||||
|
.label("Import identity")
|
||||||
|
.primary()
|
||||||
|
.font_semibold()
|
||||||
|
.h_8()
|
||||||
|
.w_full()
|
||||||
|
.on_click(|_, window, cx| {
|
||||||
|
import::open(window, cx);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.child(title_bar_drag_handlers(self.render_user(cx), window, cx))
|
||||||
|
.map(|this| match community {
|
||||||
|
Some(community) => this.child(self.render_community(community, cx)),
|
||||||
|
None => this.child(self.render_tabs(cx)),
|
||||||
|
})
|
||||||
|
.into_any_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
use gpui::{App, InteractiveElement, IntoElement, ParentElement, Styled, Window, div, svg};
|
|
||||||
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
|
||||||
use ui::button::{Button, ButtonVariants};
|
|
||||||
use ui::{StyledExt, h_flex, title_bar_drag_handlers, v_flex};
|
|
||||||
|
|
||||||
use crate::dialogs::import;
|
|
||||||
|
|
||||||
const TITLE: &str = "Welcome to Coop!";
|
|
||||||
const DESCRIPTION: &str = "Chat Freely, Stay Private on Nostr.";
|
|
||||||
|
|
||||||
pub(super) fn render(window: &mut Window, cx: &mut App) -> impl IntoElement {
|
|
||||||
v_flex()
|
|
||||||
.size_full()
|
|
||||||
.relative()
|
|
||||||
.bg(cx.theme().surface_background)
|
|
||||||
.child(title_bar_drag_handlers(
|
|
||||||
div()
|
|
||||||
.id("onboarding-drag")
|
|
||||||
.absolute()
|
|
||||||
.top_0()
|
|
||||||
.left_0()
|
|
||||||
.h(TABBAR_HEIGHT)
|
|
||||||
.w_full(),
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
))
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.size_full()
|
|
||||||
.justify_end()
|
|
||||||
.gap_4()
|
|
||||||
.p_4()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_2()
|
|
||||||
.child(
|
|
||||||
svg()
|
|
||||||
.path("brand/coop.svg")
|
|
||||||
.size_8()
|
|
||||||
.text_color(cx.theme().icon_muted),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_flex().child(div().font_semibold().child(TITLE)).child(
|
|
||||||
div()
|
|
||||||
.text_xs()
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.child(DESCRIPTION),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.gap_2()
|
|
||||||
.w_full()
|
|
||||||
.child(
|
|
||||||
Button::new("join-now")
|
|
||||||
.label("Join now")
|
|
||||||
.primary()
|
|
||||||
.font_semibold()
|
|
||||||
.h_8()
|
|
||||||
.w_full(),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new("import-identity")
|
|
||||||
.label("Import identity")
|
|
||||||
.secondary()
|
|
||||||
.font_semibold()
|
|
||||||
.h_8()
|
|
||||||
.w_full()
|
|
||||||
.on_click(|_event, window, cx| import::open(window, cx)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user