feat: automatically open the chat room

This commit is contained in:
2025-05-04 10:12:15 +07:00
parent 3bd8592f86
commit 3fea18f038
9 changed files with 105 additions and 110 deletions

View File

@@ -1,6 +1,6 @@
use std::{cmp::Reverse, collections::HashMap}; use std::{cmp::Reverse, collections::HashMap};
use anyhow::{anyhow, Error}; use anyhow::Error;
use common::room_hash; use common::room_hash;
use global::get_client; use global::get_client;
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window}; use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task, Window};
@@ -8,6 +8,7 @@ use itertools::Itertools;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use room::RoomKind; use room::RoomKind;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use ui::ContextModal;
use crate::room::Room; use crate::room::Room;
@@ -261,21 +262,18 @@ impl ChatRegistry {
/// Add a new room to the registry /// Add a new room to the registry
/// ///
/// Returns an error if the room already exists /// Returns an error if the room already exists
pub fn push(&mut self, room: Room, cx: &mut Context<Self>) -> Result<(), anyhow::Error> { pub fn push(&mut self, event: &Event, window: &mut Window, cx: &mut Context<Self>) -> u64 {
let room = cx.new(|_| room); let room = Room::new(event).kind(RoomKind::Ongoing);
let id = room.id;
if !self if !self.rooms.iter().any(|r| r.read(cx) == &room) {
.rooms self.rooms.insert(0, cx.new(|_| room));
.iter()
.any(|current| current.read(cx) == room.read(cx))
{
self.rooms.insert(0, room);
cx.notify(); cx.notify();
Ok(())
} else { } else {
Err(anyhow!("Room already exists")) window.push_notification("Room already exists", cx);
} }
id
} }
/// Push a new message to a room /// Push a new message to a room

View File

@@ -28,7 +28,7 @@ use ui::{
popup_menu::PopupMenu, popup_menu::PopupMenu,
text::RichText, text::RichText,
theme::{scale::ColorScaleStep, ActiveTheme}, 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; use crate::views::subject;
@@ -473,31 +473,33 @@ impl Panel for Chat {
fn title(&self, cx: &App) -> AnyElement { fn title(&self, cx: &App) -> AnyElement {
self.room.read_with(cx, |this, _| { self.room.read_with(cx, |this, _| {
let facepill: Vec<SharedString> = this.avatars(cx); let label = this.display_name(cx);
let url = this.display_image(cx);
div() div()
.flex() .flex()
.items_center() .items_center()
.gap_1p5() .gap_1p5()
.child( .map(|this| {
div() if let Some(url) = url {
.flex() this.child(img(url).size_5().flex_shrink_0())
.flex_row_reverse() } else {
.items_center() this.child(
.justify_start() div()
.children( .flex_shrink_0()
facepill .flex()
.into_iter() .justify_center()
.enumerate() .items_center()
.rev() .size_5()
.map(|(ix, facepill)| { .rounded_full()
div() .bg(cx.theme().accent.step(cx, ColorScaleStep::THREE))
.when(ix > 0, |div| div.ml_neg_1()) .child(Icon::new(IconName::UsersThreeFill).xsmall().text_color(
.child(img(facepill).size_5()) cx.theme().accent.step(cx, ColorScaleStep::TWELVE),
}), )),
), )
) }
.child(this.display_name(cx)) })
.child(label)
.into_any() .into_any()
}) })
} }

View File

@@ -1,8 +1,5 @@
use anyhow::Error; use anyhow::Error;
use chats::{ use chats::ChatRegistry;
room::{Room, RoomKind},
ChatRegistry,
};
use common::{profile::SharedProfile, random_name}; use common::{profile::SharedProfile, random_name};
use global::get_client; use global::get_client;
use gpui::{ use gpui::{
@@ -21,11 +18,14 @@ use std::{
}; };
use ui::{ use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
dock_area::dock::DockPlacement,
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt,
}; };
use crate::chatspace::{AddPanel, PanelKind};
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Compose> { pub fn init(window: &mut Window, cx: &mut App) -> Entity<Compose> {
cx.new(|cx| Compose::new(window, cx)) cx.new(|cx| Compose::new(window, cx))
} }
@@ -161,34 +161,26 @@ impl Compose {
Ok(event) Ok(event)
}); });
cx.spawn_in(window, async move |this, cx| { cx.spawn_in(window, async move |this, cx| match event.await {
if let Ok(event) = event.await { Ok(event) => {
cx.update(|window, cx| { cx.update(|window, cx| {
this.update(cx, |this, cx| { ChatRegistry::global(cx).update(cx, |chats, cx| {
this.set_submitting(false, cx); let id = chats.push(&event, window, cx);
}) window.close_modal(cx);
.ok(); window.dispatch_action(
Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)),
let chats = ChatRegistry::global(cx); 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();
}
}
}); });
}) })
.ok(); .ok();
} }
Err(e) => {
this.update(cx, |this, cx| {
this.set_error(Some(e.to_string().into()), cx);
})
.ok();
}
}) })
.detach(); .detach();
} }

View File

