feat: add search and refactor modal (#19)
* add find button to sidebar * update * improve search * add error msg
This commit is contained in:
@@ -20,7 +20,7 @@ use ui::{
|
||||
use crate::{
|
||||
lru_cache::cache_provider,
|
||||
views::{
|
||||
chat, compose, contacts, login, new_account, onboarding, profile, relays, sidebar, welcome,
|
||||
chat, compose, login, new_account, onboarding, profile, relays, search, sidebar, welcome,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -52,8 +52,9 @@ pub enum PanelKind {
|
||||
pub enum ModalKind {
|
||||
Profile,
|
||||
Compose,
|
||||
Contact,
|
||||
Search,
|
||||
Relay,
|
||||
Onboarding,
|
||||
SetupRelay,
|
||||
}
|
||||
|
||||
@@ -239,14 +240,15 @@ impl ChatSpace {
|
||||
.child(compose.clone())
|
||||
})
|
||||
}
|
||||
ModalKind::Contact => {
|
||||
let contacts = contacts::init(window, cx);
|
||||
ModalKind::Search => {
|
||||
let search = search::init(window, cx);
|
||||
|
||||
window.open_modal(cx, move |this, _window, _cx| {
|
||||
this.width(px(MODAL_WIDTH))
|
||||
.title("Contacts")
|
||||
.child(contacts.clone())
|
||||
});
|
||||
window.open_modal(cx, move |modal, _, _| {
|
||||
modal
|
||||
.closable(false)
|
||||
.width(px(MODAL_WIDTH))
|
||||
.child(search.clone())
|
||||
})
|
||||
}
|
||||
ModalKind::Relay => {
|
||||
let relays = relays::init(window, cx);
|
||||
@@ -266,6 +268,7 @@ impl ChatSpace {
|
||||
.child(relays.clone())
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ use futures::{select, FutureExt};
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
use global::constants::APP_NAME;
|
||||
use global::{
|
||||
constants::{ALL_MESSAGES_SUB_ID, APP_ID, APP_PUBKEY, BOOTSTRAP_RELAYS, NEW_MESSAGE_SUB_ID},
|
||||
constants::{
|
||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_PUBKEY, BOOTSTRAP_RELAYS, NEW_MESSAGE_SUB_ID,
|
||||
SEARCH_RELAYS,
|
||||
},
|
||||
get_client,
|
||||
};
|
||||
use gpui::{
|
||||
@@ -72,6 +75,12 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
for relay in SEARCH_RELAYS.into_iter() {
|
||||
if let Err(e) = client.add_relay(relay).await {
|
||||
log::error!("Failed to add relay {}: {}", relay, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Establish connection to bootstrap relays
|
||||
client.connect().await;
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ impl Compose {
|
||||
let user_input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
.text_size(ui::Size::Small)
|
||||
.small()
|
||||
.placeholder("npub1...")
|
||||
});
|
||||
|
||||
@@ -116,10 +115,10 @@ impl Compose {
|
||||
contacts,
|
||||
selected,
|
||||
error_message,
|
||||
subscriptions,
|
||||
is_loading: false,
|
||||
is_submitting: false,
|
||||
focus_handle: cx.focus_handle(),
|
||||
subscriptions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,9 +179,10 @@ impl Compose {
|
||||
window.close_modal(cx);
|
||||
}
|
||||
Err(e) => {
|
||||
_ = this.update(cx, |this, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_error(Some(e.to_string().into()), cx);
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -341,6 +341,7 @@ impl Render for Compose {
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.px_3()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(DESCRIPTION),
|
||||
@@ -348,13 +349,14 @@ impl Render for Compose {
|
||||
.when_some(self.error_message.read(cx).as_ref(), |this, msg| {
|
||||
this.child(
|
||||
div()
|
||||
.px_3()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().danger)
|
||||
.child(msg.clone()),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
div().flex().flex_col().child(
|
||||
div().px_3().flex().flex_col().child(
|
||||
div()
|
||||
.h_10()
|
||||
.border_b_1()
|
||||
@@ -372,8 +374,15 @@ impl Render for Compose {
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.mt_1()
|
||||
.child(div().text_sm().font_semibold().child("To:"))
|
||||
.child(self.user_input.clone())
|
||||
.child(
|
||||
div()
|
||||
.px_3()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(div().text_sm().font_semibold().child("To:"))
|
||||
.child(self.user_input.clone()),
|
||||
)
|
||||
.map(|this| {
|
||||
let contacts = self.contacts.read(cx).clone();
|
||||
let view = cx.entity();
|
||||
@@ -423,11 +432,10 @@ impl Render for Compose {
|
||||
.id(ix)
|
||||
.w_full()
|
||||
.h_10()
|
||||
.px_2()
|
||||
.px_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -480,7 +488,7 @@ impl Render for Compose {
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div().mt_2().child(
|
||||
div().p_3().child(
|
||||
Button::new("create_dm_btn")
|
||||
.label(label)
|
||||
.primary()
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use anyhow::Error;
|
||||
use common::profile::SharedProfile;
|
||||
use global::get_client;
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context,
|
||||
Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, SharedString, Styled, Task, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use ui::{
|
||||
button::Button,
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
indicator::Indicator,
|
||||
popup_menu::PopupMenu,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Sizable,
|
||||
};
|
||||
|
||||
const MIN_HEIGHT: f32 = 280.;
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Contacts> {
|
||||
Contacts::new(window, cx)
|
||||
}
|
||||
|
||||
pub struct Contacts {
|
||||
contacts: Option<Vec<Profile>>,
|
||||
// Panel
|
||||
name: SharedString,
|
||||
closable: bool,
|
||||
zoomable: bool,
|
||||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
impl Contacts {
|
||||
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
cx.new(|cx| Self::view(window, cx))
|
||||
}
|
||||
|
||||
fn view(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
cx.spawn(async move |this, cx| {
|
||||
let client = get_client();
|
||||
|
||||
let task: Task<Result<BTreeSet<Profile>, Error>> = cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let profiles = client.database().contacts(public_key).await?;
|
||||
|
||||
Ok(profiles)
|
||||
});
|
||||
|
||||
if let Ok(contacts) = task.await {
|
||||
cx.update(|cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.contacts = Some(contacts.into_iter().collect_vec());
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {
|
||||
contacts: None,
|
||||
name: "Contacts".into(),
|
||||
closable: true,
|
||||
zoomable: true,
|
||||
focus_handle: cx.focus_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for Contacts {
|
||||
fn panel_id(&self) -> SharedString {
|
||||
"ContactPanel".into()
|
||||
}
|
||||
|
||||
fn title(&self, _cx: &App) -> AnyElement {
|
||||
self.name.clone().into_any_element()
|
||||
}
|
||||
|
||||
fn closable(&self, _cx: &App) -> bool {
|
||||
self.closable
|
||||
}
|
||||
|
||||
fn zoomable(&self, _cx: &App) -> bool {
|
||||
self.zoomable
|
||||
}
|
||||
|
||||
fn popup_menu(&self, menu: PopupMenu, _cx: &App) -> PopupMenu {
|
||||
menu.track_focus(&self.focus_handle)
|
||||
}
|
||||
|
||||
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for Contacts {}
|
||||
|
||||
impl Focusable for Contacts {
|
||||
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Contacts {
|
||||
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let entity = cx.entity().clone();
|
||||
|
||||
div().map(|this| {
|
||||
if let Some(contacts) = self.contacts.clone() {
|
||||
this.child(
|
||||
uniform_list(
|
||||
entity,
|
||||
"contacts",
|
||||
contacts.len(),
|
||||
move |_, range, _window, cx| {
|
||||
let mut items = Vec::with_capacity(contacts.len());
|
||||
|
||||
for ix in range {
|
||||
if let Some(item) = contacts.get(ix) {
|
||||
items.push(
|
||||
div()
|
||||
.w_full()
|
||||
.h_9()
|
||||
.px_2()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.text_xs()
|
||||
.child(
|
||||
div().flex_shrink_0().child(
|
||||
img(item.shared_avatar()).size_6(),
|
||||
),
|
||||
)
|
||||
.child(item.shared_name()),
|
||||
)
|
||||
.hover(|this| {
|
||||
this.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::THREE))
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
items
|
||||
},
|
||||
)
|
||||
.min_h(px(MIN_HEIGHT)),
|
||||
)
|
||||
} else {
|
||||
this.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.h_16()
|
||||
.child(Indicator::new().small())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
pub mod chat;
|
||||
pub mod compose;
|
||||
pub mod contacts;
|
||||
pub mod login;
|
||||
pub mod new_account;
|
||||
pub mod onboarding;
|
||||
pub mod profile;
|
||||
pub mod relays;
|
||||
pub mod search;
|
||||
pub mod sidebar;
|
||||
pub mod subject;
|
||||
pub mod welcome;
|
||||
|
||||
357
crates/coop/src/views/search.rs
Normal file
357
crates/coop/src/views/search.rs
Normal file
@@ -0,0 +1,357 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use async_utility::task::spawn;
|
||||
use chats::{
|
||||
room::{Room, RoomKind},
|
||||
ChatRegistry,
|
||||
};
|
||||
use common::profile::SharedProfile;
|
||||
use global::{constants::SEARCH_RELAYS, get_client};
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, px, relative, uniform_list, App, AppContext, Context, Entity,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
indicator::Indicator,
|
||||
input::{InputEvent, TextInput},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, IconName, Sizable,
|
||||
};
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Search> {
|
||||
Search::new(window, cx)
|
||||
}
|
||||
|
||||
pub struct Search {
|
||||
input: Entity<TextInput>,
|
||||
result: Entity<Vec<Profile>>,
|
||||
error: Entity<Option<SharedString>>,
|
||||
loading: bool,
|
||||
#[allow(dead_code)]
|
||||
subscriptions: SmallVec<[Subscription; 1]>,
|
||||
}
|
||||
|
||||
impl Search {
|
||||
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
let result = cx.new(|_| vec![]);
|
||||
let error = cx.new(|_| None);
|
||||
let input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
.text_size(ui::Size::Small)
|
||||
.placeholder("type something...")
|
||||
});
|
||||
|
||||
cx.new(|cx| {
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(cx.subscribe_in(
|
||||
&input,
|
||||
window,
|
||||
move |this: &mut Search, _, input_event, window, cx| {
|
||||
if let InputEvent::PressEnter = input_event {
|
||||
this.search(window, cx);
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
Self {
|
||||
input,
|
||||
result,
|
||||
error,
|
||||
subscriptions,
|
||||
loading: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn search(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.loading {
|
||||
return;
|
||||
};
|
||||
|
||||
// Show loading spinner
|
||||
self.loading(true, cx);
|
||||
|
||||
// Get search query
|
||||
let query = self.input.read(cx).text();
|
||||
|
||||
let task: Task<Result<Vec<Profile>, Error>> = cx.background_spawn(async move {
|
||||
let client = get_client();
|
||||
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::Metadata)
|
||||
.search(query.to_lowercase())
|
||||
.limit(10);
|
||||
|
||||
let events = client
|
||||
.fetch_events_from(SEARCH_RELAYS, filter, Duration::from_secs(3))
|
||||
.await?
|
||||
.into_iter()
|
||||
.unique_by(|event| event.pubkey)
|
||||
.collect_vec();
|
||||
|
||||
let mut users = vec![];
|
||||
let (tx, rx) = smol::channel::bounded::<Profile>(events.len());
|
||||
|
||||
spawn(async move {
|
||||
for event in events.into_iter() {
|
||||
let metadata = Metadata::from_json(event.content).unwrap_or_default();
|
||||
|
||||
if let Some(target) = metadata.nip05.as_ref() {
|
||||
if let Ok(verify) = nip05::verify(&event.pubkey, target, None).await {
|
||||
if verify {
|
||||
_ = tx.send(Profile::new(event.pubkey, metadata)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while let Ok(profile) = rx.recv().await {
|
||||
users.push(profile);
|
||||
}
|
||||
|
||||
Ok(users)
|
||||
});
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| match task.await {
|
||||
Ok(users) => {
|
||||
cx.update(|_, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.loading(false, cx);
|
||||
this.result.update(cx, |this, cx| {
|
||||
*this = users;
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(error) => {
|
||||
cx.update(|_, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.loading(false, cx);
|
||||
this.error.update(cx, |this, cx| {
|
||||
*this = Some(error.to_string().into());
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn chat(&mut self, to: Profile, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let public_key = to.public_key();
|
||||
|
||||
let event: Task<Result<Event, anyhow::Error>> = cx.background_spawn(async move {
|
||||
let client = get_client();
|
||||
let signer = client.signer().await?;
|
||||
// [IMPORTANT]
|
||||
// Make sure this event is never send,
|
||||
// this event existed just use for convert to Coop's Room later.
|
||||
let event = EventBuilder::private_msg_rumor(public_key, "")
|
||||
.sign(&signer)
|
||||
.await?;
|
||||
|
||||
Ok(event)
|
||||
});
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
if let Ok(event) = event.await {
|
||||
cx.update(|window, cx| {
|
||||
let chats = ChatRegistry::global(cx);
|
||||
let room = Room::new(&event).kind(RoomKind::Ongoing);
|
||||
|
||||
chats.update(cx, |chats, cx| {
|
||||
match chats.push(room, cx) {
|
||||
Ok(_) => {
|
||||
// TODO: automatically open newly created chat panel
|
||||
window.close_modal(cx);
|
||||
}
|
||||
Err(e) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.error.update(cx, |this, cx| {
|
||||
*this = Some(e.to_string().into());
|
||||
cx.notify();
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn loading(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
self.loading = status;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Search {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let mute_color = cx.theme().base.step(cx, ColorScaleStep::NINE);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.mt_3()
|
||||
.child(
|
||||
div().px_3().child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.child(self.input.clone())
|
||||
.child(
|
||||
Button::new("find")
|
||||
.icon(IconName::Search)
|
||||
.ghost()
|
||||
.disabled(self.loading)
|
||||
.on_click(
|
||||
cx.listener(move |this, _, window, cx| this.search(window, cx)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.when_some(self.error.read(cx).as_ref(), |this, error| {
|
||||
this.child(
|
||||
div()
|
||||
.px_3()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().danger)
|
||||
.child(error.clone()),
|
||||
)
|
||||
})
|
||||
.child(div().map(|this| {
|
||||
let result = self.result.read(cx).clone();
|
||||
|
||||
if self.loading {
|
||||
this.h_32()
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.child(Indicator::new().small())
|
||||
} else if result.is_empty() {
|
||||
this.h_32()
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.text_sm()
|
||||
.text_color(mute_color)
|
||||
.child("No one with that query could be found.")
|
||||
} else {
|
||||
this.child(
|
||||
uniform_list(
|
||||
cx.entity(),
|
||||
"find-result",
|
||||
result.len(),
|
||||
move |_, range, _window, cx| {
|
||||
let mut items = Vec::new();
|
||||
|
||||
for ix in range {
|
||||
let item = result.get(ix).cloned().unwrap();
|
||||
|
||||
items.push(
|
||||
div()
|
||||
.id(ix)
|
||||
.group("")
|
||||
.w_full()
|
||||
.h_12()
|
||||
.px_2()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
img(item.shared_avatar())
|
||||
.size_8()
|
||||
.flex_shrink_0(),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.line_height(relative(1.2))
|
||||
.child(item.shared_name()),
|
||||
)
|
||||
.when_some(
|
||||
item.metadata().nip05,
|
||||
|this, nip05| {
|
||||
this.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(mute_color)
|
||||
.child(nip05),
|
||||
)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.invisible()
|
||||
.group_hover("", |this| this.visible())
|
||||
.child(
|
||||
Button::new(ix)
|
||||
.icon(IconName::ArrowRight)
|
||||
.label("Chat")
|
||||
.xsmall()
|
||||
.primary()
|
||||
.reverse()
|
||||
.on_click(cx.listener(
|
||||
move |this, _, window, cx| {
|
||||
this.chat(
|
||||
item.clone(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
},
|
||||
)),
|
||||
),
|
||||
)
|
||||
.hover(|this| {
|
||||
this.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::THREE))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
items
|
||||
},
|
||||
)
|
||||
.min_h(px(150.)),
|
||||
)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,13 @@ impl RenderOnce for SidebarButton {
|
||||
self.base
|
||||
.id(self.label.clone())
|
||||
.rounded(px(cx.theme().radius))
|
||||
.when_some(self.icon, |this, icon| this.child(icon))
|
||||
.when_some(self.icon, |this, icon| {
|
||||
this.child(
|
||||
div()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(icon),
|
||||
)
|
||||
})
|
||||
.child(self.label.clone())
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)))
|
||||
.on_click(move |ev, window, cx| handler(ev, window, cx))
|
||||
|
||||
@@ -22,7 +22,6 @@ use ui::{
|
||||
panel::{Panel, PanelEvent},
|
||||
},
|
||||
popup_menu::{PopupMenu, PopupMenuExt},
|
||||
scroll::ScrollbarAxis,
|
||||
skeleton::Skeleton,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
IconName, Sizable, StyledExt,
|
||||
@@ -266,26 +265,25 @@ impl Render for Sidebar {
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.font_medium()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(
|
||||
SidebarButton::new("New Message")
|
||||
.icon(IconName::PlusCircleFill)
|
||||
SidebarButton::new("Find")
|
||||
.icon(IconName::Search)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Compose,
|
||||
modal: ModalKind::Search,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
SidebarButton::new("Contacts")
|
||||
.icon(IconName::AddressBook)
|
||||
SidebarButton::new("New Chat")
|
||||
.icon(IconName::PlusCircleFill)
|
||||
.on_click(cx.listener(|_, _, window, cx| {
|
||||
window.dispatch_action(
|
||||
Box::new(ToggleModal {
|
||||
modal: ModalKind::Contact,
|
||||
modal: ModalKind::Compose,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
@@ -313,9 +311,9 @@ impl Render for Sidebar {
|
||||
.tooltip("Toggle chat folders")
|
||||
.map(|this| {
|
||||
if self.split_into_folders {
|
||||
this.icon(IconName::ToggleFill)
|
||||
this.icon(IconName::FilterFill)
|
||||
} else {
|
||||
this.icon(IconName::Toggle)
|
||||
this.icon(IconName::Filter)
|
||||
}
|
||||
})
|
||||
.small()
|
||||
|
||||
Reference in New Issue
Block a user