chore: improve web support (#36)
Reviewed-on: #36
This commit was merged in pull request #36.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Task, Window, div,
|
||||
};
|
||||
use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use state::USER_KEYRING;
|
||||
use theme::ActiveTheme;
|
||||
@@ -155,12 +154,7 @@ impl Render for BackupPanel {
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Public Key:")),
|
||||
)
|
||||
.child(
|
||||
Input::new(&self.npub_input)
|
||||
.small()
|
||||
.bordered(false)
|
||||
.disabled(true),
|
||||
),
|
||||
.child(Input::new(&self.npub_input).small().disabled(true)),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -173,12 +167,7 @@ impl Render for BackupPanel {
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Secret Key:")),
|
||||
)
|
||||
.child(
|
||||
Input::new(&self.nsec_input)
|
||||
.small()
|
||||
.bordered(false)
|
||||
.disabled(true),
|
||||
),
|
||||
.child(Input::new(&self.nsec_input).small().disabled(true)),
|
||||
)
|
||||
.child(
|
||||
Button::new("copy")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
@@ -8,6 +7,7 @@ use gpui::{
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, TextAlign, Window, div, rems,
|
||||
};
|
||||
use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
@@ -17,6 +17,7 @@ use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock::{Panel, PanelEvent};
|
||||
use ui::input::{Input, InputEvent, InputState};
|
||||
use ui::scroll::ScrollableElement;
|
||||
use ui::{Disableable, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex};
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<ContactListPanel> {
|
||||
@@ -220,8 +221,7 @@ impl ContactListPanel {
|
||||
.px_2()
|
||||
.justify_between()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().secondary_background)
|
||||
.text_color(cx.theme().secondary_foreground)
|
||||
.hover(|this| this.bg(cx.theme().ghost_element_hover))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
@@ -283,79 +283,78 @@ impl Focusable for ContactListPanel {
|
||||
|
||||
impl Render for ContactListPanel {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex().p_3().gap_3().w_full().child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("New contact:")),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
Input::new(&self.input)
|
||||
.small()
|
||||
.bordered(false)
|
||||
.cleanable(true),
|
||||
)
|
||||
.child(
|
||||
Button::new("add")
|
||||
.icon(IconName::Plus)
|
||||
.tooltip("Add contact")
|
||||
.ghost()
|
||||
.size(rems(2.))
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.add(window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.when_some(self.error.as_ref(), |this, error| {
|
||||
this.child(
|
||||
div()
|
||||
.italic()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().text_danger)
|
||||
.child(error.clone()),
|
||||
v_flex()
|
||||
.p_3()
|
||||
.gap_3()
|
||||
.w_full()
|
||||
.overflow_y_scrollbar()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("New contact:"),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(Input::new(&self.input).small().cleanable(true))
|
||||
.child(
|
||||
Button::new("add")
|
||||
.icon(IconName::Plus)
|
||||
.tooltip("Add contact")
|
||||
.ghost()
|
||||
.size(rems(2.))
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.add(window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.map(|this| {
|
||||
if self.contacts.is_empty() {
|
||||
this.child(self.render_empty(window, cx))
|
||||
} else {
|
||||
this.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.children(self.render_list_items(cx)),
|
||||
)
|
||||
}
|
||||
})
|
||||
.child(
|
||||
Button::new("submit")
|
||||
.icon(IconName::CheckCircle)
|
||||
.label("Update")
|
||||
.primary()
|
||||
.small()
|
||||
.font_semibold()
|
||||
.loading(self.updating)
|
||||
.disabled(self.updating)
|
||||
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||
this.update(window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.when_some(self.error.as_ref(), |this, error| {
|
||||
this.child(
|
||||
div()
|
||||
.italic()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().text_danger)
|
||||
.child(error.clone()),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.map(|this| {
|
||||
if self.contacts.is_empty() {
|
||||
this.child(self.render_empty(window, cx))
|
||||
} else {
|
||||
this.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.children(self.render_list_items(cx)),
|
||||
)
|
||||
}
|
||||
})
|
||||
.child(
|
||||
Button::new("submit")
|
||||
.icon(IconName::CheckCircle)
|
||||
.label("Update")
|
||||
.primary()
|
||||
.font_semibold()
|
||||
.loading(self.updating)
|
||||
.disabled(self.updating)
|
||||
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||
this.update(window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use anyhow::Error;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
IntoElement, ParentElement, Render, SharedString, Styled, Window, div, svg,
|
||||
IntoElement, ParentElement, Render, SharedString, Styled, Task, Window, div, svg,
|
||||
};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
@@ -8,8 +9,8 @@ use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock::{DockPlacement, Panel, PanelEvent};
|
||||
use ui::{Icon, IconName, Sizable, StyledExt, h_flex, v_flex};
|
||||
|
||||
use crate::Workspace;
|
||||
use crate::panels::profile;
|
||||
use crate::{Command, Workspace};
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<GreeterPanel> {
|
||||
cx.new(|cx| GreeterPanel::new(window, cx))
|
||||
@@ -18,6 +19,7 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<GreeterPanel> {
|
||||
pub struct GreeterPanel {
|
||||
name: SharedString,
|
||||
focus_handle: FocusHandle,
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
}
|
||||
|
||||
impl GreeterPanel {
|
||||
@@ -25,6 +27,7 @@ impl GreeterPanel {
|
||||
Self {
|
||||
name: "Onboarding".into(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
tasks: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +35,7 @@ impl GreeterPanel {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
if let Some(public_key) = nostr.read(cx).current_user() {
|
||||
cx.spawn_in(window, async move |_this, cx| {
|
||||
self.tasks.push(cx.spawn_in(window, async move |_this, cx| {
|
||||
cx.update(|window, cx| {
|
||||
Workspace::add_panel(
|
||||
profile::init(public_key, window, cx),
|
||||
@@ -42,8 +45,9 @@ impl GreeterPanel {
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,17 +146,20 @@ impl Render for GreeterPanel {
|
||||
.ghost()
|
||||
.small()
|
||||
.justify_start()
|
||||
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.add_profile_panel(window, cx)
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("invite")
|
||||
.icon(Icon::new(IconName::Invite))
|
||||
.label("Invite friends")
|
||||
Button::new("theme")
|
||||
.icon(Icon::new(IconName::Moon))
|
||||
.label("Change theme")
|
||||
.ghost()
|
||||
.small()
|
||||
.justify_start(),
|
||||
.justify_start()
|
||||
.on_click(cx.listener(move |_, _, _, cx| {
|
||||
cx.dispatch_action(&Command::ToggleTheme);
|
||||
})),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Error, anyhow};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
@@ -8,6 +7,7 @@ use gpui::{
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, TextAlign, Window, div, rems,
|
||||
};
|
||||
use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use state::NostrRegistry;
|
||||
@@ -320,12 +320,7 @@ impl Render for MessagingRelayPanel {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
Input::new(&self.input)
|
||||
.small()
|
||||
.bordered(false)
|
||||
.cleanable(true),
|
||||
)
|
||||
.child(Input::new(&self.input).small().cleanable(true))
|
||||
.child(
|
||||
Button::new("add")
|
||||
.icon(IconName::Plus)
|
||||
@@ -365,7 +360,6 @@ impl Render for MessagingRelayPanel {
|
||||
.icon(IconName::CheckCircle)
|
||||
.label("Update")
|
||||
.primary()
|
||||
.small()
|
||||
.font_semibold()
|
||||
.loading(self.updating)
|
||||
.disabled(self.updating)
|
||||
|
||||
@@ -4,4 +4,3 @@ pub mod greeter;
|
||||
pub mod messaging_relays;
|
||||
pub mod profile;
|
||||
pub mod relay_list;
|
||||
pub mod trash;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as AnyhowContext, Error};
|
||||
use gpui::{
|
||||
@@ -7,6 +6,7 @@ use gpui::{
|
||||
Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString, Styled, Task,
|
||||
Window, div,
|
||||
};
|
||||
use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Person, PersonRegistry, shorten_pubkey};
|
||||
use settings::AppSettings;
|
||||
@@ -132,7 +132,7 @@ impl ProfilePanel {
|
||||
cx.notify();
|
||||
|
||||
if status {
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(2)).await;
|
||||
|
||||
// Reset the copied state after a delay
|
||||
@@ -143,8 +143,9 @@ impl ProfilePanel {
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +353,7 @@ impl Render for ProfilePanel {
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("What should people call you?")),
|
||||
)
|
||||
.child(Input::new(&self.name_input).bordered(false).small()),
|
||||
.child(Input::new(&self.name_input).small()),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -363,7 +364,7 @@ impl Render for ProfilePanel {
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("A short introduction about you:")),
|
||||
)
|
||||
.child(Input::new(&self.bio_input).bordered(false).small()),
|
||||
.child(Input::new(&self.bio_input).small()),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -374,7 +375,7 @@ impl Render for ProfilePanel {
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Website:")),
|
||||
)
|
||||
.child(Input::new(&self.website_input).bordered(false).small()),
|
||||
.child(Input::new(&self.website_input).small()),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
@@ -418,7 +419,6 @@ impl Render for ProfilePanel {
|
||||
.icon(IconName::CheckCircle)
|
||||
.label("Update")
|
||||
.primary()
|
||||
.small()
|
||||
.font_semibold()
|
||||
.loading(self.updating)
|
||||
.disabled(self.updating)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Error, anyhow};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
@@ -8,6 +7,7 @@ use gpui::{
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, TextAlign, Window, div, px, rems,
|
||||
};
|
||||
use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
@@ -216,7 +216,7 @@ impl RelayListPanel {
|
||||
self.set_updating(true, cx);
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let event = EventBuilder::relay_list(relays)
|
||||
let event = nip65::RelayList::new(relays)
|
||||
.finalize_async(&signer)
|
||||
.await?;
|
||||
|
||||
@@ -371,12 +371,7 @@ impl Render for RelayListPanel {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
Input::new(&self.input)
|
||||
.small()
|
||||
.bordered(false)
|
||||
.cleanable(true),
|
||||
)
|
||||
.child(Input::new(&self.input).small().cleanable(true))
|
||||
.child(
|
||||
Button::new("metadata")
|
||||
.map(|this| {
|
||||
@@ -434,7 +429,6 @@ impl Render for RelayListPanel {
|
||||
.icon(IconName::CheckCircle)
|
||||
.label("Update")
|
||||
.primary()
|
||||
.small()
|
||||
.font_semibold()
|
||||
.loading(self.updating)
|
||||
.disabled(self.updating)
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
use chat::ChatRegistry;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ParentElement, Render,
|
||||
SharedString, Styled, Window, div, list, px, relative,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock::{Panel, PanelEvent};
|
||||
use ui::scroll::Scrollbar;
|
||||
use ui::{Icon, IconName, Sizable, h_flex, v_flex};
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<TrashPanel> {
|
||||
cx.new(|cx| TrashPanel::new(window, cx))
|
||||
}
|
||||
|
||||
pub struct TrashPanel {
|
||||
name: SharedString,
|
||||
focus_handle: FocusHandle,
|
||||
|
||||
/// List state for messages
|
||||
list_state: ListState,
|
||||
}
|
||||
|
||||
impl TrashPanel {
|
||||
fn new(_window: &mut Window, cx: &mut App) -> Self {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let count = chat.read(cx).count_trash_messages(cx);
|
||||
let list_state = ListState::new(count, ListAlignment::Bottom, px(1024.));
|
||||
|
||||
Self {
|
||||
name: "Trash".into(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
list_state,
|
||||
}
|
||||
}
|
||||
|
||||
fn copy(&self, ix: usize, cx: &App) {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let trashes = chat.read(cx).trashes();
|
||||
|
||||
if let Some(message) = trashes.read(cx).iter().nth(ix) {
|
||||
let item = ClipboardItem::new_string(message.raw_event.to_string());
|
||||
cx.write_to_clipboard(item);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_list_item(
|
||||
&mut self,
|
||||
ix: usize,
|
||||
_window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let trashes = chat.read(cx).trashes();
|
||||
|
||||
if let Some(message) = trashes.read(cx).iter().nth(ix) {
|
||||
v_flex()
|
||||
.id(ix)
|
||||
.p_2()
|
||||
.w_full()
|
||||
.child(
|
||||
v_flex()
|
||||
.p_2()
|
||||
.w_full()
|
||||
.gap_1()
|
||||
.rounded(cx.theme().radius_lg)
|
||||
.bg(cx.theme().surface_background)
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().text_danger)
|
||||
.child(message.reason.clone()),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_10()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.justify_between()
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
.rounded(cx.theme().radius)
|
||||
.child(
|
||||
div()
|
||||
.truncate()
|
||||
.text_ellipsis()
|
||||
.text_xs()
|
||||
.line_height(relative(1.))
|
||||
.child(message.raw_event.clone()),
|
||||
)
|
||||
.child(
|
||||
Button::new(format!("copy-{ix}"))
|
||||
.icon(IconName::Copy)
|
||||
.ghost()
|
||||
.small()
|
||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||
this.copy(ix, cx);
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
} else {
|
||||
div().id(ix).into_any_element()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for TrashPanel {
|
||||
fn panel_id(&self) -> SharedString {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
fn title(&self, _cx: &App) -> AnyElement {
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.child(Icon::new(IconName::Warning).small())
|
||||
.child("Errors")
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for TrashPanel {}
|
||||
|
||||
impl Focusable for TrashPanel {
|
||||
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TrashPanel {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex().size_full().relative().child(
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.relative()
|
||||
.child(
|
||||
list(
|
||||
self.list_state.clone(),
|
||||
cx.processor(move |this, ix, window, cx| {
|
||||
this.render_list_item(ix, window, cx)
|
||||
}),
|
||||
)
|
||||
.size_full(),
|
||||
)
|
||||
.child(Scrollbar::vertical(&self.list_state)),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user