@@ -284,6 +284,8 @@ impl Render for Relays {
div() div()
.track_focus(&self.focus_handle) .track_focus(&self.focus_handle)
.size_full() .size_full()
.px_3()
.pb_3()
.flex() .flex()
.flex_col() .flex_col()
.justify_between() .justify_between()

View File

@@ -2,10 +2,7 @@ use std::time::Duration;
use anyhow::Error; use anyhow::Error;
use async_utility::task::spawn; use async_utility::task::spawn;
use chats::{ use chats::ChatRegistry;
room::{Room, RoomKind},
ChatRegistry,
};
use common::profile::SharedProfile; use common::profile::SharedProfile;
use global::{constants::SEARCH_RELAYS, get_client}; use global::{constants::SEARCH_RELAYS, get_client};
use gpui::{ use gpui::{
@@ -18,12 +15,15 @@ use nostr_sdk::prelude::*;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use ui::{ use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
dock_area::dock::DockPlacement,
indicator::Indicator, indicator::Indicator,
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
theme::{scale::ColorScaleStep, ActiveTheme}, theme::{scale::ColorScaleStep, ActiveTheme},
ContextModal, Disableable, IconName, Sizable, ContextModal, Disableable, IconName, Sizable,
}; };
use crate::chatspace::{AddPanel, PanelKind};
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Search> { pub fn init(window: &mut Window, cx: &mut App) -> Entity<Search> {
Search::new(window, cx) Search::new(window, cx)
} }
@@ -167,28 +167,25 @@ impl Search {
Ok(event) Ok(event)
}); });
cx.spawn_in(window, async move |this, cx| { cx.spawn_in(window, async move |this, cx| match event.await {
if let Ok(event) = event.await { Ok(event) => {
cx.update(|window, cx| { cx.update(|window, cx| {
let chats = ChatRegistry::global(cx); ChatRegistry::global(cx).update(cx, |chats, cx| {
let room = Room::new(&event).kind(RoomKind::Ongoing); let id = chats.push(&event, window, cx);
window.close_modal(cx);
chats.update(cx, |chats, cx| { window.dispatch_action(
match chats.push(room, cx) { Box::new(AddPanel::new(PanelKind::Room(id), DockPlacement::Center)),
Ok(_) => { cx,
// TODO: automatically open newly created chat panel );
window.close_modal(cx); });
} })
Err(e) => { .ok();
this.update(cx, |this, cx| { }
this.error.update(cx, |this, cx| { Err(e) => {
*this = Some(e.to_string().into()); this.update(cx, |this, cx| {
cx.notify(); this.error.update(cx, |this, cx| {
}); *this = Some(e.to_string().into());
}) cx.notify();
.ok();
}
}
}); });
}) })
.ok(); .ok();

View File

@@ -306,6 +306,7 @@ impl RenderOnce for FolderItem {
} else { } else {
this.child( this.child(
div() div()
.flex_shrink_0()
.flex() .flex()
.justify_center() .justify_center()
.items_center() .items_center()

View File

@@ -104,6 +104,28 @@ impl Sidebar {
cx.notify(); cx.notify();
} }
fn on_logout(&mut self, _: &Logout, window: &mut Window, cx: &mut Context<Self>) {
let task: Task<Result<(), anyhow::Error>> = 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<Item = impl IntoElement> { fn render_skeleton(&self, total: i32) -> impl IntoIterator<Item = impl IntoElement> {
(0..total).map(|_| { (0..total).map(|_| {
div() div()
@@ -146,28 +168,6 @@ impl Sidebar {
items items
} }
fn on_logout(&mut self, _: &Logout, window: &mut Window, cx: &mut Context<Self>) {
let task: Task<Result<(), anyhow::Error>> = 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 { impl Panel for Sidebar {
@@ -206,6 +206,7 @@ impl Render for Sidebar {
div() div()
.id("sidebar") .id("sidebar")
.track_focus(&self.focus_handle)
.track_scroll(&self.scroll_handle) .track_scroll(&self.scroll_handle)
.on_action(cx.listener(Self::on_logout)) .on_action(cx.listener(Self::on_logout))
.overflow_y_scroll() .overflow_y_scroll()

View File

@@ -80,6 +80,8 @@ impl Render for Subject {
.flex() .flex()
.flex_col() .flex_col()
.gap_3() .gap_3()
.px_3()
.pb_3()
.child( .child(
div() div()
.flex() .flex()

View File

@@ -47,7 +47,7 @@ impl Modal {
let base = v_flex() let base = v_flex()
.bg(cx.theme().background) .bg(cx.theme().background)
.border_1() .border_1()
.border_color(cx.theme().base.step(cx, ColorScaleStep::SEVEN)) .border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
.rounded_xl() .rounded_xl()
.shadow_md(); .shadow_md();
@@ -169,9 +169,9 @@ impl RenderOnce for Modal {
.w(view_size.width) .w(view_size.width)
.h(view_size.height) .h(view_size.height)
.when(self.overlay, |this| { .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, { this.on_mouse_down(MouseButton::Left, {
let on_close = self.on_close.clone(); let on_close = self.on_close.clone();
move |_, window, cx| { move |_, window, cx| {