This commit is contained in:
2026-09-20 08:03:47 +07:00
parent 564382756d
commit 974f8302a1
6 changed files with 218 additions and 13 deletions
+2
View File
@@ -1,3 +1,5 @@
pub mod import;
pub mod new_chat;
pub mod new_community;
pub mod restore;
pub mod settings;
+118
View File
@@ -0,0 +1,118 @@
use chat::{ChatRegistry, Room, RoomKind};
use gpui::prelude::FluentBuilder;
use gpui::{
App, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled,
Subscription, Window, div, px,
};
use nostr_sdk::prelude::*;
use state::NostrRegistry;
use theme::ActiveTheme;
use ui::button::{Button, ButtonVariants};
use ui::input::{Input, InputEvent, InputState};
use ui::{StyledExt, WindowExtension, v_flex};
pub fn open(window: &mut Window, cx: &mut App) {
let view = cx.new(|cx| NewChat::new(window, cx));
window.open_modal(cx, move |this, _window, _cx| {
this.width(px(420.)).title("New chat").child(view.clone())
});
}
pub struct NewChat {
/// Public key input
input: Entity<InputState>,
/// Error message
error: Option<SharedString>,
/// Input subscription
_subscription: Option<Subscription>,
}
impl NewChat {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let input = cx.new(|cx| InputState::new(window, cx).placeholder("npub"));
let subscription = cx.subscribe_in(&input, window, |this, _input, event, window, cx| {
if let InputEvent::PressEnter { .. } = event {
this.start_chat(window, cx);
}
});
Self {
input,
error: None,
_subscription: Some(subscription),
}
}
fn start_chat(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let value = self.input.read(cx).value().to_string();
let Ok(peer) = PublicKey::parse(&value) else {
self.set_error("Public key is invalid", cx);
return;
};
let nostr = NostrRegistry::global(cx);
let Some(current_user) = nostr.read(cx).current_user() else {
self.set_error("You are not signed in", cx);
return;
};
if peer == current_user {
self.set_error("You cannot chat with yourself", cx);
return;
}
let room = Room::new(current_user, [peer])
.organize(&current_user)
.kind(RoomKind::Ongoing);
let chat = ChatRegistry::global(cx);
chat.update(cx, |chat, cx| {
let room = cx.new(|_| room);
chat.emit_room(&room, window, cx);
});
window.close_modal(cx);
}
fn set_error(&mut self, message: impl Into<SharedString>, cx: &mut Context<Self>) {
self.error = Some(message.into());
cx.notify();
}
}
impl Render for NewChat {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.gap_4()
.child(
v_flex()
.gap_1()
.text_color(cx.theme().text_muted)
.child("Public key of the person you want to chat with")
.child(Input::new(&self.input)),
)
.child(
Button::new("start-chat")
.label("Start chat")
.primary()
.font_semibold()
.on_click(cx.listener(|this, _event, window, cx| {
this.start_chat(window, cx);
})),
)
.when_some(self.error.clone(), |this, error| {
this.child(
div()
.text_xs()
.text_center()
.text_color(cx.theme().text_danger)
.child(error),
)
})
}
}
@@ -0,0 +1,34 @@
use community::{CommunityMetadata, CommunityRegistry};
use gpui::{App, AppContext, ParentElement, Window, px};
use ui::WindowExtension;
use ui::input::{Input, InputState};
pub fn open(window: &mut Window, cx: &mut App) {
let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Community name"));
window.open_modal(cx, move |this, _window, _cx| {
let name_input = name_input.clone();
this.width(px(380.))
.confirm()
.title("New community")
.child(Input::new(&name_input))
.on_ok(move |_event, _window, cx| {
let name = name_input.read(cx).value().trim().to_owned();
if name.is_empty() {
return false;
}
let metadata = CommunityMetadata {
name,
..CommunityMetadata::default()
};
CommunityRegistry::global(cx)
.update(cx, |registry, cx| registry.create(metadata, cx));
true
})
});
}
+9 -1
View File
@@ -26,7 +26,7 @@ use ui::{Icon, IconName, Root, Sizable, WindowExtension, h_flex, v_flex};
use crate::dialogs::import::ImportIdentity;
use crate::dialogs::restore::RestoreEncryption;
use crate::dialogs::settings;
use crate::dialogs::{new_chat, new_community, settings};
use crate::panels::{
backup, browse, contact_list, greeter, inbox, messaging_relays, profile, relay_list, requests,
search,
@@ -64,6 +64,8 @@ enum Command {
ShowRequests,
ShowBrowse,
ShowSearch,
NewChat,
NewCommunity,
}
pub struct Workspace {
@@ -324,6 +326,12 @@ impl Workspace {
Command::ShowSearch => {
self.add_panel_to_dock(search::init(window, cx), DockPlacement::Center, window, cx);
}
Command::NewChat => {
new_chat::open(window, cx);
}
Command::NewCommunity => {
new_community::open(window, cx);
}
Command::ShowBackup => {
self.add_panel_to_dock(backup::init(window, cx), DockPlacement::Left, window, cx);
}
+42 -11
View File
@@ -22,10 +22,11 @@ use ui::dock::{Panel, PanelEvent};
use ui::indicator::Indicator;
use ui::menu::{DropdownMenu, PopupMenuItem};
use ui::nav_item::NavItem;
use ui::notification::Notification;
use ui::scroll::Scrollbar;
use ui::{
Icon, IconName, Sizable, StyledExt, TRAFFIC_LIGHT_PADDING, h_flex, title_bar_drag_handlers,
v_flex,
Icon, IconName, Sizable, StyledExt, TRAFFIC_LIGHT_PADDING, WindowExtension, h_flex,
title_bar_drag_handlers, v_flex,
};
use crate::Command;
@@ -62,13 +63,16 @@ impl Sidebar {
}),
);
subscriptions.push(
cx.subscribe(&communities, |_this, _communities, event, _cx| {
subscriptions.push(cx.subscribe_in(
&communities,
window,
|_this, _communities, event, window, cx| {
if let CommunityEvent::Error(error) = event {
log::error!("community: {error}");
window
.push_notification(Notification::error(error.clone()).autohide(false), cx);
}
}),
);
},
));
Self {
focus_handle: cx.focus_handle(),
@@ -456,7 +460,7 @@ impl Render for Sidebar {
.border_r_1()
.border_color(cx.theme().border_variant)
.child(self.render_user(window, cx))
.when(active_tab == SidebarTab::Chats, |this| {
.when(active_tab.chat(), |this| {
this.child(
v_flex()
.px_2()
@@ -492,16 +496,43 @@ impl Render for Sidebar {
window.dispatch_action(Box::new(Command::ShowRequests), cx);
}
}),
)
.child(
NavItem::new(
"nav-new-chat",
"New chat",
Icon::new(IconName::Plus).small(),
)
.on_click(|_event, window, cx| {
window.dispatch_action(Box::new(Command::NewChat), cx)
}),
),
)
})
.when(active_tab == SidebarTab::Communities, |this| {
.when(active_tab.community(), |this| {
this.child(
v_flex().px_2().gap_1().child(
NavItem::new("nav-browse", "Browse", Icon::new(IconName::Compass).small())
v_flex()
.px_2()
.gap_1()
.child(
NavItem::new(
"nav-browse",
"Browse",
Icon::new(IconName::Compass).small(),
)
.on_click(|_event, window, cx| {
window.dispatch_action(Box::new(Command::ShowBrowse), cx)
}),
)
.child(
NavItem::new(
"nav-new-community",
"New community",
Icon::new(IconName::Plus).small(),
)
.on_click(|_event, window, cx| {
window.dispatch_action(Box::new(Command::NewCommunity), cx)
}),
),
)
})
+12
View File
@@ -47,6 +47,18 @@ impl SidebarTab {
Self::Communities => 2,
}
}
pub fn recents(self) -> bool {
matches!(self, Self::Recents)
}
pub fn chat(self) -> bool {
matches!(self, Self::Chats)
}
pub fn community(self) -> bool {
matches!(self, Self::Communities)
}
}
#[derive(IntoElement)]