From 3fea18f038a8f3d63e966b08ab87c97fe508b390 Mon Sep 17 00:00:00 2001 From: reya Date: Sun, 4 May 2025 10:12:15 +0700 Subject: [PATCH] feat: automatically open the chat room --- crates/chats/src/lib.rs | 22 ++++++------ crates/coop/src/views/chat.rs | 44 ++++++++++++----------- crates/coop/src/views/compose.rs | 46 ++++++++++-------------- crates/coop/src/views/relays.rs | 2 ++ crates/coop/src/views/search.rs | 47 ++++++++++++------------- crates/coop/src/views/sidebar/folder.rs | 1 + crates/coop/src/views/sidebar/mod.rs | 45 +++++++++++------------ crates/coop/src/views/subject.rs | 2 ++ crates/ui/src/modal.rs | 6 ++-- 9 files changed, 105 insertions(+), 110 deletions(-) diff --git a/crates/chats/src/lib.rs b/crates/chats/src/lib.rs index 40e9fce..b0fea62 100644 --- a/crates/chats/src/lib.rs +++ b/crates/chats/src/lib.rs @@ -1,6 +1,6 @@ use std::{cmp::Reverse, collections::HashMap}; -use anyhow::{anyhow, Error}; +use anyhow::Error; use common::room_hash; use global::get_client; use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window}; @@ -8,6 +8,7 @@ use itertools::Itertools; use nostr_sdk::prelude::*; use room::RoomKind; use smallvec::{smallvec, SmallVec}; +use ui::ContextModal; use crate::room::Room; @@ -261,21 +262,18 @@ impl ChatRegistry { /// Add a new room to the registry /// /// Returns an error if the room already exists - pub fn push(&mut self, room: Room, cx: &mut Context) -> Result<(), anyhow::Error> { - let room = cx.new(|_| room); + pub fn push(&mut self, event: &Event, window: &mut Window, cx: &mut Context) -> u64 { + let room = Room::new(event).kind(RoomKind::Ongoing); + let id = room.id; - if !self - .rooms - .iter() - .any(|current| current.read(cx) == room.read(cx)) - { - self.rooms.insert(0, room); + if !self.rooms.iter().any(|r| r.read(cx) == &room) { + self.rooms.insert(0, cx.new(|_| room)); cx.notify(); - - Ok(()) } else { - Err(anyhow!("Room already exists")) + window.push_notification("Room already exists", cx); } + + id } /// Push a new message to a room diff --git a/crates/coop/src/views/chat.rs b/crates/coop/src/views/chat.rs index 37b416e..1ae965f 100644 --- a/crates/coop/src/views/chat.rs +++ b/crates/coop/src/views/chat.rs @@ -28,7 +28,7 @@ use ui::{ popup_menu::PopupMenu, text::RichText, theme::{scale::ColorScaleStep, ActiveTheme}, - v_flex, ContextModal, Disableable, Icon, IconName, Size, StyledExt, + v_flex, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; use crate::views::subject; @@ -473,31 +473,33 @@ impl Panel for Chat { fn title(&self, cx: &App) -> AnyElement { self.room.read_with(cx, |this, _| { - let facepill: Vec = this.avatars(cx); + let label = this.display_name(cx); + let url = this.display_image(cx); div() .flex() .items_center() .gap_1p5() - .child( - div() - .flex() - .flex_row_reverse() - .items_center() - .justify_start() - .children( - facepill - .into_iter() - .enumerate() - .rev() - .map(|(ix, facepill)| { - div() - .when(ix > 0, |div| div.ml_neg_1()) - .child(img(facepill).size_5()) - }), - ), - ) - .child(this.display_name(cx)) + .map(|this| { + if let Some(url) = url { + this.child(img(url).size_5().flex_shrink_0()) + } else { + this.child( + div() + .flex_shrink_0() + .flex() + .justify_center() + .items_center() + .size_5() + .rounded_full() + .bg(cx.theme().accent.step(cx, ColorScaleStep::THREE)) + .child(Icon::new(IconName::UsersThreeFill).xsmall().text_color( + cx.theme().accent.step(cx, ColorScaleStep::TWELVE), + )), + ) + } + }) + .child(label) .into_any() }) } diff --git a/crates/coop/src/views/compose.rs b/crates/coop/src/views/compose.rs index 9c63351..87759e8 100644 --- a/crates/coop/src/views/compose.rs +++ b/crates/coop/src/views/compose.rs @@ -1,8 +1,5 @@ use anyhow::Error; -use chats::{ - room::{Room, RoomKind}, - ChatRegistry, -}; +use chats::ChatRegistry; use common::{profile::SharedProfile, random_name}; use global::get_client; use gpui::{ @@ -21,11 +18,14 @@ use std::{ }; use ui::{ button::{Button, ButtonVariants}, + dock_area::dock::DockPlacement, input::{InputEvent, TextInput}, theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; +use crate::chatspace::{AddPanel, PanelKind}; + pub fn init(window: &mut Window, cx: &mut App) -> Entity { cx.new(|cx| Compose::new(window, cx)) } @@ -161,34 +161,26 @@ impl Compose { Ok(event) }); - cx.spawn_in(window, async move |this, cx| { - if let Ok(event) = event.await { + cx.spawn_in(window, async move |this, cx| match event.await { + Ok(event) => { cx.update(|window, cx| { - this.update(cx, |this, cx| { - this.set_submitting(false, cx); - }) - .ok(); - - 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.set_error(Some(e.to_string().into()), cx); - }) - .ok(); - } - } + ChatRegistry::global(cx).update(cx, |chats, cx| { + let id = chats.push(&event, window, cx); + window.close_modal(cx); + window.dispatch_action( + Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)), + cx, + ); }); }) .ok(); } + Err(e) => { + this.update(cx, |this, cx| { + this.set_error(Some(e.to_string().into()), cx); + }) + .ok(); + } }) .detach(); } diff --git a/crates/coop/src/views/relays.rs b/crates/coop/src/views/relays.rs index ffc18be..5accf9e 100644 --- a/crates/coop/src/views/relays.rs +++ b/crates/coop/src/views/relays.rs @@ -284,6 +284,8 @@ impl Render for Relays { div() .track_focus(&self.focus_handle) .size_full() + .px_3() + .pb_3() .flex() .flex_col() .justify_between() diff --git a/crates/coop/src/views/search.rs b/crates/coop/src/views/search.rs index d272c48..9ffea14 100644 --- a/crates/coop/src/views/search.rs +++ b/crates/coop/src/views/search.rs @@ -2,10 +2,7 @@ use std::time::Duration; use anyhow::Error; use async_utility::task::spawn; -use chats::{ - room::{Room, RoomKind}, - ChatRegistry, -}; +use chats::ChatRegistry; use common::profile::SharedProfile; use global::{constants::SEARCH_RELAYS, get_client}; use gpui::{ @@ -18,12 +15,15 @@ use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; use ui::{ button::{Button, ButtonVariants}, + dock_area::dock::DockPlacement, indicator::Indicator, input::{InputEvent, TextInput}, theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, IconName, Sizable, }; +use crate::chatspace::{AddPanel, PanelKind}; + pub fn init(window: &mut Window, cx: &mut App) -> Entity { Search::new(window, cx) } @@ -167,28 +167,25 @@ impl Search { Ok(event) }); - cx.spawn_in(window, async move |this, cx| { - if let Ok(event) = event.await { + cx.spawn_in(window, async move |this, cx| match event.await { + Ok(event) => { 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(); - } - } + ChatRegistry::global(cx).update(cx, |chats, cx| { + let id = chats.push(&event, window, cx); + window.close_modal(cx); + window.dispatch_action( + Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)), + cx, + ); + }); + }) + .ok(); + } + Err(e) => { + this.update(cx, |this, cx| { + this.error.update(cx, |this, cx| { + *this = Some(e.to_string().into()); + cx.notify(); }); }) .ok(); diff --git a/crates/coop/src/views/sidebar/folder.rs b/crates/coop/src/views/sidebar/folder.rs index 2906451..599fc50 100644 --- a/crates/coop/src/views/sidebar/folder.rs +++ b/crates/coop/src/views/sidebar/folder.rs @@ -306,6 +306,7 @@ impl RenderOnce for FolderItem { } else { this.child( div() + .flex_shrink_0() .flex() .justify_center() .items_center() diff --git a/crates/coop/src/views/sidebar/mod.rs b/crates/coop/src/views/sidebar/mod.rs index a1fc509..739dac5 100644 --- a/crates/coop/src/views/sidebar/mod.rs +++ b/crates/coop/src/views/sidebar/mod.rs @@ -104,6 +104,28 @@ impl Sidebar { cx.notify(); } + fn on_logout(&mut self, _: &Logout, window: &mut Window, cx: &mut Context) { + let task: Task> = cx.background_spawn(async move { + let client = get_client(); + _ = client.reset().await; + + Ok(()) + }); + + cx.spawn_in(window, async move |_, cx| { + if task.await.is_ok() { + cx.update(|_, cx| { + Account::global(cx).update(cx, |this, cx| { + this.profile = None; + cx.notify(); + }); + }) + .ok(); + }; + }) + .detach(); + } + fn render_skeleton(&self, total: i32) -> impl IntoIterator { (0..total).map(|_| { div() @@ -146,28 +168,6 @@ impl Sidebar { items } - - fn on_logout(&mut self, _: &Logout, window: &mut Window, cx: &mut Context) { - let task: Task> = cx.background_spawn(async move { - let client = get_client(); - _ = client.reset().await; - - Ok(()) - }); - - cx.spawn_in(window, async move |_, cx| { - if task.await.is_ok() { - cx.update(|_, cx| { - Account::global(cx).update(cx, |this, cx| { - this.profile = None; - cx.notify(); - }); - }) - .ok(); - }; - }) - .detach(); - } } impl Panel for Sidebar { @@ -206,6 +206,7 @@ impl Render for Sidebar { div() .id("sidebar") + .track_focus(&self.focus_handle) .track_scroll(&self.scroll_handle) .on_action(cx.listener(Self::on_logout)) .overflow_y_scroll() diff --git a/crates/coop/src/views/subject.rs b/crates/coop/src/views/subject.rs index 51f4bda..ade93a9 100644 --- a/crates/coop/src/views/subject.rs +++ b/crates/coop/src/views/subject.rs @@ -80,6 +80,8 @@ impl Render for Subject { .flex() .flex_col() .gap_3() + .px_3() + .pb_3() .child( div() .flex() diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index 6096818..3465ffb 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -47,7 +47,7 @@ impl Modal { let base = v_flex() .bg(cx.theme().background) .border_1() - .border_color(cx.theme().base.step(cx, ColorScaleStep::SEVEN)) + .border_color(cx.theme().base.step(cx, ColorScaleStep::SIX)) .rounded_xl() .shadow_md(); @@ -169,9 +169,9 @@ impl RenderOnce for Modal { .w(view_size.width) .h(view_size.height) .when(self.overlay, |this| { - this.bg(cx.theme().base.step_alpha(cx, ColorScaleStep::EIGHT)) + this.bg(cx.theme().base.step_alpha(cx, ColorScaleStep::TWO)) }) - .when(self.closable, |this| { + .when(self.keyboard, |this| { this.on_mouse_down(MouseButton::Left, { let on_close = self.on_close.clone(); move |_, window, cx| {