wip: refactor

This commit is contained in:
2025-01-01 18:51:19 +07:00
parent 8e6c462177
commit 3a2e5cb4ab
4 changed files with 138 additions and 13 deletions

View File

@@ -0,0 +1,115 @@
use coop_ui::StyledExt;
use gpui::*;
use nostr_sdk::prelude::*;
use prelude::FluentBuilder;
use crate::{
constants::IMAGE_SERVICE,
get_client,
states::{metadata::MetadataRegistry, signal::SignalRegistry},
utils::show_npub,
};
pub struct ContactListItem {
public_key: PublicKey,
metadata: Model<Option<Metadata>>,
}
impl ContactListItem {
pub fn new(public_key: PublicKey, cx: &mut ViewContext<'_, Self>) -> Self {
let metadata = cx.new_model(|_| None);
// Request metadata
_ = cx.global::<SignalRegistry>().tx.send(public_key);
// Reload when received metadata
cx.observe_global::<MetadataRegistry>(|item, cx| {
item.load_metadata(cx);
})
.detach();
Self {
public_key,
metadata,
}
}
pub fn load_metadata(&mut self, cx: &mut ViewContext<Self>) {
let public_key = self.public_key;
let async_metadata = self.metadata.clone();
let mut async_cx = cx.to_async();
cx.foreground_executor()
.spawn({
let client = get_client();
async move {
let query = async_cx
.background_executor()
.spawn(async move { client.database().metadata(public_key).await })
.await;
if let Ok(metadata) = query {
_ = async_cx.update_model(&async_metadata, |a, b| {
*a = metadata;
b.notify();
});
};
}
})
.detach();
}
}
impl Render for ContactListItem {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let fallback = show_npub(self.public_key, 16);
let mut content = div();
if let Some(metadata) = self.metadata.read(cx).as_ref() {
content = content
.flex()
.items_center()
.gap_2()
.map(|this| {
if let Some(picture) = metadata.picture.clone() {
this.flex_shrink_0().child(
img(format!("{}/?url={}&w=72&h=72&n=-1", IMAGE_SERVICE, picture))
.size_6()
.rounded_full()
.object_fit(ObjectFit::Cover),
)
} else {
this.flex_shrink_0()
.child(img("brand/avatar.png").size_6().rounded_full())
}
})
.map(|this| {
if let Some(display_name) = metadata.display_name.clone() {
this.child(display_name)
} else {
this.child(fallback)
}
})
} else {
content = content
.flex()
.items_center()
.gap_2()
.child(
img("brand/avatar.png")
.flex_shrink_0()
.size_6()
.rounded_full(),
)
.child(fallback)
}
div()
.scrollable(
cx.view().entity_id(),
coop_ui::scroll::ScrollbarAxis::Vertical,
)
.child(content)
}
}

View File

@@ -1,13 +1,12 @@
use gpui::*;
use prelude::FluentBuilder;
use std::time::Duration;
use gpui::*;
use nostr_sdk::prelude::*;
use prelude::FluentBuilder;
use super::item::ContactListItem;
use crate::get_client;
pub struct ContactList {
contacts: Model<Option<Vec<Contact>>>,
contacts: Model<Option<Vec<View<ContactListItem>>>>,
}
impl ContactList {
@@ -27,8 +26,17 @@ impl ContactList {
.spawn(async move { client.get_contact_list(Duration::from_secs(3)).await })
.await
{
let views: Vec<View<ContactListItem>> = contacts
.into_iter()
.map(|contact| {
async_cx
.new_view(|cx| ContactListItem::new(contact.public_key, cx))
.unwrap()
})
.collect();
_ = async_cx.update_model(&async_contacts, |model, cx| {
*model = Some(contacts);
*model = Some(views);
cx.notify();
});
}
@@ -43,7 +51,7 @@ impl ContactList {
impl Render for ContactList {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div().when_some(self.contacts.read(cx).as_ref(), |this, contacts| {
this.child("Total").child(contacts.len().to_string())
this.children(contacts.clone())
})
}
}

View File

@@ -6,6 +6,7 @@ use coop_ui::{
use gpui::*;
use list::ContactList;
mod item;
mod list;
pub struct ContactPanel {