update sidebar
This commit is contained in:
@@ -46,6 +46,8 @@ setting_accessors! {
|
||||
pub nip4e: bool,
|
||||
pub trusted_relays: Vec<String>,
|
||||
pub file_server: Url,
|
||||
pub pinned_rooms: Vec<u64>,
|
||||
pub expanded_sections: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Signer kind
|
||||
@@ -130,6 +132,14 @@ pub struct Settings {
|
||||
|
||||
/// Server for blossom media attachments
|
||||
pub file_server: Url,
|
||||
|
||||
/// Pinned sidebar room ids, in pin order
|
||||
#[serde(default)]
|
||||
pub pinned_rooms: Vec<u64>,
|
||||
|
||||
/// Expanded sidebar tree sections; `None` means the default sections
|
||||
#[serde(default)]
|
||||
pub expanded_sections: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
@@ -142,6 +152,8 @@ impl Default for Settings {
|
||||
nip4e: false,
|
||||
trusted_relays: vec![],
|
||||
file_server: Url::parse(DEFAULT_FILE_SERVER).unwrap(),
|
||||
pinned_rooms: vec![],
|
||||
expanded_sections: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +183,13 @@ impl AppSettings {
|
||||
cx.global::<GlobalAppSettings>().0.clone()
|
||||
}
|
||||
|
||||
/// The underlying settings entity, which notifies whenever any field changes.
|
||||
/// Settings load asynchronously, so observers can watch it to pick up values
|
||||
/// that arrive after construction.
|
||||
pub fn entity(&self) -> &Entity<Settings> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Set the global settings instance
|
||||
fn set_global(state: Entity<Self>, cx: &mut App) {
|
||||
cx.set_global(GlobalAppSettings(state));
|
||||
|
||||
@@ -12,6 +12,7 @@ use gpui::{
|
||||
UniformListScrollHandle, Window, div, px, retain_all, uniform_list,
|
||||
};
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use state::NostrRegistry;
|
||||
use theme::{ActiveTheme, TABBAR_HEIGHT};
|
||||
@@ -49,17 +50,17 @@ pub struct Sidebar {
|
||||
pinned_rooms: Vec<u64>,
|
||||
|
||||
/// Event subscriptions
|
||||
_subscriptions: SmallVec<[Subscription; 1]>,
|
||||
_subscriptions: SmallVec<[Subscription; 2]>,
|
||||
}
|
||||
|
||||
impl Sidebar {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let settings = AppSettings::global(cx).read(cx).entity().clone();
|
||||
let chat = ChatRegistry::global(cx);
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
// Subscribe for registry new events
|
||||
cx.subscribe_in(&chat, window, move |this, _s, event, _window, cx| {
|
||||
if event == &ChatEvent::Ping {
|
||||
this.new_requests = true;
|
||||
@@ -68,12 +69,16 @@ impl Sidebar {
|
||||
}),
|
||||
);
|
||||
|
||||
subscriptions.push(cx.observe(&settings, move |this, _settings, cx| {
|
||||
this.restore_state(cx);
|
||||
}));
|
||||
|
||||
Self {
|
||||
focus_handle: cx.focus_handle(),
|
||||
scroll_handle: UniformListScrollHandle::new(),
|
||||
new_requests: false,
|
||||
expanded: BTreeSet::from([TreeSection::Community, TreeSection::Messages]),
|
||||
pinned_rooms: Vec::new(),
|
||||
expanded: load_expanded(cx),
|
||||
pinned_rooms: AppSettings::get_pinned_rooms(cx),
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
}
|
||||
@@ -87,6 +92,7 @@ impl Sidebar {
|
||||
self.new_requests = false;
|
||||
}
|
||||
|
||||
self.save_expanded(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -94,16 +100,43 @@ impl Sidebar {
|
||||
self.expanded.contains(§ion)
|
||||
}
|
||||
|
||||
fn restore_state(&mut self, cx: &mut Context<Self>) {
|
||||
let pinned_rooms = AppSettings::get_pinned_rooms(cx);
|
||||
let expanded = load_expanded(cx);
|
||||
|
||||
if self.pinned_rooms == pinned_rooms && self.expanded == expanded {
|
||||
return;
|
||||
}
|
||||
|
||||
self.pinned_rooms = pinned_rooms;
|
||||
self.expanded = expanded;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn save_expanded(&self, cx: &mut App) {
|
||||
let keys = self
|
||||
.expanded
|
||||
.iter()
|
||||
.map(|section| section.key().to_string())
|
||||
.collect();
|
||||
AppSettings::update_expanded_sections(Some(keys), cx);
|
||||
}
|
||||
|
||||
fn pin_room(&mut self, room_id: u64, cx: &mut Context<Self>) {
|
||||
if !self.pinned_rooms.contains(&room_id) {
|
||||
self.pinned_rooms.push(room_id);
|
||||
}
|
||||
self.expanded.insert(TreeSection::Pins);
|
||||
|
||||
AppSettings::update_pinned_rooms(self.pinned_rooms.clone(), cx);
|
||||
self.save_expanded(cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn unpin_room(&mut self, room_id: u64, cx: &mut Context<Self>) {
|
||||
self.pinned_rooms.retain(|id| *id != room_id);
|
||||
|
||||
AppSettings::update_pinned_rooms(self.pinned_rooms.clone(), cx);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -443,6 +476,16 @@ fn nav_item(id: &'static str, icon: IconName, label: &'static str, command: Comm
|
||||
})
|
||||
}
|
||||
|
||||
fn load_expanded(cx: &App) -> BTreeSet<TreeSection> {
|
||||
let Some(keys) = AppSettings::get_expanded_sections(cx) else {
|
||||
return BTreeSet::from([TreeSection::Community, TreeSection::Messages]);
|
||||
};
|
||||
|
||||
keys.iter()
|
||||
.filter_map(|key| TreeSection::from_key(key.as_str()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Panel for Sidebar {
|
||||
fn panel_id(&self) -> SharedString {
|
||||
"Sidebar".into()
|
||||
|
||||
@@ -9,7 +9,6 @@ use gpui::{
|
||||
use theme::ActiveTheme;
|
||||
use ui::{Icon, IconName, Sizable, StyledExt, h_flex};
|
||||
|
||||
/// Collapsible tree sections; declaration order is render order.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum TreeSection {
|
||||
Pins,
|
||||
@@ -34,9 +33,27 @@ impl TreeSection {
|
||||
Self::Messages => IconName::Message,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key(self) -> &'static str {
|
||||
match self {
|
||||
Self::Pins => "pins",
|
||||
Self::Requests => "requests",
|
||||
Self::Community => "community",
|
||||
Self::Messages => "messages",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_key(key: &str) -> Option<Self> {
|
||||
match key {
|
||||
"pins" => Some(Self::Pins),
|
||||
"requests" => Some(Self::Requests),
|
||||
"community" => Some(Self::Community),
|
||||
"messages" => Some(Self::Messages),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One rendered tree row, in flattened order.
|
||||
pub enum SidebarRow {
|
||||
Section {
|
||||
section: TreeSection,
|
||||
@@ -57,12 +74,10 @@ pub enum SidebarRow {
|
||||
},
|
||||
}
|
||||
|
||||
/// A community shown under the Community section.
|
||||
pub struct CommunityEntry {
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
/// Communities to show until the Concord backend is wired up.
|
||||
pub fn dummy_communities() -> &'static [CommunityEntry] {
|
||||
// TODO(concord): replace with ConcordRegistry communities, see docs/concord-usage.md.
|
||||
&[
|
||||
@@ -75,7 +90,6 @@ pub fn dummy_communities() -> &'static [CommunityEntry] {
|
||||
]
|
||||
}
|
||||
|
||||
/// Presentation differences between the rows [`TreeRow`] draws.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum TreeRowKind {
|
||||
Section,
|
||||
@@ -83,7 +97,6 @@ pub enum TreeRowKind {
|
||||
Hint,
|
||||
}
|
||||
|
||||
/// Folder/file row. One element for section headers, community rows and hints.
|
||||
#[derive(IntoElement)]
|
||||
pub struct TreeRow {
|
||||
id: ElementId,
|
||||
@@ -95,7 +108,6 @@ pub struct TreeRow {
|
||||
label: SharedString,
|
||||
count: Option<usize>,
|
||||
dot: bool,
|
||||
selected: bool,
|
||||
#[allow(clippy::type_complexity)]
|
||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
|
||||
}
|
||||
@@ -116,7 +128,6 @@ impl TreeRow {
|
||||
label: label.into(),
|
||||
count: None,
|
||||
dot: false,
|
||||
selected: false,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
@@ -151,11 +162,6 @@ impl TreeRow {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
|
||||
@@ -196,9 +202,6 @@ impl RenderOnce for TreeRow {
|
||||
.font_normal()
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
})
|
||||
.when(self.selected, |this| {
|
||||
this.bg(cx.theme().ghost_element_selected)
|
||||
})
|
||||
.when_some(self.caret, |this, caret| {
|
||||
this.child(Icon::new(caret).xsmall().text_color(cx.theme().icon_muted))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user