.
This commit is contained in:
@@ -4,11 +4,11 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use common::{EventUtils, RenderedProfile};
|
use common::EventUtils;
|
||||||
use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task};
|
use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::{Person, PersonRegistry};
|
||||||
use state::{tracker, NostrRegistry};
|
use state::{tracker, NostrRegistry};
|
||||||
|
|
||||||
use crate::NewMessage;
|
use crate::NewMessage;
|
||||||
@@ -264,9 +264,9 @@ impl Room {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the display image for the room
|
/// Gets the display image for the room
|
||||||
pub fn display_image(&self, proxy: bool, cx: &App) -> SharedString {
|
pub fn display_image(&self, cx: &App) -> SharedString {
|
||||||
if !self.is_group() {
|
if !self.is_group() {
|
||||||
self.display_member(cx).avatar(proxy)
|
self.display_member(cx).avatar()
|
||||||
} else {
|
} else {
|
||||||
SharedString::from("brand/group.png")
|
SharedString::from("brand/group.png")
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ impl Room {
|
|||||||
/// Get a member to represent the room
|
/// Get a member to represent the room
|
||||||
///
|
///
|
||||||
/// Display member is always different from the current user.
|
/// Display member is always different from the current user.
|
||||||
pub fn display_member(&self, cx: &App) -> Profile {
|
pub fn display_member(&self, cx: &App) -> Person {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||||
@@ -295,7 +295,7 @@ impl Room {
|
|||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
|
|
||||||
if self.is_group() {
|
if self.is_group() {
|
||||||
let profiles: Vec<Profile> = self
|
let profiles: Vec<Person> = self
|
||||||
.members
|
.members
|
||||||
.iter()
|
.iter()
|
||||||
.map(|public_key| persons.read(cx).get(public_key, cx))
|
.map(|public_key| persons.read(cx).get(public_key, cx))
|
||||||
@@ -314,7 +314,7 @@ impl Room {
|
|||||||
|
|
||||||
SharedString::from(name)
|
SharedString::from(name)
|
||||||
} else {
|
} else {
|
||||||
self.display_member(cx).display_name()
|
self.display_member(cx).name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
pub use actions::*;
|
pub use actions::*;
|
||||||
use chat::{Message, RenderedMessage, Room, RoomEvent, RoomKind, SendReport};
|
use chat::{Message, RenderedMessage, Room, RoomEvent, RoomKind, SendReport};
|
||||||
use common::{nip96_upload, RenderedProfile, RenderedTimestamp};
|
use common::{nip96_upload, RenderedTimestamp};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, img, list, px, red, relative, rems, svg, white, AnyElement, App, AppContext,
|
div, img, list, px, red, relative, rems, svg, white, AnyElement, App, AppContext,
|
||||||
@@ -16,7 +16,7 @@ use gpui_tokio::Tokio;
|
|||||||
use indexset::{BTreeMap, BTreeSet};
|
use indexset::{BTreeMap, BTreeSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::{Person, PersonRegistry};
|
||||||
use settings::AppSettings;
|
use settings::AppSettings;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
@@ -506,7 +506,7 @@ impl ChatPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn profile(&self, public_key: &PublicKey, cx: &Context<Self>) -> Profile {
|
fn profile(&self, public_key: &PublicKey, cx: &Context<Self>) -> Person {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
persons.read(cx).get(public_key, cx)
|
persons.read(cx).get(public_key, cx)
|
||||||
}
|
}
|
||||||
@@ -632,7 +632,7 @@ impl ChatPanel {
|
|||||||
this.child(
|
this.child(
|
||||||
div()
|
div()
|
||||||
.id(SharedString::from(format!("{ix}-avatar")))
|
.id(SharedString::from(format!("{ix}-avatar")))
|
||||||
.child(Avatar::new(author.avatar(proxy)).size(rems(2.)))
|
.child(Avatar::new(author.avatar()).size(rems(2.)))
|
||||||
.context_menu(move |this, _window, _cx| {
|
.context_menu(move |this, _window, _cx| {
|
||||||
let view = Box::new(OpenPublicKey(public_key));
|
let view = Box::new(OpenPublicKey(public_key));
|
||||||
let copy = Box::new(CopyPublicKey(public_key));
|
let copy = Box::new(CopyPublicKey(public_key));
|
||||||
@@ -657,7 +657,7 @@ impl ChatPanel {
|
|||||||
div()
|
div()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
.text_color(cx.theme().text)
|
.text_color(cx.theme().text)
|
||||||
.child(author.display_name()),
|
.child(author.name()),
|
||||||
)
|
)
|
||||||
.child(message.created_at.to_human_time())
|
.child(message.created_at.to_human_time())
|
||||||
.when_some(is_sent_success, |this, status| {
|
.when_some(is_sent_success, |this, status| {
|
||||||
@@ -714,7 +714,7 @@ impl ChatPanel {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_color(cx.theme().text_accent)
|
.text_color(cx.theme().text_accent)
|
||||||
.child(author.display_name()),
|
.child(author.name()),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
@@ -796,8 +796,8 @@ impl ChatPanel {
|
|||||||
fn render_report(report: &SendReport, cx: &App) -> impl IntoElement {
|
fn render_report(report: &SendReport, cx: &App) -> impl IntoElement {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let profile = persons.read(cx).get(&report.receiver, cx);
|
let profile = persons.read(cx).get(&report.receiver, cx);
|
||||||
let name = profile.display_name();
|
let name = profile.name();
|
||||||
let avatar = profile.avatar(true);
|
let avatar = profile.avatar();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
@@ -1080,7 +1080,7 @@ impl ChatPanel {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_color(cx.theme().text_accent)
|
.text_color(cx.theme().text_accent)
|
||||||
.child(profile.display_name()),
|
.child(profile.name()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -1134,7 +1134,7 @@ impl Panel for ChatPanel {
|
|||||||
.read_with(cx, |this, cx| {
|
.read_with(cx, |this, cx| {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
||||||
let label = this.display_name(cx);
|
let label = this.display_name(cx);
|
||||||
let url = this.display_image(proxy, cx);
|
let url = this.display_image(cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use common::RenderedProfile;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, ElementId, HighlightStyle, InteractiveText, IntoElement, SharedString,
|
AnyElement, App, ElementId, HighlightStyle, InteractiveText, IntoElement, SharedString,
|
||||||
StyledText, UnderlineStyle, Window,
|
StyledText, UnderlineStyle, Window,
|
||||||
@@ -255,7 +254,7 @@ fn render_pubkey(
|
|||||||
) {
|
) {
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let profile = persons.read(cx).get(&public_key, cx);
|
let profile = persons.read(cx).get(&public_key, cx);
|
||||||
let display_name = format!("@{}", profile.display_name());
|
let display_name = format!("@{}", profile.name());
|
||||||
|
|
||||||
text.replace_range(range.clone(), &display_name);
|
text.replace_range(range.clone(), &display_name);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use auto_update::{AutoUpdateStatus, AutoUpdater};
|
use auto_update::{AutoUpdateStatus, AutoUpdater};
|
||||||
use chat::{ChatEvent, ChatRegistry};
|
use chat::{ChatEvent, ChatRegistry};
|
||||||
use chat_ui::{CopyPublicKey, OpenPublicKey};
|
use chat_ui::{CopyPublicKey, OpenPublicKey};
|
||||||
use common::{RenderedProfile, DEFAULT_SIDEBAR_WIDTH};
|
use common::DEFAULT_SIDEBAR_WIDTH;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
deferred, div, px, relative, rems, App, AppContext, Axis, ClipboardItem, Context, Entity,
|
deferred, div, px, relative, rems, App, AppContext, Axis, ClipboardItem, Context, Entity,
|
||||||
@@ -14,7 +14,6 @@ use key_store::{Credential, KeyItem, KeyStore};
|
|||||||
use nostr_connect::prelude::*;
|
use nostr_connect::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
use relay_auth::RelayAuth;
|
use relay_auth::RelayAuth;
|
||||||
use settings::AppSettings;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::{ActiveTheme, Theme, ThemeMode, ThemeRegistry};
|
use theme::{ActiveTheme, Theme, ThemeMode, ThemeRegistry};
|
||||||
@@ -257,9 +256,9 @@ impl ChatSpace {
|
|||||||
|
|
||||||
this.update_in(cx, |_, window, cx| {
|
this.update_in(cx, |_, window, cx| {
|
||||||
match result {
|
match result {
|
||||||
Ok(profile) => {
|
Ok(person) => {
|
||||||
persons.update(cx, |this, cx| {
|
persons.update(cx, |this, cx| {
|
||||||
this.insert(profile, cx);
|
this.insert(person, cx);
|
||||||
// Close the edit profile modal
|
// Close the edit profile modal
|
||||||
window.close_all_modals(cx);
|
window.close_all_modals(cx);
|
||||||
});
|
});
|
||||||
@@ -476,7 +475,6 @@ impl ChatSpace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
|
||||||
let auto_update = AutoUpdater::global(cx);
|
let auto_update = AutoUpdater::global(cx);
|
||||||
|
|
||||||
let relay_auth = RelayAuth::global(cx);
|
let relay_auth = RelayAuth::global(cx);
|
||||||
@@ -562,9 +560,9 @@ impl ChatSpace {
|
|||||||
.reverse()
|
.reverse()
|
||||||
.transparent()
|
.transparent()
|
||||||
.icon(IconName::CaretDown)
|
.icon(IconName::CaretDown)
|
||||||
.child(Avatar::new(profile.avatar(proxy)).size(rems(1.45)))
|
.child(Avatar::new(profile.avatar()).size(rems(1.45)))
|
||||||
.popup_menu(move |this, _window, _cx| {
|
.popup_menu(move |this, _window, _cx| {
|
||||||
this.label(profile.display_name())
|
this.label(profile.name())
|
||||||
.menu_with_icon(
|
.menu_with_icon(
|
||||||
"Profile",
|
"Profile",
|
||||||
IconName::EmojiFill,
|
IconName::EmojiFill,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ use gpui::{
|
|||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use list_item::RoomListItem;
|
use list_item::RoomListItem;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use settings::AppSettings;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::{NostrRegistry, GIFTWRAP_SUBSCRIPTION};
|
use state::{NostrRegistry, GIFTWRAP_SUBSCRIPTION};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
@@ -540,7 +539,6 @@ impl Sidebar {
|
|||||||
range: Range<usize>,
|
range: Range<usize>,
|
||||||
cx: &Context<Self>,
|
cx: &Context<Self>,
|
||||||
) -> Vec<impl IntoElement> {
|
) -> Vec<impl IntoElement> {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
|
||||||
let mut items = Vec::with_capacity(range.end - range.start);
|
let mut items = Vec::with_capacity(range.end - range.start);
|
||||||
|
|
||||||
for ix in range {
|
for ix in range {
|
||||||
@@ -563,7 +561,7 @@ impl Sidebar {
|
|||||||
RoomListItem::new(ix)
|
RoomListItem::new(ix)
|
||||||
.room_id(room_id)
|
.room_id(room_id)
|
||||||
.name(this.display_name(cx))
|
.name(this.display_name(cx))
|
||||||
.avatar(this.display_image(proxy, cx))
|
.avatar(this.display_image(cx))
|
||||||
.public_key(member.public_key())
|
.public_key(member.public_key())
|
||||||
.kind(this.kind)
|
.kind(this.kind)
|
||||||
.created_at(this.created_at.to_ago())
|
.created_at(this.created_at.to_ago())
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
|
use person::Person;
|
||||||
use settings::AppSettings;
|
use settings::AppSettings;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use smol::fs;
|
use smol::fs;
|
||||||
@@ -233,7 +234,7 @@ impl UserProfile {
|
|||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Profile, Error>> {
|
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Person, Error>> {
|
||||||
let avatar = self.avatar_input.read(cx).value().to_string();
|
let avatar = self.avatar_input.read(cx).value().to_string();
|
||||||
let name = self.name_input.read(cx).value().to_string();
|
let name = self.name_input.read(cx).value().to_string();
|
||||||
let bio = self.bio_input.read(cx).value().to_string();
|
let bio = self.bio_input.read(cx).value().to_string();
|
||||||
@@ -274,7 +275,7 @@ impl UserProfile {
|
|||||||
|
|
||||||
// Return the updated profile
|
// Return the updated profile
|
||||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||||
let profile = Profile::new(event.pubkey, metadata);
|
let profile = Person::new(event.pubkey, metadata);
|
||||||
|
|
||||||
Ok(profile)
|
Ok(profile)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use common::{nip05_verify, shorten_pubkey, RenderedProfile};
|
use common::{nip05_verify, shorten_pubkey};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, relative, rems, App, AppContext, ClipboardItem, Context, Entity, IntoElement,
|
div, relative, rems, App, AppContext, ClipboardItem, Context, Entity, IntoElement,
|
||||||
@@ -8,8 +8,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::{Person, PersonRegistry};
|
||||||
use settings::AppSettings;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
@@ -23,7 +22,7 @@ pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProfileViewer {
|
pub struct ProfileViewer {
|
||||||
profile: Profile,
|
profile: Person,
|
||||||
|
|
||||||
/// Follow status
|
/// Follow status
|
||||||
followed: bool,
|
followed: bool,
|
||||||
@@ -134,7 +133,6 @@ impl ProfileViewer {
|
|||||||
|
|
||||||
impl Render for ProfileViewer {
|
impl Render for ProfileViewer {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
|
||||||
let bech32 = shorten_pubkey(self.profile.public_key(), 16);
|
let bech32 = shorten_pubkey(self.profile.public_key(), 16);
|
||||||
let shared_bech32 = SharedString::from(bech32);
|
let shared_bech32 = SharedString::from(bech32);
|
||||||
|
|
||||||
@@ -147,14 +145,14 @@ impl Render for ProfileViewer {
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.text_center()
|
.text_center()
|
||||||
.child(Avatar::new(self.profile.avatar(proxy)).size(rems(4.)))
|
.child(Avatar::new(self.profile.avatar()).size(rems(4.)))
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
.line_height(relative(1.25))
|
.line_height(relative(1.25))
|
||||||
.child(self.profile.display_name()),
|
.child(self.profile.name()),
|
||||||
)
|
)
|
||||||
.when_some(self.address(cx), |this, address| {
|
.when_some(self.address(cx), |this, address| {
|
||||||
this.child(
|
this.child(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use chat::{ChatRegistry, Room};
|
use chat::{ChatRegistry, Room};
|
||||||
use common::{nip05_profile, RenderedProfile, TextUtils, BOOTSTRAP_RELAYS};
|
use common::{nip05_profile, TextUtils, BOOTSTRAP_RELAYS};
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, relative, rems, uniform_list, App, AppContext, Context, Entity, InteractiveElement,
|
div, px, relative, rems, uniform_list, App, AppContext, Context, Entity, InteractiveElement,
|
||||||
@@ -13,7 +13,6 @@ use gpui::{
|
|||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
use settings::AppSettings;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
@@ -359,7 +358,6 @@ impl Compose {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn list_items(&self, range: Range<usize>, cx: &Context<Self>) -> Vec<impl IntoElement> {
|
fn list_items(&self, range: Range<usize>, cx: &Context<Self>) -> Vec<impl IntoElement> {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
|
||||||
let persons = PersonRegistry::global(cx);
|
let persons = PersonRegistry::global(cx);
|
||||||
let mut items = Vec::with_capacity(self.contacts.read(cx).len());
|
let mut items = Vec::with_capacity(self.contacts.read(cx).len());
|
||||||
|
|
||||||
@@ -383,8 +381,8 @@ impl Compose {
|
|||||||
h_flex()
|
h_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(Avatar::new(profile.avatar(proxy)).size(rems(1.75)))
|
.child(Avatar::new(profile.avatar()).size(rems(1.75)))
|
||||||
.child(profile.display_name()),
|
.child(profile.name()),
|
||||||
)
|
)
|
||||||
.when(contact.selected, |this| {
|
.when(contact.selected, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::{Person, PersonRegistry};
|
||||||
use settings::AppSettings;
|
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::NostrRegistry;
|
use state::NostrRegistry;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
@@ -23,7 +22,7 @@ pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Screening {
|
pub struct Screening {
|
||||||
profile: Profile,
|
profile: Person,
|
||||||
verified: bool,
|
verified: bool,
|
||||||
followed: bool,
|
followed: bool,
|
||||||
last_active: Option<Timestamp>,
|
last_active: Option<Timestamp>,
|
||||||
@@ -225,7 +224,6 @@ impl Screening {
|
|||||||
|
|
||||||
impl Render for Screening {
|
impl Render for Screening {
|
||||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
|
||||||
let shorten_pubkey = shorten_pubkey(self.profile.public_key(), 8);
|
let shorten_pubkey = shorten_pubkey(self.profile.public_key(), 8);
|
||||||
let total_mutuals = self.mutual_contacts.len();
|
let total_mutuals = self.mutual_contacts.len();
|
||||||
let last_active = self.last_active.map(|_| true);
|
let last_active = self.last_active.map(|_| true);
|
||||||
@@ -238,12 +236,12 @@ impl Render for Screening {
|
|||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.text_center()
|
.text_center()
|
||||||
.child(Avatar::new(self.profile.avatar(proxy)).size(rems(4.)))
|
.child(Avatar::new(self.profile.avatar()).size(rems(4.)))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.font_semibold()
|
.font_semibold()
|
||||||
.line_height(relative(1.25))
|
.line_height(relative(1.25))
|
||||||
.child(self.profile.display_name()),
|
.child(self.profile.name()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use common::{RenderedProfile, BUNKER_TIMEOUT};
|
use common::BUNKER_TIMEOUT;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, relative, rems, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter,
|
div, relative, rems, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter,
|
||||||
@@ -266,8 +266,8 @@ impl Render for Startup {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!self.loading, |this| {
|
.when(!self.loading, |this| {
|
||||||
let avatar = profile.avatar(true);
|
let avatar = profile.avatar();
|
||||||
let name = profile.display_name();
|
let name = profile.name();
|
||||||
|
|
||||||
this.child(
|
this.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ use anyhow::{anyhow, Error};
|
|||||||
use common::{EventUtils, BOOTSTRAP_RELAYS};
|
use common::{EventUtils, BOOTSTRAP_RELAYS};
|
||||||
use gpui::{App, AppContext, Context, Entity, Global, Task};
|
use gpui::{App, AppContext, Context, Entity, Global, Task};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
|
pub use person::*;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use state::{NostrRegistry, TIMEOUT};
|
use state::{NostrRegistry, TIMEOUT};
|
||||||
|
|
||||||
|
mod person;
|
||||||
|
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
|
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
|
||||||
}
|
}
|
||||||
@@ -22,7 +25,7 @@ impl Global for GlobalPersonRegistry {}
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PersonRegistry {
|
pub struct PersonRegistry {
|
||||||
/// Collection of all persons (user profiles)
|
/// Collection of all persons (user profiles)
|
||||||
persons: HashMap<PublicKey, Entity<Profile>>,
|
persons: HashMap<PublicKey, Entity<Person>>,
|
||||||
|
|
||||||
/// Set of public keys that have been seen
|
/// Set of public keys that have been seen
|
||||||
seen: Rc<RefCell<HashSet<PublicKey>>>,
|
seen: Rc<RefCell<HashSet<PublicKey>>>,
|
||||||
@@ -51,7 +54,7 @@ impl PersonRegistry {
|
|||||||
let client = nostr.read(cx).client();
|
let client = nostr.read(cx).client();
|
||||||
|
|
||||||
// Channel for communication between nostr and gpui
|
// Channel for communication between nostr and gpui
|
||||||
let (tx, rx) = flume::bounded::<Profile>(100);
|
let (tx, rx) = flume::bounded::<Person>(100);
|
||||||
let (mta_tx, mta_rx) = flume::bounded::<PublicKey>(100);
|
let (mta_tx, mta_rx) = flume::bounded::<PublicKey>(100);
|
||||||
|
|
||||||
let mut tasks = smallvec![];
|
let mut tasks = smallvec![];
|
||||||
@@ -81,9 +84,9 @@ impl PersonRegistry {
|
|||||||
tasks.push(
|
tasks.push(
|
||||||
// Update GPUI state
|
// Update GPUI state
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
while let Ok(profile) = rx.recv_async().await {
|
while let Ok(person) = rx.recv_async().await {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.insert(profile, cx);
|
this.insert(person, cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
@@ -99,9 +102,9 @@ impl PersonRegistry {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(profiles) => {
|
Ok(persons) => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.bulk_inserts(profiles, cx);
|
this.bulk_inserts(persons, cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
@@ -121,7 +124,7 @@ impl PersonRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Handle nostr notifications
|
/// Handle nostr notifications
|
||||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Profile>) {
|
async fn handle_notifications(client: &Client, tx: &flume::Sender<Person>) {
|
||||||
let mut notifications = client.notifications();
|
let mut notifications = client.notifications();
|
||||||
let mut processed_events = HashSet::new();
|
let mut processed_events = HashSet::new();
|
||||||
|
|
||||||
@@ -140,9 +143,9 @@ impl PersonRegistry {
|
|||||||
match event.kind {
|
match event.kind {
|
||||||
Kind::Metadata => {
|
Kind::Metadata => {
|
||||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||||
let profile = Profile::new(event.pubkey, metadata);
|
let person = Person::new(event.pubkey, metadata);
|
||||||
|
|
||||||
tx.send_async(profile).await.ok();
|
tx.send_async(person).await.ok();
|
||||||
}
|
}
|
||||||
Kind::ContactList => {
|
Kind::ContactList => {
|
||||||
let public_keys = event.extract_public_keys();
|
let public_keys = event.extract_public_keys();
|
||||||
@@ -214,51 +217,50 @@ impl PersonRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Load all user profiles from the database
|
/// Load all user profiles from the database
|
||||||
async fn load_persons(client: &Client) -> Result<Vec<Profile>, Error> {
|
async fn load_persons(client: &Client) -> Result<Vec<Person>, Error> {
|
||||||
let filter = Filter::new().kind(Kind::Metadata).limit(200);
|
let filter = Filter::new().kind(Kind::Metadata).limit(200);
|
||||||
let events = client.database().query(filter).await?;
|
let events = client.database().query(filter).await?;
|
||||||
|
|
||||||
let mut profiles = vec![];
|
let mut persons = vec![];
|
||||||
|
|
||||||
for event in events.into_iter() {
|
for event in events.into_iter() {
|
||||||
let metadata = Metadata::from_json(event.content).unwrap_or_default();
|
let metadata = Metadata::from_json(event.content).unwrap_or_default();
|
||||||
let profile = Profile::new(event.pubkey, metadata);
|
let person = Person::new(event.pubkey, metadata);
|
||||||
profiles.push(profile);
|
persons.push(person);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(profiles)
|
Ok(persons)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert batch of persons
|
/// Insert batch of persons
|
||||||
fn bulk_inserts(&mut self, profiles: Vec<Profile>, cx: &mut Context<Self>) {
|
fn bulk_inserts(&mut self, persons: Vec<Person>, cx: &mut Context<Self>) {
|
||||||
for profile in profiles.into_iter() {
|
for person in persons.into_iter() {
|
||||||
self.persons
|
self.persons.insert(person.public_key(), cx.new(|_| person));
|
||||||
.insert(profile.public_key(), cx.new(|_| profile));
|
|
||||||
}
|
}
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert or update a person
|
/// Insert or update a person
|
||||||
pub fn insert(&mut self, profile: Profile, cx: &mut App) {
|
pub fn insert(&mut self, person: Person, cx: &mut App) {
|
||||||
let public_key = profile.public_key();
|
let public_key = person.public_key();
|
||||||
|
|
||||||
match self.persons.get(&public_key) {
|
match self.persons.get(&public_key) {
|
||||||
Some(person) => {
|
Some(this) => {
|
||||||
person.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
*this = profile;
|
*this = person;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.persons.insert(public_key, cx.new(|_| profile));
|
self.persons.insert(public_key, cx.new(|_| person));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get single person by public key
|
/// Get single person by public key
|
||||||
pub fn get(&self, public_key: &PublicKey, cx: &App) -> Profile {
|
pub fn get(&self, public_key: &PublicKey, cx: &App) -> Person {
|
||||||
if let Some(profile) = self.persons.get(public_key) {
|
if let Some(person) = self.persons.get(public_key) {
|
||||||
return profile.read(cx).clone();
|
return person.read(cx).clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key = *public_key;
|
let public_key = *public_key;
|
||||||
@@ -277,6 +279,6 @@ impl PersonRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return a temporary profile with default metadata
|
// Return a temporary profile with default metadata
|
||||||
Profile::new(public_key, Metadata::default())
|
Person::new(public_key, Metadata::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
111
crates/person/src/person.rs
Normal file
111
crates/person/src/person.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use gpui::SharedString;
|
||||||
|
use nostr_sdk::prelude::*;
|
||||||
|
use state::Announcement;
|
||||||
|
|
||||||
|
/// Person
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Person {
|
||||||
|
public_key: PublicKey,
|
||||||
|
metadata: Metadata,
|
||||||
|
announcement: Option<Announcement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Person {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.public_key == other.public_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Person {}
|
||||||
|
|
||||||
|
impl PartialOrd for Person {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Person {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.name().cmp(&other.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Person {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.public_key.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PublicKey> for Person {
|
||||||
|
fn from(public_key: PublicKey) -> Self {
|
||||||
|
Self::new(public_key, Metadata::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Person {
|
||||||
|
pub fn new(public_key: PublicKey, metadata: Metadata) -> Self {
|
||||||
|
Self {
|
||||||
|
public_key,
|
||||||
|
metadata,
|
||||||
|
announcement: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get profile public key
|
||||||
|
pub fn public_key(&self) -> PublicKey {
|
||||||
|
self.public_key
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get profile metadata
|
||||||
|
pub fn metadata(&self) -> Metadata {
|
||||||
|
self.metadata.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get profile encryption keys announcement
|
||||||
|
pub fn announcement(&self) -> Option<Announcement> {
|
||||||
|
self.announcement.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get profile avatar
|
||||||
|
pub fn avatar(&self) -> SharedString {
|
||||||
|
self.metadata()
|
||||||
|
.picture
|
||||||
|
.as_ref()
|
||||||
|
.filter(|picture| !picture.is_empty())
|
||||||
|
.map(|picture| picture.into())
|
||||||
|
.unwrap_or_else(|| "brand/avatar.png".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get profile name
|
||||||
|
pub fn name(&self) -> SharedString {
|
||||||
|
if let Some(display_name) = self.metadata().display_name.as_ref() {
|
||||||
|
if !display_name.is_empty() {
|
||||||
|
return SharedString::from(display_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = self.metadata().name.as_ref() {
|
||||||
|
if !name.is_empty() {
|
||||||
|
return SharedString::from(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedString::from(shorten_pubkey(self.public_key(), 4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shorten a [`PublicKey`] to a string with the first and last `len` characters
|
||||||
|
///
|
||||||
|
/// Ex. `00000000:00000002`
|
||||||
|
pub fn shorten_pubkey(public_key: PublicKey, len: usize) -> String {
|
||||||
|
let Ok(pubkey) = public_key.to_bech32();
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"{}:{}",
|
||||||
|
&pubkey[0..(len + 1)],
|
||||||
|
&pubkey[pubkey.len() - len..]
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,6 +8,26 @@ pub struct Announcement {
|
|||||||
client_name: Option<String>,
|
client_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Event> for Announcement {
|
||||||
|
fn from(val: &Event) -> Self {
|
||||||
|
let public_key = val
|
||||||
|
.tags
|
||||||
|
.iter()
|
||||||
|
.find(|tag| tag.kind().as_str() == "n" || tag.kind().as_str() == "P")
|
||||||
|
.and_then(|tag| tag.content())
|
||||||
|
.and_then(|c| PublicKey::parse(c).ok())
|
||||||
|
.unwrap_or(val.pubkey);
|
||||||
|
|
||||||
|
let client_name = val
|
||||||
|
.tags
|
||||||
|
.find(TagKind::Client)
|
||||||
|
.and_then(|tag| tag.content())
|
||||||
|
.map(|c| c.to_string());
|
||||||
|
|
||||||
|
Self::new(val.id, client_name, public_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Announcement {
|
impl Announcement {
|
||||||
pub fn new(id: EventId, client_name: Option<String>, public_key: PublicKey) -> Self {
|
pub fn new(id: EventId, client_name: Option<String>, public_key: PublicKey) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -528,8 +528,18 @@ impl NostrRegistry {
|
|||||||
.limit(1)
|
.limit(1)
|
||||||
.author(public_key);
|
.author(public_key);
|
||||||
|
|
||||||
|
// Filter for encryption keys announcement
|
||||||
|
let encryption_keys = Filter::new()
|
||||||
|
.kind(Kind::Custom(10044))
|
||||||
|
.limit(1)
|
||||||
|
.author(public_key);
|
||||||
|
|
||||||
client
|
client
|
||||||
.subscribe_to(urls, vec![metadata, contact_list], Some(opts))
|
.subscribe_to(
|
||||||
|
urls,
|
||||||
|
vec![metadata, contact_list, encryption_keys],
|
||||||
|
Some(opts),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user