From a9d109486eb374320ad48957231794c3f8ab088e Mon Sep 17 00:00:00 2001 From: reya Date: Sun, 12 Jan 2025 15:53:44 +0700 Subject: [PATCH] wip: refactor --- crates/app/src/states/chat/mod.rs | 5 +- crates/app/src/states/chat/room.rs | 50 ++++++++++---- crates/app/src/utils.rs | 4 +- crates/app/src/views/chat/message.rs | 71 ++++++-------------- crates/app/src/views/chat/mod.rs | 94 +++++++++++++-------------- crates/app/src/views/sidebar/inbox.rs | 47 +++++--------- 6 files changed, 124 insertions(+), 147 deletions(-) diff --git a/crates/app/src/states/chat/mod.rs b/crates/app/src/states/chat/mod.rs index bf7bf47..e6dd513 100644 --- a/crates/app/src/states/chat/mod.rs +++ b/crates/app/src/states/chat/mod.rs @@ -162,10 +162,7 @@ impl ChatRegistry { self.inbox.update(cx, |this, cx| { if let Some(room) = this.rooms.iter().find(|room| { - let room = room.read(cx); - let mut all_keys: Vec<_> = room.members.iter().map(|m| m.public_key()).collect(); - all_keys.push(room.owner.public_key()); - + let all_keys = room.read(cx).get_all_keys(); compare(&all_keys, &pubkeys) }) { room.update(cx, |this, cx| { diff --git a/crates/app/src/states/chat/room.rs b/crates/app/src/states/chat/room.rs index 9cad6c0..9925d81 100644 --- a/crates/app/src/states/chat/room.rs +++ b/crates/app/src/states/chat/room.rs @@ -1,4 +1,7 @@ -use crate::utils::{room_hash, shorted_public_key}; +use crate::{ + constants::IMAGE_SERVICE, + utils::{room_hash, shorted_public_key}, +}; use gpui::SharedString; use nostr_sdk::prelude::*; use rnglib::{Language, RNG}; @@ -21,8 +24,15 @@ impl Member { self.public_key } - pub fn metadata(&self) -> Metadata { - self.metadata.clone() + pub fn avatar(&self) -> String { + if let Some(picture) = &self.metadata.picture { + format!( + "{}/?url={}&w=100&h=100&fit=cover&mask=circle&n=-1", + IMAGE_SERVICE, picture + ) + } else { + "brands/avatar.png".into() + } } pub fn name(&self) -> String { @@ -92,14 +102,6 @@ impl Room { } } - pub fn metadata(&self, public_key: PublicKey) -> Metadata { - if let Some(member) = self.members.iter().find(|m| m.public_key == public_key) { - member.metadata() - } else { - Metadata::default() - } - } - pub fn set_metadata(&mut self, public_key: PublicKey, metadata: Metadata) { if self.owner.public_key() == public_key { self.owner.update(&metadata); @@ -111,4 +113,30 @@ impl Room { } } } + + pub fn member(&self, public_key: &PublicKey) -> Option { + if &self.owner.public_key() == public_key { + Some(self.owner.clone()) + } else { + self.members + .iter() + .find(|m| &m.public_key() == public_key) + .cloned() + } + } + + pub fn name(&self) -> String { + self.members + .iter() + .map(|profile| profile.name()) + .collect::>() + .join(", ") + } + + pub fn get_all_keys(&self) -> Vec { + let mut pubkeys: Vec<_> = self.members.iter().map(|m| m.public_key()).collect(); + pubkeys.push(self.owner.public_key()); + + pubkeys + } } diff --git a/crates/app/src/utils.rs b/crates/app/src/utils.rs index 1a5b185..898f7a6 100644 --- a/crates/app/src/utils.rs +++ b/crates/app/src/utils.rs @@ -46,9 +46,9 @@ pub fn show_npub(public_key: PublicKey, len: usize) -> String { ) } -pub fn ago(time: u64) -> String { +pub fn ago(time: Timestamp) -> String { let now = Local::now(); - let input_time = Local.timestamp_opt(time as i64, 0).unwrap(); + let input_time = Local.timestamp_opt(time.as_u64() as i64, 0).unwrap(); let diff = (now - input_time).num_hours(); if diff < 24 { diff --git a/crates/app/src/views/chat/message.rs b/crates/app/src/views/chat/message.rs index 7f7f4ac..c33dfe7 100644 --- a/crates/app/src/views/chat/message.rs +++ b/crates/app/src/views/chat/message.rs @@ -1,46 +1,28 @@ +use crate::states::chat::room::Member; use gpui::{ - div, img, prelude::FluentBuilder, InteractiveElement, IntoElement, ParentElement, RenderOnce, - SharedString, Styled, WindowContext, + div, img, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString, Styled, + WindowContext, }; -use nostr_sdk::prelude::*; use ui::{theme::ActiveTheme, StyledExt}; -use crate::{ - constants::IMAGE_SERVICE, - utils::{ago, show_npub}, -}; - #[derive(Clone, Debug, IntoElement)] -pub struct RoomMessage { - #[allow(dead_code)] - author: PublicKey, - fallback: SharedString, - metadata: Metadata, +pub struct Message { + member: Member, content: SharedString, - created_at: SharedString, + ago: SharedString, } -impl RoomMessage { - pub fn new( - author: PublicKey, - metadata: Metadata, - content: String, - created_at: Timestamp, - ) -> Self { - let created_at = ago(created_at.as_u64()).into(); - let fallback = show_npub(author, 16).into(); - +impl Message { + pub fn new(member: Member, content: SharedString, ago: SharedString) -> Self { Self { - author, - metadata, - fallback, - created_at, - content: content.into(), + member, + content, + ago, } } } -impl RenderOnce for RoomMessage { +impl RenderOnce for Message { fn render(self, cx: &mut WindowContext) -> impl IntoElement { div() .flex() @@ -54,19 +36,12 @@ impl RenderOnce for RoomMessage { .border_color(cx.theme().primary_active) .text_color(cx.theme().muted_foreground) }) - .child(div().flex_shrink_0().map(|this| { - if let Some(picture) = self.metadata.picture { - this.child( - img(format!( - "{}/?url={}&w=100&h=100&fit=cover&mask=circle&n=-1", - IMAGE_SERVICE, picture - )) - .size_8(), - ) - } else { - this.child(img("brand/avatar.png").size_8().rounded_full()) - } - })) + .child( + img(self.member.avatar()) + .size_8() + .rounded_full() + .flex_shrink_0(), + ) .child( div() .flex() @@ -79,16 +54,10 @@ impl RenderOnce for RoomMessage { .items_baseline() .gap_2() .text_xs() - .child(div().font_semibold().map(|this| { - if let Some(display_name) = self.metadata.display_name { - this.child(display_name) - } else { - this.child(self.fallback) - } - })) + .child(div().font_semibold().child(self.member.name())) .child( div() - .child(self.created_at) + .child(self.ago) .text_color(cx.theme().muted_foreground), ), ) diff --git a/crates/app/src/views/chat/mod.rs b/crates/app/src/views/chat/mod.rs index 35e45dd..6e5c9ad 100644 --- a/crates/app/src/views/chat/mod.rs +++ b/crates/app/src/views/chat/mod.rs @@ -1,12 +1,16 @@ -use crate::{get_client, states::chat::room::Room, utils::compare}; +use crate::{ + get_client, + states::chat::room::Room, + utils::{ago, compare}, +}; use gpui::{ div, list, px, AnyElement, AppContext, Context, EventEmitter, Flatten, FocusHandle, FocusableView, IntoElement, ListAlignment, ListState, Model, ParentElement, PathPromptOptions, - Pixels, Render, SharedString, Styled, View, ViewContext, VisualContext, WeakModel, + Pixels, Render, SharedString, Styled, View, ViewContext, VisualContext, WeakModel, WeakView, WindowContext, }; use itertools::Itertools; -use message::RoomMessage; +use message::Message; use nostr_sdk::prelude::*; use std::sync::Arc; use ui::{ @@ -23,7 +27,7 @@ mod message; #[derive(Clone)] pub struct State { count: usize, - items: Vec, + items: Vec, } pub struct ChatPanel { @@ -61,19 +65,22 @@ impl ChatPanel { .cleanable() }); - // Send message when user presses enter - cx.subscribe(&input, move |this: &mut ChatPanel, _, input_event, cx| { - if let InputEvent::PressEnter = input_event { - this.send_message(cx); - } - }) - .detach(); - let state = cx.new_model(|_| State { count: 0, items: vec![], }); + // Send message when user presses enter + cx.subscribe( + &input, + move |this: &mut ChatPanel, view, input_event, cx| { + if let InputEvent::PressEnter = input_event { + this.send_message(view.downgrade(), cx); + } + }, + ) + .detach(); + // Update list on every state changes cx.observe(&state, |this, model, cx| { let items = model.read(cx).items.clone(); @@ -117,10 +124,8 @@ impl ChatPanel { let room = self.room.read(cx); let members = room.members.clone(); let owner = room.owner.clone(); - // Get all public keys - let mut all_keys: Vec<_> = room.members.iter().map(|m| m.public_key()).collect(); - all_keys.push(room.owner.public_key()); + let all_keys = room.get_all_keys(); // Async let async_state = self.state.clone(); @@ -157,7 +162,7 @@ impl ChatPanel { .await; if let Ok(events) = events { - let items: Vec = events + let items: Vec = events .into_iter() .sorted_by_key(|ev| ev.created_at) .filter_map(|ev| { @@ -165,21 +170,18 @@ impl ChatPanel { pubkeys.push(ev.pubkey); if compare(&pubkeys, &all_keys) { - let metadata = if let Some(member) = + let member = if let Some(member) = members.iter().find(|&m| m.public_key() == ev.pubkey) { - member.metadata() - } else if ev.pubkey == owner.public_key() { - owner.metadata() + member.to_owned() } else { - Metadata::default() + owner.clone() }; - Some(RoomMessage::new( - ev.pubkey, - metadata, - ev.content, - ev.created_at, + Some(Message::new( + member, + ev.content.into(), + ago(ev.created_at).into(), )) } else { None @@ -202,18 +204,17 @@ impl ChatPanel { fn load_new_messages(&self, model: WeakModel, cx: &mut ViewContext) { if let Some(model) = model.upgrade() { let room = model.read(cx); - let items: Vec = room + let items: Vec = room .new_messages .iter() - .map(|event| { - let metadata = room.metadata(event.pubkey); - - RoomMessage::new( - event.pubkey, - metadata, - event.content.clone(), - event.created_at, - ) + .filter_map(|event| { + room.member(&event.pubkey).map(|member| { + Message::new( + member, + event.content.clone().into(), + ago(event.created_at).into(), + ) + }) }) .collect(); @@ -225,7 +226,7 @@ impl ChatPanel { } } - fn send_message(&mut self, cx: &mut ViewContext) { + fn send_message(&mut self, view: WeakView, cx: &mut ViewContext) { let room = self.room.read(cx); let content = Arc::new(self.input.read(cx).text().to_string()); let owner = room.owner.clone(); @@ -234,7 +235,6 @@ impl ChatPanel { members.push(owner.clone()); // Async - let async_input = self.input.clone(); let async_state = self.state.clone(); let mut async_cx = cx.to_async(); @@ -269,12 +269,10 @@ impl ChatPanel { .detach(); _ = async_cx.update_model(&async_state, |model, cx| { - let created_at = Timestamp::now(); - let message = RoomMessage::new( - owner.public_key(), - owner.metadata(), - content.to_string(), - created_at, + let message = Message::new( + owner, + content.to_string().into(), + ago(Timestamp::now()).into(), ); model.items.extend(vec![message]); @@ -282,9 +280,11 @@ impl ChatPanel { cx.notify(); }); - _ = async_cx.update_view(&async_input, |input, cx| { - input.set_text("", cx); - }); + if let Some(input) = view.upgrade() { + _ = async_cx.update_view(&input, |input, cx| { + input.set_text("", cx); + }); + } }) .detach(); } diff --git a/crates/app/src/views/sidebar/inbox.rs b/crates/app/src/views/sidebar/inbox.rs index 122af61..49bd5f7 100644 --- a/crates/app/src/views/sidebar/inbox.rs +++ b/crates/app/src/views/sidebar/inbox.rs @@ -1,5 +1,4 @@ use crate::{ - constants::IMAGE_SERVICE, states::chat::ChatRegistry, utils::ago, views::app::{AddPanel, PanelKind}, @@ -50,18 +49,7 @@ impl Inbox { let room = model.read(cx); let id = room.id; let room_id: SharedString = id.to_string().into(); - let ago: SharedString = ago(room.last_seen.as_u64()).into(); - let is_group = room.is_group; - // Get first member - let sender = room.members.first().unwrap(); - // Compute group name based on member' names - let name: SharedString = room - .members - .iter() - .map(|profile| profile.name()) - .collect::>() - .join(", ") - .into(); + let ago: SharedString = ago(room.last_seen).into(); div() .id(room_id) @@ -81,32 +69,27 @@ impl Inbox { .font_medium() .text_color(cx.theme().sidebar_accent_foreground) .map(|this| { - if is_group { + if room.is_group { this.flex() .items_center() .gap_2() .child( img("brand/avatar.png").size_6().rounded_full(), ) - .child(name) + .child(room.name()) } else { - this.flex() - .items_center() - .gap_2() - .child( - img(format!( - "{}/?url={}&w=72&h=72&fit=cover&mask=circle&n=-1", - IMAGE_SERVICE, - sender - .metadata() - .picture - .unwrap_or("brand/avatar.png".into()) - )) - .flex_shrink_0() - .size_6() - .rounded_full(), - ) - .child(sender.name()) + this.when_some(room.members.first(), |this, sender| { + this.flex() + .items_center() + .gap_2() + .child( + img(sender.avatar()) + .size_6() + .rounded_full() + .flex_shrink_0(), + ) + .child(sender.name()) + }) } }), )