chore: improve performance (#42)
* use uniform list for rooms list * move profile cache to outside gpui context * update comment * refactor * refactor * . * . * add avatar component * . * refactor * .
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::BTreeSet, ops::Range, time::Duration};
|
||||
|
||||
use account::Account;
|
||||
use async_utility::task::spawn;
|
||||
@@ -10,33 +7,30 @@ use chats::{
|
||||
ChatRegistry,
|
||||
};
|
||||
|
||||
use common::{debounced_delay::DebouncedDelay, profile::SharedProfile};
|
||||
use folder::{Folder, FolderItem, Parent};
|
||||
use common::{debounced_delay::DebouncedDelay, profile::RenderProfile};
|
||||
use element::DisplayRoom;
|
||||
use global::{constants::SEARCH_RELAYS, get_client};
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, AnyElement, App, AppContext, Context, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, Subscription, Task, Window,
|
||||
div, prelude::FluentBuilder, rems, uniform_list, AnyElement, App, AppContext, Context, Entity,
|
||||
EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, RetainAllImageCache,
|
||||
SharedString, Styled, Subscription, Task, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonCustomVariant, ButtonRounded, ButtonVariants},
|
||||
dock_area::{
|
||||
dock::DockPlacement,
|
||||
panel::{Panel, PanelEvent},
|
||||
},
|
||||
avatar::Avatar,
|
||||
button::{Button, ButtonRounded, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
input::{InputEvent, InputState, TextInput},
|
||||
popup_menu::{PopupMenu, PopupMenuExt},
|
||||
skeleton::Skeleton,
|
||||
IconName, Sizable, StyledExt,
|
||||
ContextModal, IconName, Selectable, Sizable, StyledExt,
|
||||
};
|
||||
|
||||
use crate::chatspace::{AddPanel, ModalKind, PanelKind, ToggleModal};
|
||||
use crate::chatspace::{ModalKind, ToggleModal};
|
||||
|
||||
mod folder;
|
||||
mod element;
|
||||
|
||||
const FIND_DELAY: u64 = 600;
|
||||
const FIND_LIMIT: usize = 10;
|
||||
@@ -45,18 +39,6 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Sidebar> {
|
||||
Sidebar::new(window, cx)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Item {
|
||||
Ongoing,
|
||||
Incoming,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum SubItem {
|
||||
Trusted,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub struct Sidebar {
|
||||
name: SharedString,
|
||||
// Search
|
||||
@@ -65,13 +47,12 @@ pub struct Sidebar {
|
||||
finding: bool,
|
||||
local_result: Entity<Option<Vec<Entity<Room>>>>,
|
||||
global_result: Entity<Option<Vec<Entity<Room>>>>,
|
||||
// Layout
|
||||
folders: bool,
|
||||
active_items: HashSet<Item>,
|
||||
active_subitems: HashSet<SubItem>,
|
||||
// Rooms
|
||||
active_filter: Entity<RoomKind>,
|
||||
trusted_only: bool,
|
||||
// GPUI
|
||||
focus_handle: FocusHandle,
|
||||
scroll_handle: ScrollHandle,
|
||||
image_cache: Entity<RetainAllImageCache>,
|
||||
#[allow(dead_code)]
|
||||
subscriptions: SmallVec<[Subscription; 1]>,
|
||||
}
|
||||
@@ -82,16 +63,7 @@ impl Sidebar {
|
||||
}
|
||||
|
||||
fn view(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let focus_handle = cx.focus_handle();
|
||||
let scroll_handle = ScrollHandle::default();
|
||||
|
||||
let mut active_items = HashSet::with_capacity(2);
|
||||
active_items.insert(Item::Ongoing);
|
||||
|
||||
let mut active_subitems = HashSet::with_capacity(2);
|
||||
active_subitems.insert(SubItem::Trusted);
|
||||
active_subitems.insert(SubItem::Unknown);
|
||||
|
||||
let active_filter = cx.new(|_| RoomKind::Ongoing);
|
||||
let local_result = cx.new(|_| None);
|
||||
let global_result = cx.new(|_| None);
|
||||
|
||||
@@ -100,8 +72,10 @@ impl Sidebar {
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
cx.subscribe_in(&find_input, window, |this, _, event, _, cx| {
|
||||
subscriptions.push(cx.subscribe_in(
|
||||
&find_input,
|
||||
window,
|
||||
|this, _state, event, _window, cx| {
|
||||
match event {
|
||||
InputEvent::PressEnter { .. } => this.search(cx),
|
||||
InputEvent::Change(text) => {
|
||||
@@ -119,44 +93,24 @@ impl Sidebar {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
));
|
||||
|
||||
Self {
|
||||
name: "Chat Sidebar".into(),
|
||||
folders: false,
|
||||
focus_handle: cx.focus_handle(),
|
||||
image_cache: RetainAllImageCache::new(cx),
|
||||
find_debouncer: DebouncedDelay::new(),
|
||||
finding: false,
|
||||
trusted_only: false,
|
||||
active_filter,
|
||||
find_input,
|
||||
local_result,
|
||||
global_result,
|
||||
active_items,
|
||||
active_subitems,
|
||||
focus_handle,
|
||||
scroll_handle,
|
||||
subscriptions,
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_item(&mut self, item: Item, cx: &mut Context<Self>) {
|
||||
if !self.active_items.remove(&item) {
|
||||
self.active_items.insert(item);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn toggle_subitem(&mut self, subitem: SubItem, cx: &mut Context<Self>) {
|
||||
if !self.active_subitems.remove(&subitem) {
|
||||
self.active_subitems.insert(subitem);
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn toggle_folder(&mut self, cx: &mut Context<Self>) {
|
||||
self.folders = !self.folders;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn debounced_search(&self, cx: &mut Context<Self>) -> Task<()> {
|
||||
cx.spawn(async move |this, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
@@ -189,7 +143,7 @@ impl Sidebar {
|
||||
|
||||
spawn(async move {
|
||||
let client = get_client();
|
||||
let signer = client.signer().await.unwrap();
|
||||
let signer = client.signer().await.expect("signer is required");
|
||||
|
||||
for event in events.into_iter() {
|
||||
let metadata = Metadata::from_json(event.content).unwrap_or_default();
|
||||
@@ -227,6 +181,14 @@ impl Sidebar {
|
||||
return;
|
||||
}
|
||||
|
||||
if query.starts_with("nevent1")
|
||||
|| query.starts_with("naddr")
|
||||
|| query.starts_with("nsec1")
|
||||
|| query.starts_with("note1")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Return if search is in progress
|
||||
if self.finding {
|
||||
return;
|
||||
@@ -299,83 +261,151 @@ impl Sidebar {
|
||||
});
|
||||
}
|
||||
|
||||
fn push_room(&mut self, id: u64, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(result) = self.global_result.read(cx).as_ref() {
|
||||
if let Some(room) = result.iter().find(|this| this.read(cx).id == id).cloned() {
|
||||
ChatRegistry::global(cx).update(cx, |this, cx| {
|
||||
this.push_room(room, cx);
|
||||
});
|
||||
window.dispatch_action(
|
||||
Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)),
|
||||
cx,
|
||||
);
|
||||
self.clear_search_results(cx);
|
||||
}
|
||||
}
|
||||
fn filter(&self, kind: &RoomKind, cx: &Context<Self>) -> bool {
|
||||
self.active_filter.read(cx) == kind
|
||||
}
|
||||
|
||||
fn set_filter(&mut self, kind: RoomKind, cx: &mut Context<Self>) {
|
||||
self.active_filter.update(cx, |this, cx| {
|
||||
*this = kind;
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
|
||||
fn set_trusted_only(&mut self, cx: &mut Context<Self>) {
|
||||
self.trusted_only = !self.trusted_only;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn open_room(&mut self, id: u64, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let room = if let Some(room) = ChatRegistry::get_global(cx).room(&id, cx) {
|
||||
room
|
||||
} else {
|
||||
self.clear_search_results(cx);
|
||||
|
||||
let Some(result) = self.global_result.read(cx).as_ref() else {
|
||||
window.push_notification("Failed to open room. Please try again later.", cx);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(room) = result.iter().find(|this| this.read(cx).id == id).cloned() else {
|
||||
window.push_notification("Failed to open room. Please try again later.", cx);
|
||||
return;
|
||||
};
|
||||
|
||||
room
|
||||
};
|
||||
|
||||
ChatRegistry::global(cx).update(cx, |this, cx| {
|
||||
this.push_room(room, cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_account(&self, profile: &Profile, cx: &Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.px_3()
|
||||
.h_8()
|
||||
.flex_none()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.child(Avatar::new(profile.render_avatar()).size(rems(1.75)))
|
||||
.child(profile.render_name()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("user")
|
||||
.icon(IconName::Ellipsis)
|
||||
.small()
|
||||
.ghost()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.popup_menu(|this, _window, _cx| {
|
||||
this.menu(
|
||||
"Profile",
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Profile,
|
||||
}),
|
||||
)
|
||||
.menu(
|
||||
"Relays",
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Relay,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("compose")
|
||||
.icon(IconName::PlusFill)
|
||||
.tooltip("Create DM or Group DM")
|
||||
.small()
|
||||
.primary()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Compose,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_skeleton(&self, total: i32) -> impl IntoIterator<Item = impl IntoElement> {
|
||||
(0..total).map(|_| {
|
||||
div()
|
||||
.h_8()
|
||||
.h_9()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.px_1p5()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(Skeleton::new().flex_shrink_0().size_6().rounded_full())
|
||||
.child(Skeleton::new().w_20().h_3().rounded_sm())
|
||||
.child(Skeleton::new().w_40().h_4().rounded_sm())
|
||||
})
|
||||
}
|
||||
|
||||
fn render_global_items(rooms: &[Entity<Room>], cx: &Context<Self>) -> Vec<FolderItem> {
|
||||
let mut items = Vec::with_capacity(rooms.len());
|
||||
fn render_uniform_item(
|
||||
&self,
|
||||
rooms: &[Entity<Room>],
|
||||
range: Range<usize>,
|
||||
cx: &Context<Self>,
|
||||
) -> Vec<impl IntoElement> {
|
||||
let mut items = Vec::with_capacity(range.end - range.start);
|
||||
|
||||
for room in rooms.iter() {
|
||||
let this = room.read(cx);
|
||||
let id = this.id;
|
||||
let label = this.display_name(cx);
|
||||
let img = this.display_image(cx).map(img);
|
||||
for ix in range {
|
||||
if let Some(room) = rooms.get(ix) {
|
||||
let this = room.read(cx);
|
||||
let id = this.id;
|
||||
let ago = this.ago();
|
||||
let label = this.display_name(cx);
|
||||
let img = this.display_image(cx);
|
||||
|
||||
let item = FolderItem::new(id as usize)
|
||||
.label(label)
|
||||
.img(img)
|
||||
.on_click({
|
||||
cx.listener(move |this, _, window, cx| {
|
||||
this.push_room(id, window, cx);
|
||||
})
|
||||
let handler = cx.listener(move |this, _, window, cx| {
|
||||
this.open_room(id, window, cx);
|
||||
});
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
fn render_items(rooms: &[Entity<Room>], cx: &Context<Self>) -> Vec<FolderItem> {
|
||||
let mut items = Vec::with_capacity(rooms.len());
|
||||
|
||||
for room in rooms.iter() {
|
||||
let room = room.read(cx);
|
||||
let id = room.id;
|
||||
let ago = room.ago();
|
||||
let label = room.display_name(cx);
|
||||
let img = room.display_image(cx).map(img);
|
||||
|
||||
let item = FolderItem::new(id as usize)
|
||||
.label(label)
|
||||
.description(ago)
|
||||
.img(img)
|
||||
.on_click({
|
||||
cx.listener(move |_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
items.push(item);
|
||||
items.push(
|
||||
DisplayRoom::new(ix)
|
||||
.img(img)
|
||||
.label(label)
|
||||
.description(ago)
|
||||
.on_click(handler),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items
|
||||
@@ -409,220 +439,145 @@ impl Focusable for Sidebar {
|
||||
}
|
||||
|
||||
impl Render for Sidebar {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let account = Account::get_global(cx).profile_ref();
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let chats = ChatRegistry::get_global(cx);
|
||||
|
||||
// Get search result
|
||||
let local_result = self.local_result.read(cx);
|
||||
let global_result = self.global_result.read(cx);
|
||||
let rooms = if let Some(results) = self.local_result.read(cx) {
|
||||
results.to_owned()
|
||||
} else {
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if self.active_filter.read(cx) == &RoomKind::Ongoing {
|
||||
chats.ongoing_rooms(cx)
|
||||
} else {
|
||||
chats.request_rooms(self.trusted_only, cx)
|
||||
}
|
||||
};
|
||||
|
||||
div()
|
||||
.id("sidebar")
|
||||
.track_focus(&self.focus_handle)
|
||||
.track_scroll(&self.scroll_handle)
|
||||
.overflow_y_scroll()
|
||||
.image_cache(self.image_cache.clone())
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.py_1()
|
||||
.when_some(account, |this, profile| {
|
||||
this.child(
|
||||
div()
|
||||
.px_3()
|
||||
.h_7()
|
||||
.flex_none()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.child(img(profile.shared_avatar()).size_7())
|
||||
.child(profile.shared_name()),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Button::new("user")
|
||||
.icon(IconName::Ellipsis)
|
||||
.small()
|
||||
.ghost()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.popup_menu(|this, _window, _cx| {
|
||||
this.menu(
|
||||
"Profile",
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Profile,
|
||||
}),
|
||||
)
|
||||
.menu(
|
||||
"Relays",
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Relay,
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("compose")
|
||||
.icon(IconName::PlusFill)
|
||||
.tooltip("Create DM or Group DM")
|
||||
.small()
|
||||
.primary()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Compose,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
// Account
|
||||
.when_some(Account::get_global(cx).profile_ref(), |this, profile| {
|
||||
this.child(self.render_account(profile, cx))
|
||||
})
|
||||
// Search Input
|
||||
.child(
|
||||
div().px_3().h_7().flex_none().child(
|
||||
div().px_3().w_full().h_7().flex_none().child(
|
||||
TextInput::new(&self.find_input).small().suffix(
|
||||
Button::new("find")
|
||||
.icon(IconName::Search)
|
||||
.tooltip("Press Enter to search")
|
||||
.small()
|
||||
.custom(
|
||||
ButtonCustomVariant::new(window, cx)
|
||||
.active(gpui::transparent_black())
|
||||
.color(gpui::transparent_black())
|
||||
.hover(gpui::transparent_black())
|
||||
.foreground(cx.theme().text_placeholder),
|
||||
),
|
||||
.transparent()
|
||||
.small(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.when_some(global_result.as_ref(), |this, rooms| {
|
||||
// Global Search Results
|
||||
.when_some(self.global_result.read(cx).clone(), |this, rooms| {
|
||||
this.child(
|
||||
div()
|
||||
.px_1()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.children(Self::render_global_items(rooms, cx)),
|
||||
div().px_1().w_full().flex_1().overflow_y_hidden().child(
|
||||
uniform_list(
|
||||
cx.entity(),
|
||||
"results",
|
||||
rooms.len(),
|
||||
move |this, range, _window, cx| {
|
||||
this.render_uniform_item(&rooms, range, cx)
|
||||
},
|
||||
)
|
||||
.h_full(),
|
||||
),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
.px_2()
|
||||
.w_full()
|
||||
.flex_1()
|
||||
.overflow_y_hidden()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.mb_1()
|
||||
.px_2()
|
||||
.flex_none()
|
||||
.px_1()
|
||||
.w_full()
|
||||
.h_9()
|
||||
.flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.child("Messages")
|
||||
.justify_between()
|
||||
.child(
|
||||
Button::new("menu")
|
||||
.tooltip("Toggle chat folders")
|
||||
.map(|this| {
|
||||
if self.folders {
|
||||
this.icon(IconName::FilterFill)
|
||||
} else {
|
||||
this.icon(IconName::Filter)
|
||||
}
|
||||
})
|
||||
.small()
|
||||
.custom(
|
||||
ButtonCustomVariant::new(window, cx)
|
||||
.foreground(cx.theme().text_placeholder)
|
||||
.color(cx.theme().ghost_element_background)
|
||||
.hover(cx.theme().ghost_element_background)
|
||||
.active(cx.theme().ghost_element_background),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_folder(cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.when(chats.wait_for_eose, |this| {
|
||||
this.children(self.render_skeleton(6))
|
||||
})
|
||||
.map(|this| {
|
||||
if let Some(rooms) = local_result {
|
||||
this.children(Self::render_items(rooms, cx))
|
||||
} else if !self.folders {
|
||||
this.children(Self::render_items(&chats.rooms, cx))
|
||||
} else {
|
||||
this.child(
|
||||
Folder::new("Ongoing")
|
||||
.icon(IconName::Folder)
|
||||
.tooltip("All ongoing conversations")
|
||||
.collapsed(!self.active_items.contains(&Item::Ongoing))
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_item(Item::Ongoing, cx);
|
||||
}))
|
||||
.children(Self::render_items(
|
||||
&chats.rooms_by_kind(RoomKind::Ongoing, cx),
|
||||
cx,
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
Parent::new("Incoming")
|
||||
.icon(IconName::Folder)
|
||||
.tooltip("Incoming messages")
|
||||
.collapsed(!self.active_items.contains(&Item::Incoming))
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_item(Item::Incoming, cx);
|
||||
}))
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Folder::new("Trusted")
|
||||
.icon(IconName::Folder)
|
||||
.tooltip("Incoming messages from trusted contacts")
|
||||
.collapsed(
|
||||
!self.active_subitems.contains(&SubItem::Trusted),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_subitem(SubItem::Trusted, cx);
|
||||
}))
|
||||
.children(Self::render_items(
|
||||
&chats.rooms_by_kind(RoomKind::Trusted, cx),
|
||||
cx,
|
||||
)),
|
||||
Button::new("all")
|
||||
.label("All")
|
||||
.small()
|
||||
.bold()
|
||||
.secondary()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.selected(self.filter(&RoomKind::Ongoing, cx))
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.set_filter(RoomKind::Ongoing, cx);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Folder::new("Unknown")
|
||||
.icon(IconName::Folder)
|
||||
.tooltip("Incoming messages from unknowns")
|
||||
.collapsed(
|
||||
!self.active_subitems.contains(&SubItem::Unknown),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.toggle_subitem(SubItem::Unknown, cx);
|
||||
}))
|
||||
.children(Self::render_items(
|
||||
&chats.rooms_by_kind(RoomKind::Unknown, cx),
|
||||
cx,
|
||||
)),
|
||||
Button::new("requests")
|
||||
.label("Requests")
|
||||
.small()
|
||||
.bold()
|
||||
.secondary()
|
||||
.rounded(ButtonRounded::Full)
|
||||
.selected(!self.filter(&RoomKind::Ongoing, cx))
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.set_filter(RoomKind::Unknown, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}),
|
||||
.when(!self.filter(&RoomKind::Ongoing, cx), |this| {
|
||||
this.child(
|
||||
Button::new("trusted")
|
||||
.tooltip("Only show rooms from trusted contacts")
|
||||
.map(|this| {
|
||||
if self.trusted_only {
|
||||
this.icon(IconName::FilterFill)
|
||||
} else {
|
||||
this.icon(IconName::Filter)
|
||||
}
|
||||
})
|
||||
.small()
|
||||
.transparent()
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.set_trusted_only(cx);
|
||||
})),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when(chats.wait_for_eose, |this| {
|
||||
this.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.children(self.render_skeleton(10)),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
uniform_list(
|
||||
cx.entity(),
|
||||
"rooms",
|
||||
rooms.len(),
|
||||
move |this, range, _window, cx| {
|
||||
this.render_uniform_item(&rooms, range, cx)
|
||||
},
|
||||
)
|
||||
.h_full(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user