From 274fd1a040b6ea340a7863f166dcdcb73ab10688 Mon Sep 17 00:00:00 2001 From: Akiomi Kamakura Date: Sun, 14 Jan 2024 02:28:57 +0900 Subject: [PATCH] Use Profile --- src/app.rs | 3 ++- src/components/home.rs | 32 ++++++++++++--------------- src/components/status_bar.rs | 31 ++++++++++++++------------ src/nostr/profile.rs | 1 + src/widgets/text_note.rs | 42 +++++++++++++++++++++++++++++------- 5 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/app.rs b/src/app.rs index a3b2d953..af340b40 100644 --- a/src/app.rs +++ b/src/app.rs @@ -165,7 +165,8 @@ impl App { action_tx.send(Action::SystemMessage(format!("[Reposted] {note1}")))?; } Action::SendTextNote(ref content, ref tags) => { - let event = EventBuilder::new_text_note(content, tags.iter().cloned()).to_event(&keys)?; + let event = EventBuilder::new_text_note(content, tags.iter().cloned()) + .to_event(&keys)?; log::info!("Send text note: {event:?}"); event_tx.send(event)?; action_tx.send(Action::SystemMessage(format!("[Posted] {content}")))?; diff --git a/src/components/home.rs b/src/components/home.rs index 7b029372..e511ba91 100644 --- a/src/components/home.rs +++ b/src/components/home.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use std::collections::{hash_map::Entry, HashMap}; use color_eyre::eyre::Result; -use nostr_sdk::prelude::{Metadata as NostrMetadata, *}; +use nostr_sdk::prelude::*; use ratatui::{prelude::*, widgets, widgets::*}; use sorted_vec::ReverseSortedSet; use tokio::sync::mpsc::UnboundedSender; @@ -11,10 +11,11 @@ use tui_textarea::TextArea; use tui_widget_list::List; use super::{Component, Frame}; +use crate::text::shorten_hex; use crate::{ action::Action, config::Config, - nostr::{nip10::ReplyTagsBuilder, Metadata, SortableEvent}, + nostr::{nip10::ReplyTagsBuilder, Metadata, Profile, SortableEvent}, widgets::ScrollableList, widgets::TextNote, }; @@ -25,7 +26,7 @@ pub struct Home<'a> { config: Config, list_state: tui_widget_list::ListState, notes: ReverseSortedSet, - profiles: HashMap, + profiles: HashMap, reactions: HashMap>, reposts: HashMap>, zap_receipts: HashMap>, @@ -58,8 +59,14 @@ impl<'a> Home<'a> { fn add_profile(&mut self, event: Event) { if let Ok(metadata) = Metadata::from_json(event.content.clone()) { - self.profiles - .insert(event.pubkey, NostrMetadata::from(metadata)); + let profile = Profile::new(event.pubkey, event.created_at, metadata); + if let Some(existing_profile) = self.profiles.get(&event.pubkey) { + if existing_profile.created_at > profile.created_at { + return; + } + } + + self.profiles.insert(event.pubkey, profile); } } @@ -264,20 +271,9 @@ impl<'a> Component for Home<'a> { let block = if let Some(ref reply_to) = self.reply_to { let name = if let Some(profile) = self.profiles.get(&reply_to.pubkey) { - match ( - profile.display_name.clone(), - profile.name.clone(), - reply_to.pubkey.to_bech32(), - ) { - (Some(display_name), _, _) if !display_name.is_empty() => display_name, - (_, Some(name), _) if !name.is_empty() => format!("@{name}"), - (_, _, Ok(npub)) => npub, - _ => reply_to.pubkey.to_string(), - } - } else if let Ok(npub) = reply_to.pubkey.to_bech32() { - npub.to_string() + profile.name() } else { - reply_to.pubkey.to_string() + shorten_hex(&reply_to.pubkey.to_string()) }; widgets::Block::default() diff --git a/src/components/status_bar.rs b/src/components/status_bar.rs index 7db43437..13e16fb2 100644 --- a/src/components/status_bar.rs +++ b/src/components/status_bar.rs @@ -5,12 +5,13 @@ use ratatui::{prelude::*, widgets::*}; use crate::action::Action; use crate::components::Component; use crate::nostr::Metadata; +use crate::nostr::Profile; use crate::tui::Frame; use crate::widgets::PublicKey; pub struct StatusBar { pubkey: XOnlyPublicKey, - metadata: Option, + profile: Option, message: Option, is_loading: bool, } @@ -18,30 +19,26 @@ pub struct StatusBar { impl StatusBar { pub fn new( pubkey: XOnlyPublicKey, - metadata: Option, + profile: Option, message: Option, is_loading: bool, ) -> Self { Self { pubkey, - metadata, + profile, message, is_loading, } } - pub fn set_metadata(&mut self, metadata: Option) { - self.metadata = metadata; + pub fn set_profile(&mut self, profile: Option) { + self.profile = profile; } pub fn name(&self) -> String { - self.metadata + self.profile .clone() - .and_then(|metadata| match (metadata.name, metadata.display_name) { - (Some(name), _) if !name.is_empty() => Some(format!("@{name}")), - (_, Some(display_name)) if !display_name.is_empty() => Some(display_name), - (_, _) => None, - }) + .map(|profile| profile.name()) .unwrap_or(PublicKey::new(self.pubkey).shortened()) } } @@ -54,9 +51,15 @@ impl Component for StatusBar { match ev.kind { Kind::Metadata if ev.pubkey == self.pubkey => { - let maybe_metadata = Metadata::from_json(ev.content); - if let Ok(metadata) = maybe_metadata { - self.set_metadata(Some(metadata)); + if let Ok(metadata) = Metadata::from_json(ev.content.clone()) { + let profile = Profile::new(ev.pubkey, ev.created_at, metadata); + if let Some(existing_profile) = &self.profile { + if existing_profile.created_at > profile.created_at { + // TODO + } + } + + self.set_profile(Some(profile)); } } _ => {} diff --git a/src/nostr/profile.rs b/src/nostr/profile.rs index 42d770c7..4d0f9708 100644 --- a/src/nostr/profile.rs +++ b/src/nostr/profile.rs @@ -3,6 +3,7 @@ use nostr_sdk::prelude::*; use crate::nostr::Metadata; use crate::text::shorten_hex; +#[derive(Clone, Debug)] pub struct Profile { pub pubkey: XOnlyPublicKey, pub created_at: Timestamp, diff --git a/src/widgets/text_note.rs b/src/widgets/text_note.rs index 276da95c..aa579ac8 100644 --- a/src/widgets/text_note.rs +++ b/src/widgets/text_note.rs @@ -1,17 +1,18 @@ use std::collections::HashSet; use chrono::{DateTime, Local}; -use nostr_sdk::{Event, Metadata, Tag, ToBech32}; +use nostr_sdk::prelude::*; use ratatui::{prelude::*, widgets::*}; use thousands::Separable; use tui_widget_list::Listable; +use crate::nostr::Profile; use crate::widgets::{PublicKey, ShrinkText}; #[derive(Clone, Debug)] pub struct TextNote { pub event: Event, - pub profile: Option, + pub profile: Option, pub reactions: HashSet, pub reposts: HashSet, pub zap_receipts: HashSet, @@ -24,7 +25,7 @@ pub struct TextNote { impl TextNote { pub fn new( event: Event, - profile: Option, + profile: Option, reactions: HashSet, reposts: HashSet, zap_receipts: HashSet, @@ -46,7 +47,7 @@ impl TextNote { pub fn display_name(&self) -> Option { if let Some(profile) = self.profile.clone() { - if let Some(display_name) = profile.display_name { + if let Some(display_name) = profile.metadata.display_name { if !display_name.is_empty() { return Some(display_name); } @@ -58,7 +59,7 @@ impl TextNote { pub fn name(&self) -> Option { if let Some(profile) = self.profile.clone() { - if let Some(name) = profile.name { + if let Some(name) = profile.metadata.name { if !name.is_empty() { match self.display_name() { Some(display_name) if name == display_name => return None, @@ -247,11 +248,14 @@ impl Listable for TextNote { #[cfg(test)] mod tests { - use nostr_sdk::JsonUtil; + use std::str::FromStr; + + use nostr_sdk::{secp256k1::XOnlyPublicKey, JsonUtil}; use pretty_assertions::assert_eq; use rstest::*; use super::*; + use crate::nostr::Metadata; #[fixture] fn event() -> Event { @@ -293,9 +297,20 @@ mod tests { area: Rect, padding: Padding, ) { + let profile = metadata.map(|metadata| { + Profile::new( + XOnlyPublicKey::from_str( + "4d39c23b3b03bf99494df5f3a149c7908ae1bc7416807fdd6b34a31886eaae25", + ) + .unwrap(), + Timestamp::now(), + metadata, + ) + }); + let note = TextNote::new( event, - metadata, + profile, HashSet::new(), HashSet::new(), HashSet::new(), @@ -319,9 +334,20 @@ mod tests { area: Rect, padding: Padding, ) { + let profile = metadata.map(|metadata| { + Profile::new( + XOnlyPublicKey::from_str( + "4d39c23b3b03bf99494df5f3a149c7908ae1bc7416807fdd6b34a31886eaae25", + ) + .unwrap(), + Timestamp::now(), + metadata, + ) + }); + let note = TextNote::new( event, - metadata, + profile, HashSet::new(), HashSet::new(), HashSet::new(),