Prune dead code and key elements by stable ids
This commit is contained in:
@@ -52,7 +52,7 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Workspace> {
|
||||
cx.new(|cx| Workspace::new(window, cx))
|
||||
}
|
||||
|
||||
struct DeviceNotifcation;
|
||||
struct DeviceNotification;
|
||||
struct MsgRelayNotification;
|
||||
|
||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||
@@ -141,7 +141,7 @@ impl Workspace {
|
||||
one shown there before approving.";
|
||||
|
||||
let note = Notification::new()
|
||||
.id::<DeviceNotifcation>()
|
||||
.id::<DeviceNotification>()
|
||||
.autohide(false)
|
||||
.title(format!("Wait for approval · {code}"))
|
||||
.message(MSG)
|
||||
@@ -155,7 +155,7 @@ impl Workspace {
|
||||
from the settings.";
|
||||
|
||||
let note = Notification::new()
|
||||
.id::<DeviceNotifcation>()
|
||||
.id::<DeviceNotification>()
|
||||
.message(MSG)
|
||||
.with_type(NotificationType::Info)
|
||||
.action(|_this, _window, _cx| {
|
||||
@@ -165,7 +165,7 @@ impl Workspace {
|
||||
device.update(cx, |this, cx| {
|
||||
this.get_announcement(cx);
|
||||
});
|
||||
window.remove_notification::<DeviceNotifcation>(cx);
|
||||
window.remove_notification::<DeviceNotification>(cx);
|
||||
},
|
||||
)
|
||||
});
|
||||
@@ -174,7 +174,7 @@ impl Workspace {
|
||||
}
|
||||
DeviceEvent::Set => {
|
||||
let note = Notification::new()
|
||||
.id::<DeviceNotifcation>()
|
||||
.id::<DeviceNotification>()
|
||||
.message("Encryption Key has been set")
|
||||
.with_type(NotificationType::Success);
|
||||
|
||||
@@ -231,14 +231,6 @@ impl Workspace {
|
||||
::nip60::WalletEvent::Error(error) => {
|
||||
window.push_notification(Notification::error(error.clone()), cx);
|
||||
}
|
||||
::nip60::WalletEvent::TokenCreated(_) => {
|
||||
window.push_notification(
|
||||
Notification::new()
|
||||
.message("Cashu token created, copy it from the wallet panel")
|
||||
.with_type(NotificationType::Success),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
::nip60::WalletEvent::Updated => {}
|
||||
},
|
||||
));
|
||||
|
||||
@@ -235,13 +235,14 @@ impl ContactListPanel {
|
||||
fn render_list_items(&mut self, cx: &mut Context<Self>) -> Vec<impl IntoElement> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
for (ix, contact) in self.contacts.iter().enumerate() {
|
||||
for contact in &self.contacts {
|
||||
let public_key = contact.public_key;
|
||||
let hex = public_key.to_hex();
|
||||
let profile = self.directory.read(cx).person(&public_key, cx);
|
||||
|
||||
items.push(
|
||||
h_flex()
|
||||
.id(ix)
|
||||
.id(hex.clone())
|
||||
.group("")
|
||||
.flex_1()
|
||||
.w_full()
|
||||
@@ -262,7 +263,7 @@ impl ContactListPanel {
|
||||
.child(profile.name()),
|
||||
)
|
||||
.child(
|
||||
Button::new("remove_{ix}")
|
||||
Button::new(format!("remove-{hex}"))
|
||||
.icon(IconName::X)
|
||||
.xsmall()
|
||||
.ghost()
|
||||
|
||||
@@ -213,17 +213,17 @@ impl GreeterPanel {
|
||||
} else {
|
||||
requests
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (room, preview))| {
|
||||
.map(|(room, preview)| {
|
||||
let room = room.clone();
|
||||
let dock = dock.clone();
|
||||
let room_id = room.read(cx).id;
|
||||
let name = room.read(cx).display_name(cx);
|
||||
let seed = room.read(cx).display_image_seed(cx);
|
||||
let created_at = room.read(cx).created_at.to_ago();
|
||||
let preview = preview.clone();
|
||||
|
||||
h_flex()
|
||||
.id(format!("request-{index}"))
|
||||
.id(format!("request-{room_id}"))
|
||||
.group("")
|
||||
.w_full()
|
||||
.h_9()
|
||||
@@ -280,7 +280,7 @@ impl GreeterPanel {
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new(format!("response-{index}"))
|
||||
Button::new(format!("response-{room_id}"))
|
||||
.label("Response")
|
||||
.xsmall()
|
||||
.ghost()
|
||||
@@ -332,17 +332,17 @@ impl GreeterPanel {
|
||||
let rows: Vec<AnyElement> = self
|
||||
.follow_ups
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (room, preview))| {
|
||||
.map(|(room, preview)| {
|
||||
let room = room.clone();
|
||||
let dock = dock.clone();
|
||||
let room_id = room.read(cx).id;
|
||||
let name = room.read(cx).display_name(cx);
|
||||
let seed = room.read(cx).display_image_seed(cx);
|
||||
let created_at = room.read(cx).created_at.to_ago();
|
||||
let preview = preview.clone();
|
||||
|
||||
h_flex()
|
||||
.id(format!("follow-up-{index}"))
|
||||
.id(format!("follow-up-{room_id}"))
|
||||
.group("")
|
||||
.w_full()
|
||||
.h_9()
|
||||
@@ -399,7 +399,7 @@ impl GreeterPanel {
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new(format!("reply-{index}"))
|
||||
Button::new(format!("reply-{room_id}"))
|
||||
.label("Reply")
|
||||
.xsmall()
|
||||
.ghost()
|
||||
|
||||
@@ -34,11 +34,42 @@ impl ListSection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cheap snapshot of the cached rows' inputs, compared on every render; covers
|
||||
/// inputs that never notify the sidebar (pin edits, room kind promotions, and
|
||||
/// the visibility flags mutated across the sidebar's modules).
|
||||
#[derive(Default, PartialEq)]
|
||||
pub(super) struct RowsSignature {
|
||||
contacts_open: bool,
|
||||
requests_open: bool,
|
||||
contacts_loaded: bool,
|
||||
pins_open: bool,
|
||||
groups_open: bool,
|
||||
chats_open: bool,
|
||||
pinned_rooms: Vec<u64>,
|
||||
pinned_groups: Vec<String>,
|
||||
/// Kinds of the rooms held by the cache; a request can turn ongoing with
|
||||
/// no event or registry notify the sidebar can hear
|
||||
room_kinds: Vec<(u64, RoomKind)>,
|
||||
}
|
||||
|
||||
/// Read the id and kind of every room row held by the given cache.
|
||||
fn room_signature(rows: &[SidebarRow], cx: &App) -> Vec<(u64, RoomKind)> {
|
||||
rows.iter()
|
||||
.filter_map(|row| match row {
|
||||
SidebarRow::Room { room } => {
|
||||
let room = room.read(cx);
|
||||
Some((room.id, room.kind))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Sidebar {
|
||||
pub(super) fn render_list(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
||||
let requests_open = self.requests_open;
|
||||
let contacts_open = self.contacts_open && !requests_open;
|
||||
let rows = Rc::new(self.rows_for(cx));
|
||||
let rows = self.cached_rows(cx);
|
||||
// Whether to show the create button for the contact picker
|
||||
let create_button = contacts_open && !self.selected_contacts.is_empty();
|
||||
|
||||
@@ -175,6 +206,31 @@ impl Sidebar {
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// Rebuild the rows only when a dirty source fired or the snapshot changed.
|
||||
fn cached_rows(&mut self, cx: &mut Context<Self>) -> Rc<Vec<SidebarRow>> {
|
||||
let mut signature = RowsSignature {
|
||||
contacts_open: self.contacts_open,
|
||||
requests_open: self.requests_open,
|
||||
contacts_loaded: self.contacts.is_some(),
|
||||
pins_open: self.pins_open,
|
||||
groups_open: self.is_groups_open(cx),
|
||||
chats_open: self.is_chats_open(cx),
|
||||
pinned_rooms: AppSettings::get_pinned_rooms(cx),
|
||||
pinned_groups: AppSettings::get_pinned_groups(cx),
|
||||
room_kinds: room_signature(&self.rows, cx),
|
||||
};
|
||||
|
||||
if self.rows_dirty || self.rows_signature != signature {
|
||||
self.rows = Rc::new(self.rows_for(cx));
|
||||
self.rows_dirty = false;
|
||||
// Describe the rebuilt cache, so the next render compares apples to apples
|
||||
signature.room_kinds = room_signature(&self.rows, cx);
|
||||
}
|
||||
|
||||
self.rows_signature = signature;
|
||||
Rc::clone(&self.rows)
|
||||
}
|
||||
|
||||
fn rows_for(&self, cx: &App) -> Vec<SidebarRow> {
|
||||
if self.contacts_open {
|
||||
return self.contacts_rows();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Error;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -18,9 +19,11 @@ use gpui_kit::component::notification::Notification;
|
||||
use gpui_kit::component::progress::ProgressCircle;
|
||||
use gpui_kit::component::{ActiveTheme, WindowExt, h_flex, v_flex};
|
||||
use instant::Duration;
|
||||
use list::RowsSignature;
|
||||
use nip29::{Group, GroupKey, GroupsEvent, GroupsRegistry};
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Directory, PersonRegistry};
|
||||
use rows::SidebarRow;
|
||||
pub(crate) use rows::open_room;
|
||||
use search::SearchResult;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
@@ -90,6 +93,12 @@ pub struct Sidebar {
|
||||
chat_progress: f32,
|
||||
/// Simulated web of trust sync progress
|
||||
sync_progress: f32,
|
||||
/// Cached main list rows; rebuilt only when invalidation is signaled
|
||||
rows: Rc<Vec<SidebarRow>>,
|
||||
/// Snapshot of the cached rows' inputs, compared on every render
|
||||
rows_signature: RowsSignature,
|
||||
/// Whether the rows changed through a source the snapshot cannot see
|
||||
rows_dirty: bool,
|
||||
/// The updater facts the header renders, mirrored so updater churn cannot invalidate the cached sidebar
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updater_display: Entity<Option<UpdateDisplay>>,
|
||||
@@ -118,10 +127,12 @@ impl Sidebar {
|
||||
match event {
|
||||
ChatEvent::Ping => {
|
||||
this.new_requests = true;
|
||||
this.rows_dirty = true;
|
||||
cx.notify();
|
||||
}
|
||||
ChatEvent::Loaded => {
|
||||
this.chat_progress = 0.0;
|
||||
this.rows_dirty = true;
|
||||
cx.notify();
|
||||
}
|
||||
_ => {}
|
||||
@@ -130,9 +141,10 @@ impl Sidebar {
|
||||
);
|
||||
|
||||
subscriptions.push(
|
||||
cx.subscribe_in(&groups, window, |_this, _, event, window, cx| {
|
||||
cx.subscribe_in(&groups, window, |this, _, event, window, cx| {
|
||||
match event {
|
||||
GroupsEvent::Updated => {
|
||||
this.rows_dirty = true;
|
||||
// TODO: we must not trigger re-render directly like this
|
||||
cx.notify();
|
||||
}
|
||||
@@ -145,11 +157,24 @@ impl Sidebar {
|
||||
} => {
|
||||
window.push_notification(Notification::warning(warning.clone()), cx);
|
||||
}
|
||||
// New groups can arrive without `Updated` and without a
|
||||
// registry notify, so only the event itself can dirty the cache
|
||||
GroupsEvent::Synced | GroupsEvent::Created { .. } => {
|
||||
this.rows_dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
subscriptions.push(cx.observe(&chat, |this, _, _| {
|
||||
this.rows_dirty = true;
|
||||
}));
|
||||
|
||||
subscriptions.push(cx.observe(&groups, |this, _, _| {
|
||||
this.rows_dirty = true;
|
||||
}));
|
||||
|
||||
subscriptions.push(
|
||||
cx.subscribe_in(
|
||||
&nostr,
|
||||
@@ -260,6 +285,9 @@ impl Sidebar {
|
||||
global_task: None,
|
||||
chat_progress: 0.0,
|
||||
sync_progress: 0.0,
|
||||
rows: Rc::default(),
|
||||
rows_signature: RowsSignature::default(),
|
||||
rows_dirty: true,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
updater_display,
|
||||
tasks,
|
||||
|
||||
@@ -105,10 +105,7 @@ impl Sidebar {
|
||||
rows.get(range.clone())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.enumerate()
|
||||
.map(|(offset, row)| {
|
||||
let index = range.start + offset;
|
||||
|
||||
.map(|row| {
|
||||
let element = match row {
|
||||
SidebarRow::Section(section) => {
|
||||
let section = *section;
|
||||
@@ -181,7 +178,7 @@ impl Sidebar {
|
||||
let dock = self.dock.clone();
|
||||
let room = room.clone();
|
||||
|
||||
let nav = Nav::new(index)
|
||||
let nav = Nav::new(id)
|
||||
.label(name)
|
||||
.text_sm()
|
||||
.when(!hide_avatar, |this| {
|
||||
@@ -220,7 +217,7 @@ impl Sidebar {
|
||||
});
|
||||
|
||||
div()
|
||||
.id(index)
|
||||
.id(id)
|
||||
.w_full()
|
||||
.child(nav)
|
||||
.context_menu(move |menu, _window, _cx| {
|
||||
@@ -251,7 +248,7 @@ impl Sidebar {
|
||||
let sidebar = cx.entity().downgrade();
|
||||
let public_key = public_key.to_owned();
|
||||
|
||||
Nav::new(index)
|
||||
Nav::new(public_key.to_hex())
|
||||
.label(person.name())
|
||||
.text_sm()
|
||||
.when(!hide_avatar, |this| {
|
||||
@@ -275,7 +272,7 @@ impl Sidebar {
|
||||
let person = self.directory.read(cx).person(public_key, cx);
|
||||
let public_key = public_key.to_owned();
|
||||
|
||||
Nav::new(index)
|
||||
Nav::new(public_key.to_hex())
|
||||
.label(person.name())
|
||||
.text_sm()
|
||||
.when(!hide_avatar, |this| {
|
||||
@@ -308,7 +305,7 @@ impl Sidebar {
|
||||
.or_else(|| group.read(cx).elsewhere().map(|_| "Moved"))
|
||||
.map(SharedString::from);
|
||||
|
||||
let nav = Nav::new(index)
|
||||
let nav = Nav::new(tag.clone())
|
||||
.label(name)
|
||||
.text_sm()
|
||||
.when(!hide_avatar, |this| {
|
||||
@@ -347,7 +344,7 @@ impl Sidebar {
|
||||
});
|
||||
|
||||
div()
|
||||
.id(index)
|
||||
.id(tag.clone())
|
||||
.w_full()
|
||||
.child(nav)
|
||||
.context_menu(move |menu, _window, _cx| {
|
||||
@@ -383,8 +380,15 @@ impl Sidebar {
|
||||
let dock = self.dock.clone();
|
||||
let room = hit.room.clone();
|
||||
let content = hit.content.clone();
|
||||
// Several hits can share a room, so the id digests the whole hit
|
||||
let hit_id = common::stable_id_u64(&[
|
||||
hit.room.read(cx).id.to_string().as_str(),
|
||||
hit.author.to_string().as_str(),
|
||||
hit.created_at.as_secs().to_string().as_str(),
|
||||
hit.content.as_ref(),
|
||||
]);
|
||||
|
||||
Nav::new(index)
|
||||
Nav::new(hit_id)
|
||||
.label(content)
|
||||
.text_sm()
|
||||
.font_medium()
|
||||
|
||||
Reference in New Issue
Block a user