feat: refactor to use gpui event instead of local state #18
@@ -282,7 +282,7 @@ impl ChatRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get all messages for current user
|
/// Get all messages for current user
|
||||||
fn get_messages(&mut self, cx: &mut Context<Self>) {
|
pub fn get_messages(&mut self, cx: &mut Context<Self>) {
|
||||||
let task = self.subscribe(cx);
|
let task = self.subscribe(cx);
|
||||||
|
|
||||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
|
|||||||
@@ -604,7 +604,10 @@ impl ChatPanel {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
this.set_uploading(false, cx);
|
this.set_uploading(false, cx);
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,7 +655,10 @@ impl ChatPanel {
|
|||||||
})
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
window.push_notification(Notification::error("Failed to change subject"), cx);
|
window.push_notification(
|
||||||
|
Notification::error("Failed to change subject").autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::ChangeSigner(kind) => {
|
Command::ChangeSigner(kind) => {
|
||||||
@@ -663,7 +669,10 @@ impl ChatPanel {
|
|||||||
})
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
window.push_notification(Notification::error("Failed to change signer"), cx);
|
window.push_notification(
|
||||||
|
Notification::error("Failed to change signer").autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::ToggleBackup => {
|
Command::ToggleBackup => {
|
||||||
@@ -674,7 +683,10 @@ impl ChatPanel {
|
|||||||
})
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
window.push_notification(Notification::error("Failed to toggle backup"), cx);
|
window.push_notification(
|
||||||
|
Notification::error("Failed to toggle backup").autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Subject => {
|
Command::Subject => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use gpui::http_client::Url;
|
use gpui::http_client::Url;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString,
|
App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled,
|
||||||
Styled, Window,
|
Window, div, px,
|
||||||
};
|
};
|
||||||
use settings::{AppSettings, AuthMode};
|
use settings::{AppSettings, AuthMode};
|
||||||
use theme::{ActiveTheme, ThemeMode};
|
use theme::{ActiveTheme, ThemeMode};
|
||||||
@@ -11,7 +11,7 @@ use ui::input::{InputState, TextInput};
|
|||||||
use ui::menu::{DropdownMenu, PopupMenuItem};
|
use ui::menu::{DropdownMenu, PopupMenuItem};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::switch::Switch;
|
use ui::switch::Switch;
|
||||||
use ui::{h_flex, v_flex, IconName, Sizable, WindowExtension};
|
use ui::{IconName, Sizable, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Preferences> {
|
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Preferences> {
|
||||||
cx.new(|cx| Preferences::new(window, cx))
|
cx.new(|cx| Preferences::new(window, cx))
|
||||||
@@ -41,7 +41,7 @@ impl Preferences {
|
|||||||
AppSettings::update_file_server(url, cx);
|
AppSettings::update_file_server(url, cx);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(Notification::error(e.to_string()).autohide(false), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,21 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{Context as AnyhowContext, Error};
|
use anyhow::{Context as AnyhowContext, Error};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
||||||
Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Task,
|
Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Task,
|
||||||
Window,
|
Window, div,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::{shorten_pubkey, Person, PersonRegistry};
|
use person::{Person, PersonRegistry, shorten_pubkey};
|
||||||
use settings::AppSettings;
|
use settings::AppSettings;
|
||||||
use state::{upload, NostrRegistry};
|
use state::{NostrRegistry, upload};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::button::{Button, ButtonVariants};
|
use ui::button::{Button, ButtonVariants};
|
||||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||||
use ui::input::{InputState, TextInput};
|
use ui::input::{InputState, TextInput};
|
||||||
use ui::notification::Notification;
|
use ui::notification::Notification;
|
||||||
use ui::{h_flex, v_flex, Disableable, IconName, Sizable, StyledExt, WindowExtension};
|
use ui::{Disableable, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
|
||||||
|
|
||||||
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<ProfilePanel> {
|
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<ProfilePanel> {
|
||||||
cx.new(|cx| ProfilePanel::new(public_key, window, cx))
|
cx.new(|cx| ProfilePanel::new(public_key, window, cx))
|
||||||
@@ -186,7 +186,10 @@ impl ProfilePanel {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
this.update_in(cx, |this, window, cx| {
|
this.update_in(cx, |this, window, cx| {
|
||||||
this.set_uploading(false, cx);
|
this.set_uploading(false, cx);
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,7 +272,10 @@ impl ProfilePanel {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.update(|window, cx| {
|
cx.update(|window, cx| {
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ impl Sidebar {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.update(|window, cx| {
|
cx.update(|window, cx| {
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use ::settings::AppSettings;
|
use ::settings::AppSettings;
|
||||||
use chat::{ChatEvent, ChatRegistry};
|
use chat::{ChatEvent, ChatRegistry};
|
||||||
use device::DeviceRegistry;
|
use device::{DeviceEvent, DeviceRegistry};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
Action, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, ParentElement,
|
Action, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, ParentElement,
|
||||||
@@ -39,6 +39,8 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Workspace> {
|
|||||||
cx.new(|cx| Workspace::new(window, cx))
|
cx.new(|cx| Workspace::new(window, cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RelayNotifcation;
|
||||||
|
|
||||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||||
#[action(namespace = workspace, no_json)]
|
#[action(namespace = workspace, no_json)]
|
||||||
enum Command {
|
enum Command {
|
||||||
@@ -65,15 +67,23 @@ pub struct Workspace {
|
|||||||
/// App's Dock Area
|
/// App's Dock Area
|
||||||
dock: Entity<DockArea>,
|
dock: Entity<DockArea>,
|
||||||
|
|
||||||
|
/// Whether a user's relay list is connected
|
||||||
|
relay_connected: bool,
|
||||||
|
|
||||||
|
/// Whether the inbox is connected
|
||||||
|
inbox_connected: bool,
|
||||||
|
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
_subscriptions: SmallVec<[Subscription; 4]>,
|
_subscriptions: SmallVec<[Subscription; 6]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
|
let chat = ChatRegistry::global(cx);
|
||||||
|
let device = DeviceRegistry::global(cx);
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let npubs = nostr.read(cx).npubs();
|
let npubs = nostr.read(cx).npubs();
|
||||||
let chat = ChatRegistry::global(cx);
|
|
||||||
let titlebar = cx.new(|_| TitleBar::new());
|
let titlebar = cx.new(|_| TitleBar::new());
|
||||||
let dock = cx.new(|cx| DockArea::new(window, cx));
|
let dock = cx.new(|cx| DockArea::new(window, cx));
|
||||||
|
|
||||||
@@ -101,6 +111,7 @@ impl Workspace {
|
|||||||
match event {
|
match event {
|
||||||
StateEvent::Connecting => {
|
StateEvent::Connecting => {
|
||||||
let note = Notification::new()
|
let note = Notification::new()
|
||||||
|
.id::<RelayNotifcation>()
|
||||||
.message("Connecting to the bootstrap relay...")
|
.message("Connecting to the bootstrap relay...")
|
||||||
.title("Relays")
|
.title("Relays")
|
||||||
.icon(IconName::Relay);
|
.icon(IconName::Relay);
|
||||||
@@ -109,6 +120,7 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
StateEvent::Connected => {
|
StateEvent::Connected => {
|
||||||
let note = Notification::new()
|
let note = Notification::new()
|
||||||
|
.id::<RelayNotifcation>()
|
||||||
.message("Connected to the bootstrap relay")
|
.message("Connected to the bootstrap relay")
|
||||||
.title("Relays")
|
.title("Relays")
|
||||||
.with_kind(NotificationKind::Success)
|
.with_kind(NotificationKind::Success)
|
||||||
@@ -120,22 +132,36 @@ impl Workspace {
|
|||||||
this.relay_notification(window, cx);
|
this.relay_notification(window, cx);
|
||||||
}
|
}
|
||||||
StateEvent::RelayConnected => {
|
StateEvent::RelayConnected => {
|
||||||
let note = Notification::new()
|
window.clear_notification::<RelayNotifcation>(cx);
|
||||||
.message("Connected to user's relay list")
|
this.set_relay_connected(true, cx);
|
||||||
.title("Relays")
|
|
||||||
.with_kind(NotificationKind::Success)
|
|
||||||
.icon(IconName::Relay);
|
|
||||||
|
|
||||||
window.push_notification(note, cx);
|
|
||||||
}
|
}
|
||||||
StateEvent::SignerSet => {
|
StateEvent::SignerSet => {
|
||||||
this.set_center_layout(window, cx);
|
this.set_center_layout(window, cx);
|
||||||
|
this.set_relay_connected(false, cx);
|
||||||
|
this.set_inbox_connected(false, cx);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
subscriptions.push(
|
||||||
|
// Observe all events emitted by the device registry
|
||||||
|
cx.subscribe_in(&device, window, |_this, _device, ev, window, cx| {
|
||||||
|
match ev {
|
||||||
|
DeviceEvent::Set => {
|
||||||
|
window.push_notification(
|
||||||
|
Notification::success("Encryption Key has been set"),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
DeviceEvent::Error(error) => {
|
||||||
|
window.push_notification(Notification::error(error).autohide(false), cx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
// Observe all events emitted by the chat registry
|
// Observe all events emitted by the chat registry
|
||||||
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| {
|
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| {
|
||||||
@@ -164,6 +190,12 @@ impl Workspace {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
ChatEvent::Subscribed => {
|
||||||
|
this.set_inbox_connected(true, cx);
|
||||||
|
}
|
||||||
|
ChatEvent::Error(error) => {
|
||||||
|
window.push_notification(Notification::error(error).autohide(false), cx);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
@@ -188,6 +220,8 @@ impl Workspace {
|
|||||||
Self {
|
Self {
|
||||||
titlebar,
|
titlebar,
|
||||||
dock,
|
dock,
|
||||||
|
relay_connected: false,
|
||||||
|
inbox_connected: false,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,6 +253,18 @@ impl Workspace {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set whether the relay list is connected
|
||||||
|
fn set_relay_connected(&mut self, connected: bool, cx: &mut Context<Self>) {
|
||||||
|
self.relay_connected = connected;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether the inbox is connected
|
||||||
|
fn set_inbox_connected(&mut self, connected: bool, cx: &mut Context<Self>) {
|
||||||
|
self.inbox_connected = connected;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the dock layout
|
/// Set the dock layout
|
||||||
fn set_layout(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn set_layout(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let left = DockItem::panel(Arc::new(sidebar::init(window, cx)));
|
let left = DockItem::panel(Arc::new(sidebar::init(window, cx)));
|
||||||
@@ -301,6 +347,12 @@ impl Workspace {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Command::RefreshMessagingRelays => {
|
||||||
|
let chat = ChatRegistry::global(cx);
|
||||||
|
chat.update(cx, |this, cx| {
|
||||||
|
this.get_messages(cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
Command::ShowRelayList => {
|
Command::ShowRelayList => {
|
||||||
self.dock.update(cx, |this, cx| {
|
self.dock.update(cx, |this, cx| {
|
||||||
this.add_panel(
|
this.add_panel(
|
||||||
@@ -311,6 +363,16 @@ impl Workspace {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Command::RefreshRelayList => {
|
||||||
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let signer = nostr.read(cx).signer();
|
||||||
|
|
||||||
|
if let Some(public_key) = signer.public_key() {
|
||||||
|
nostr.update(cx, |this, cx| {
|
||||||
|
this.ensure_relay_list(&public_key, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Command::RefreshEncryption => {
|
Command::RefreshEncryption => {
|
||||||
let device = DeviceRegistry::global(cx);
|
let device = DeviceRegistry::global(cx);
|
||||||
device.update(cx, |this, cx| {
|
device.update(cx, |this, cx| {
|
||||||
@@ -326,7 +388,6 @@ impl Workspace {
|
|||||||
Command::ToggleAccount => {
|
Command::ToggleAccount => {
|
||||||
self.account_selector(window, cx);
|
self.account_selector(window, cx);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,8 +425,10 @@ impl Workspace {
|
|||||||
window.close_modal(cx);
|
window.close_modal(cx);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
window
|
window.push_notification(
|
||||||
.push_notification(Notification::error(e.to_string()), cx);
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
@@ -489,6 +552,7 @@ impl Workspace {
|
|||||||
|
|
||||||
let note = Notification::new()
|
let note = Notification::new()
|
||||||
.autohide(false)
|
.autohide(false)
|
||||||
|
.id::<RelayNotifcation>()
|
||||||
.icon(IconName::Relay)
|
.icon(IconName::Relay)
|
||||||
.title("Gossip Relays are required")
|
.title("Gossip Relays are required")
|
||||||
.content(move |_this, _window, cx| {
|
.content(move |_this, _window, cx| {
|
||||||
@@ -607,6 +671,16 @@ impl Workspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let relay_connected = self.relay_connected;
|
||||||
|
let inbox_connected = self.inbox_connected;
|
||||||
|
|
||||||
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let signer = nostr.read(cx).signer();
|
||||||
|
|
||||||
|
let Some(public_key) = signer.public_key() else {
|
||||||
|
return div();
|
||||||
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
.when(!cx.theme().platform.is_mac(), |this| this.pr_2())
|
||||||
.gap_3()
|
.gap_3()
|
||||||
@@ -621,7 +695,7 @@ impl Workspace {
|
|||||||
let state = device.read(cx).state();
|
let state = device.read(cx).state();
|
||||||
|
|
||||||
this.min_w(px(260.))
|
this.min_w(px(260.))
|
||||||
.item(PopupMenuItem::element(move |_window, _cx| {
|
.item(PopupMenuItem::element(move |_window, cx| {
|
||||||
h_flex()
|
h_flex()
|
||||||
.px_1()
|
.px_1()
|
||||||
.w_full()
|
.w_full()
|
||||||
@@ -633,7 +707,7 @@ impl Workspace {
|
|||||||
.rounded_full()
|
.rounded_full()
|
||||||
.when(state.set(), |this| this.bg(gpui::green()))
|
.when(state.set(), |this| this.bg(gpui::green()))
|
||||||
.when(state.requesting(), |this| {
|
.when(state.requesting(), |this| {
|
||||||
this.bg(gpui::yellow())
|
this.bg(cx.theme().icon_accent)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.child(SharedString::from(state.to_string()))
|
.child(SharedString::from(state.to_string()))
|
||||||
@@ -651,6 +725,83 @@ impl Workspace {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("inbox")
|
||||||
|
.icon(IconName::Inbox)
|
||||||
|
.small()
|
||||||
|
.ghost()
|
||||||
|
.loading(!inbox_connected)
|
||||||
|
.disabled(!inbox_connected)
|
||||||
|
.when(!inbox_connected, |this| {
|
||||||
|
this.tooltip("Connecting to user's messaging relays...")
|
||||||
|
})
|
||||||
|
.when(inbox_connected, |this| this.indicator())
|
||||||
|
.dropdown_menu(move |this, _window, cx| {
|
||||||
|
let persons = PersonRegistry::global(cx);
|
||||||
|
let profile = persons.read(cx).get(&public_key, cx);
|
||||||
|
|
||||||
|
let urls: Vec<SharedString> = profile
|
||||||
|
.messaging_relays()
|
||||||
|
.iter()
|
||||||
|
.map(|url| SharedString::from(url.to_string()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
let menu = this.min_w(px(260.)).label("Messaging Relays");
|
||||||
|
|
||||||
|
// Content
|
||||||
|
let menu = urls.into_iter().fold(menu, |this, url| {
|
||||||
|
this.item(PopupMenuItem::element(move |_window, _cx| {
|
||||||
|
h_flex()
|
||||||
|
.px_1()
|
||||||
|
.w_full()
|
||||||
|
.gap_2()
|
||||||
|
.text_sm()
|
||||||
|
.child(div().size_1p5().rounded_full().bg(gpui::green()))
|
||||||
|
.child(url.clone())
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
// Footer
|
||||||
|
menu.separator()
|
||||||
|
.menu_with_icon(
|
||||||
|
"Reload",
|
||||||
|
IconName::Refresh,
|
||||||
|
Box::new(Command::RefreshMessagingRelays),
|
||||||
|
)
|
||||||
|
.menu_with_icon(
|
||||||
|
"Update relays",
|
||||||
|
IconName::Settings,
|
||||||
|
Box::new(Command::ShowMessaging),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("relay-list")
|
||||||
|
.icon(IconName::Relay)
|
||||||
|
.small()
|
||||||
|
.ghost()
|
||||||
|
.loading(!relay_connected)
|
||||||
|
.disabled(!relay_connected)
|
||||||
|
.when(!relay_connected, |this| {
|
||||||
|
this.tooltip("Connecting to user's relay list...")
|
||||||
|
})
|
||||||
|
.when(relay_connected, |this| this.indicator())
|
||||||
|
.dropdown_menu(move |this, _window, _cx| {
|
||||||
|
this.label("User's Relay List")
|
||||||
|
.separator()
|
||||||
|
.menu_with_icon(
|
||||||
|
"Reload",
|
||||||
|
IconName::Refresh,
|
||||||
|
Box::new(Command::RefreshRelayList),
|
||||||
|
)
|
||||||
|
.menu_with_icon(
|
||||||
|
"Update",
|
||||||
|
IconName::Settings,
|
||||||
|
Box::new(Command::ShowRelayList),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -597,7 +597,10 @@ impl DeviceRegistry {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.update(|window, cx| {
|
cx.update(|window, cx| {
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,7 +288,10 @@ impl RelayAuth {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
window.push_notification(Notification::error(e.to_string()), cx);
|
window.push_notification(
|
||||||
|
Notification::error(e.to_string()).autohide(false),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,9 +29,13 @@ impl NotificationKind {
|
|||||||
fn icon(&self, cx: &App) -> Icon {
|
fn icon(&self, cx: &App) -> Icon {
|
||||||
match self {
|
match self {
|
||||||
Self::Info => Icon::new(IconName::Info).text_color(cx.theme().icon),
|
Self::Info => Icon::new(IconName::Info).text_color(cx.theme().icon),
|
||||||
Self::Success => Icon::new(IconName::CheckCircle).text_color(cx.theme().icon_accent),
|
Self::Warning => Icon::new(IconName::Warning).text_color(cx.theme().warning_foreground),
|
||||||
Self::Warning => Icon::new(IconName::Warning).text_color(cx.theme().warning_active),
|
Self::Success => {
|
||||||
Self::Error => Icon::new(IconName::CloseCircle).text_color(cx.theme().danger_active),
|
Icon::new(IconName::CheckCircle).text_color(cx.theme().secondary_foreground)
|
||||||
|
}
|
||||||
|
Self::Error => {
|
||||||
|
Icon::new(IconName::CloseCircle).text_color(cx.theme().danger_foreground)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,6 +286,9 @@ impl Styled for Notification {
|
|||||||
}
|
}
|
||||||
impl Render for Notification {
|
impl Render for Notification {
|
||||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let closing = self.closing;
|
||||||
|
let placement = cx.theme().notification.placement;
|
||||||
|
|
||||||
let content = self
|
let content = self
|
||||||
.content_builder
|
.content_builder
|
||||||
.clone()
|
.clone()
|
||||||
@@ -297,9 +304,15 @@ impl Render for Notification {
|
|||||||
Some(kind) => Some(kind.icon(cx)),
|
Some(kind) => Some(kind.icon(cx)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let has_icon = icon.is_some();
|
let background = match self.kind {
|
||||||
let closing = self.closing;
|
Some(NotificationKind::Error) => cx.theme().danger_background,
|
||||||
let placement = cx.theme().notification.placement;
|
_ => cx.theme().surface_background,
|
||||||
|
};
|
||||||
|
|
||||||
|
let text_color = match self.kind {
|
||||||
|
Some(NotificationKind::Error) => cx.theme().danger_foreground,
|
||||||
|
_ => cx.theme().text,
|
||||||
|
};
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("notification")
|
.id("notification")
|
||||||
@@ -309,7 +322,8 @@ impl Render for Notification {
|
|||||||
.w_112()
|
.w_112()
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
.bg(cx.theme().surface_background)
|
.bg(background)
|
||||||
|
.text_color(text_color)
|
||||||
.rounded(cx.theme().radius_lg)
|
.rounded(cx.theme().radius_lg)
|
||||||
.when(cx.theme().shadow, |this| this.shadow_md())
|
.when(cx.theme().shadow, |this| this.shadow_md())
|
||||||
.p_2()
|
.p_2()
|
||||||
@@ -318,22 +332,23 @@ impl Render for Notification {
|
|||||||
.items_start()
|
.items_start()
|
||||||
.refine_style(&self.style)
|
.refine_style(&self.style)
|
||||||
.when_some(icon, |this, icon| {
|
.when_some(icon, |this, icon| {
|
||||||
this.child(div().flex_shrink_0().pt_1().child(icon))
|
this.child(div().flex_shrink_0().pt(px(3.)).child(icon))
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.when(has_icon, |this| this.pl_1())
|
|
||||||
.when_some(self.title.clone(), |this, title| {
|
.when_some(self.title.clone(), |this, title| {
|
||||||
this.child(div().text_sm().font_semibold().child(title))
|
this.child(div().text_sm().font_semibold().child(title))
|
||||||
})
|
})
|
||||||
.when_some(self.message.clone(), |this, message| {
|
.when_some(self.message.clone(), |this, message| {
|
||||||
this.child(div().text_sm().child(message))
|
this.child(div().text_sm().child(message))
|
||||||
})
|
})
|
||||||
.when_some(content, |this, content| this.child(content)),
|
.when_some(content, |this, content| this.child(content))
|
||||||
|
.when_some(action, |this, action| {
|
||||||
|
this.child(h_flex().flex_1().gap_1().justify_end().child(action))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.when_some(action, |this, action| this.child(action))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
|
|||||||
Reference in New Issue
Block a user