wip: refactor

This commit is contained in:
2024-12-25 13:44:16 +07:00
parent 37d810d9e5
commit 0745b497f0
13 changed files with 117 additions and 120 deletions

View File

@@ -2,12 +2,42 @@ use gpui::*;
use nostr_sdk::prelude::*;
use serde::Deserialize;
use crate::utils::get_room_id;
#[derive(Clone, PartialEq, Eq, Deserialize)]
pub struct Room {
pub id: SharedString,
pub owner: PublicKey,
pub members: Vec<PublicKey>,
pub last_seen: Timestamp,
pub title: Option<String>,
pub title: Option<SharedString>,
}
impl Room {
pub fn new(event: Event) -> Self {
let owner = event.pubkey;
let last_seen = event.created_at;
// Get all members from event's tag
let members: Vec<PublicKey> = event.tags.public_keys().copied().collect();
// Get title from event's tag
let title = if let Some(tag) = event.tags.find(TagKind::Title) {
tag.content().map(|s| s.to_owned().into())
} else {
// TODO: create random name?
None
};
// Get unique id based on members
let id = get_room_id(&owner, &members).into();
Self {
id,
title,
members,
last_seen,
owner,
}
}
}
#[derive(Clone, Debug)]

View File

@@ -27,6 +27,21 @@ pub fn get_keys_by_account(public_key: PublicKey) -> Result<Keys, anyhow::Error>
Ok(keys)
}
pub fn get_room_id(owner: &PublicKey, public_keys: &[PublicKey]) -> String {
let hex: Vec<String> = public_keys
.iter()
.map(|m| {
let hex = m.to_hex();
let split = &hex[..6];
split.to_owned()
})
.collect();
let mems = hex.join("-");
format!("{}-{}", &owner.to_hex()[..6], mems)
}
pub fn show_npub(public_key: PublicKey, len: usize) -> String {
let bech32 = public_key.to_bech32().unwrap_or_default();
let separator = " ... ";

View File

@@ -1,6 +1,6 @@
use coop_ui::{
button::Button,
dock::{Panel, PanelEvent, PanelState, TitleStyle},
dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu,
};
use gpui::*;
@@ -18,11 +18,13 @@ pub struct ChatPanel {
zoomable: bool,
focus_handle: FocusHandle,
// Room
id: SharedString,
room: View<ChatRoom>,
}
impl ChatPanel {
pub fn new(room: &Arc<Room>, cx: &mut WindowContext) -> View<Self> {
let id = room.id.clone();
let room = cx.new_view(|cx| {
let view = ChatRoom::new(room, cx);
// Load messages
@@ -38,24 +40,21 @@ impl ChatPanel {
closeable: true,
zoomable: true,
focus_handle: cx.focus_handle(),
id,
room,
})
}
}
impl Panel for ChatPanel {
fn panel_name(&self) -> &'static str {
"ChatPanel"
fn panel_name(&self) -> SharedString {
self.id.clone()
}
fn title(&self, _cx: &WindowContext) -> AnyElement {
self.name.clone().into_any_element()
}
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle> {
None
}
fn closeable(&self, _cx: &WindowContext) -> bool {
self.closeable
}

View File

@@ -181,22 +181,20 @@ impl ChatRoom {
cx.foreground_executor()
.spawn({
let client = get_client();
let owner = self.owner;
let members = self.members.to_vec();
async move {
let signer = client.signer().await.unwrap();
let public_key = signer.get_public_key().await.unwrap();
let recv = Filter::new()
.kind(Kind::PrivateDirectMessage)
.authors(members.clone())
.pubkey(public_key);
.author(owner)
.pubkeys(members.clone());
let send = Filter::new()
.kind(Kind::PrivateDirectMessage)
.author(public_key)
.pubkeys(members);
.authors(members)
.pubkey(owner);
async move {
let events = async_cx
.background_executor()
.spawn(async move { client.database().query(vec![recv, send]).await })

View File

@@ -126,7 +126,7 @@ impl RenderOnce for Item {
)
.on_click(move |_, cx| {
cx.dispatch_action(Box::new(AddPanel {
room: self.room.clone(),
room: Arc::clone(&self.room),
position: coop_ui::dock::DockPlacement::Center,
}))
})
@@ -136,31 +136,15 @@ impl RenderOnce for Item {
pub struct InboxItem {
room: Arc<Room>,
metadata: Model<Option<Metadata>>,
pub(crate) sender: PublicKey,
}
impl InboxItem {
pub fn new(event: Event, cx: &mut ViewContext<'_, Self>) -> Self {
let sender = event.pubkey;
let last_seen = event.created_at;
// Get all members from event's tag
let mut members: Vec<PublicKey> = event.tags.public_keys().copied().collect();
// Add sender to members
members.insert(0, sender);
// Get title from event's tag
let title = if let Some(tag) = event.tags.find(TagKind::Title) {
tag.content().map(|s| s.to_string())
} else {
// TODO: create random name?
None
};
let room = Arc::new(Room::new(event));
let metadata = cx.new_model(|_| None);
// Request metadata
_ = cx.global::<SignalRegistry>().tx.send(sender);
_ = cx.global::<SignalRegistry>().tx.send(room.owner);
// Reload when received metadata
cx.observe_global::<MetadataRegistry>(|chat, cx| {
@@ -168,22 +152,11 @@ impl InboxItem {
})
.detach();
let room = Arc::new(Room {
title,
members,
last_seen,
owner: sender,
});
Self {
room,
sender,
metadata,
}
Self { room, metadata }
}
pub fn load_metadata(&mut self, cx: &mut ViewContext<Self>) {
let public_key = self.sender;
let public_key = self.room.owner;
let async_metadata = self.metadata.clone();
let mut async_cx = cx.to_async();
@@ -205,9 +178,13 @@ impl InboxItem {
.detach();
}
pub fn id(&self) -> String {
self.room.id.clone().into()
}
fn render_item(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let metadata = self.metadata.read(cx).clone();
let room = self.room.clone();
let room = Arc::clone(&self.room);
Item::new(room, metadata)
}

View File

@@ -8,7 +8,7 @@ use nostr_sdk::prelude::*;
use prelude::FluentBuilder;
use std::cmp::Reverse;
use crate::{get_client, states::chat::ChatRegistry};
use crate::{get_client, states::chat::ChatRegistry, utils::get_room_id};
pub mod item;
@@ -35,15 +35,18 @@ impl Inbox {
let new_messages = state.new_messages.clone();
// Get all current chats
let current: Vec<PublicKey> = items
.iter()
.map(|item| item.model.read(cx).sender)
.collect();
let current_rooms: Vec<String> =
items.iter().map(|item| item.model.read(cx).id()).collect();
// Create view for only new chats
// Create view for new chats only
let new = new_messages
.into_iter()
.filter(|m| current.iter().any(|pk| pk == &m.event.pubkey))
.filter(|m| {
let keys = m.event.tags.public_keys().copied().collect::<Vec<_>>();
let nid = get_room_id(&m.event.pubkey, &keys);
current_rooms.iter().any(|id| id == &nid)
})
.map(|m| cx.new_view(|cx| InboxItem::new(m.event, cx)))
.collect::<Vec<_>>();

View File

@@ -1,6 +1,6 @@
use coop_ui::{
button::Button,
dock::{Panel, PanelEvent, PanelState, TitleStyle},
dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu,
scroll::ScrollbarAxis,
StyledExt,
@@ -40,18 +40,14 @@ impl LeftDock {
}
impl Panel for LeftDock {
fn panel_name(&self) -> &'static str {
"ChatPanel"
fn panel_name(&self) -> SharedString {
"LeftDock".into()
}
fn title(&self, _cx: &WindowContext) -> AnyElement {
self.name.clone().into_any_element()
}
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle> {
None
}
fn closeable(&self, _cx: &WindowContext) -> bool {
self.closeable
}

View File

@@ -1,6 +1,6 @@
use coop_ui::{
button::Button,
dock::{Panel, PanelEvent, PanelState, TitleStyle},
dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu,
theme::{ActiveTheme, Colorize},
StyledExt,
@@ -30,18 +30,14 @@ impl WelcomePanel {
}
impl Panel for WelcomePanel {
fn panel_name(&self) -> &'static str {
"WelcomePanel"
fn panel_name(&self) -> SharedString {
"WelcomePanel".into()
}
fn title(&self, _cx: &WindowContext) -> AnyElement {
self.name.clone().into_any_element()
}
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle> {
None
}
fn closeable(&self, _cx: &WindowContext) -> bool {
self.closeable
}

View File

@@ -22,8 +22,8 @@ impl InvalidPanel {
}
}
impl Panel for InvalidPanel {
fn panel_name(&self) -> &'static str {
"InvalidPanel"
fn panel_name(&self) -> SharedString {
"InvalidPanel".into()
}
fn dump(&self, _cx: &AppContext) -> super::PanelState {

View File

@@ -32,18 +32,13 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
///
/// This is used to identify the panel when deserializing the panel.
/// Once you have defined a panel name, this must not be changed.
fn panel_name(&self) -> &'static str;
fn panel_name(&self) -> SharedString;
/// The title of the panel
fn title(&self, _cx: &WindowContext) -> AnyElement {
SharedString::from("Untitled").into_any_element()
}
/// The theme of the panel title, default is `None`.
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle> {
None
}
/// Whether the panel can be closed, default is `true`.
fn closeable(&self, _cx: &WindowContext) -> bool {
true
@@ -71,9 +66,8 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
}
pub trait PanelView: 'static + Send + Sync {
fn panel_name(&self, _cx: &AppContext) -> &'static str;
fn panel_name(&self, cx: &WindowContext) -> SharedString;
fn title(&self, _cx: &WindowContext) -> AnyElement;
fn title_style(&self, _cx: &WindowContext) -> Option<TitleStyle>;
fn closeable(&self, cx: &WindowContext) -> bool;
fn zoomable(&self, cx: &WindowContext) -> bool;
fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu;
@@ -84,7 +78,7 @@ pub trait PanelView: 'static + Send + Sync {
}
impl<T: Panel> PanelView for View<T> {
fn panel_name(&self, cx: &AppContext) -> &'static str {
fn panel_name(&self, cx: &WindowContext) -> SharedString {
self.read(cx).panel_name()
}
@@ -92,10 +86,6 @@ impl<T: Panel> PanelView for View<T> {
self.read(cx).title(cx)
}
fn title_style(&self, cx: &WindowContext) -> Option<TitleStyle> {
self.read(cx).title_style(cx)
}
fn closeable(&self, cx: &WindowContext) -> bool {
self.read(cx).closeable(cx)
}

View File

@@ -1,8 +1,5 @@
use gpui::{
prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, EventEmitter, FocusHandle,
FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, Subscription, View,
ViewContext, VisualContext, WeakView,
};
use gpui::*;
use prelude::FluentBuilder;
use smallvec::SmallVec;
use std::sync::Arc;
@@ -28,8 +25,8 @@ pub struct StackPanel {
}
impl Panel for StackPanel {
fn panel_name(&self) -> &'static str {
"StackPanel"
fn panel_name(&self) -> SharedString {
"StackPanel".into()
}
fn title(&self, _cx: &gpui::WindowContext) -> gpui::AnyElement {

View File

@@ -71,7 +71,6 @@ pub struct TabPanel {
/// If this is true, the Panel closeable will follow the active panel's closeable,
/// otherwise this TabPanel will not able to close
pub(crate) closeable: bool,
tab_bar_scroll_handle: ScrollHandle,
is_zoomed: bool,
is_collapsed: bool,
@@ -80,8 +79,8 @@ pub struct TabPanel {
}
impl Panel for TabPanel {
fn panel_name(&self) -> &'static str {
"TabPanel"
fn panel_name(&self) -> SharedString {
"TabPanel".into()
}
fn title(&self, cx: &WindowContext) -> gpui::AnyElement {
@@ -180,25 +179,32 @@ impl TabPanel {
active: bool,
cx: &mut ViewContext<Self>,
) {
assert_ne!(
panel.panel_name(cx),
"StackPanel",
"can not allows add `StackPanel` to `TabPanel`"
);
if self
.panels
.iter()
.any(|p| p.view().entity_id() == panel.view().entity_id())
.any(|p| p.panel_name(cx) == panel.panel_name(cx))
{
// set the active panel to the matched panel
if active {
if let Some(ix) = self
.panels
.iter()
.position(|p| p.panel_name(cx) == panel.panel_name(cx))
{
self.set_active_ix(ix, cx);
}
}
return;
}
self.panels.push(panel);
// set the active panel to the new panel
if active {
self.set_active_ix(self.panels.len() - 1, cx);
}
cx.emit(PanelEvent::LayoutChanged);
cx.notify();
}
@@ -466,7 +472,6 @@ impl TabPanel {
if self.panels.len() == 1 && panel_style == PanelStyle::Default {
let panel = self.panels.first().unwrap();
let title_style = panel.title_style(cx);
return h_flex()
.justify_between()
@@ -477,9 +482,6 @@ impl TabPanel {
.px_3()
.when(left_dock_button.is_some(), |this| this.pl_2())
.when(right_dock_button.is_some(), |this| this.pr_2())
.when_some(title_style, |this, theme| {
this.bg(theme.background).text_color(theme.foreground)
})
.when(
left_dock_button.is_some() || bottom_dock_button.is_some(),
|this| {

View File

@@ -1,10 +1,4 @@
use gpui::{
canvas, div, point, px, size, AnyElement, AppContext, Bounds, DismissEvent, DragMoveEvent,
Entity, EntityId, EventEmitter, FocusHandle, FocusableView, Half, InteractiveElement,
IntoElement, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, Point, Render,
ScrollHandle, Size, StatefulInteractiveElement, Styled, ViewContext, VisualContext, WeakView,
WindowContext,
};
use gpui::*;
use std::{
cell::Cell,
fmt::{Debug, Formatter},
@@ -92,8 +86,8 @@ pub struct Tiles {
}
impl Panel for Tiles {
fn panel_name(&self) -> &'static str {
"Tiles"
fn panel_name(&self) -> SharedString {
"Tiles".into()
}
fn title(&self, _cx: &WindowContext) -> AnyElement {