use common::profile::NostrProfile; use gpui::{ div, img, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Window, }; use nostr_sdk::prelude::*; use state::get_client; use ui::{ button::Button, dock_area::panel::{Panel, PanelEvent}, indicator::Indicator, popup_menu::PopupMenu, theme::{scale::ColorScaleStep, ActiveTheme}, Sizable, }; pub fn init(window: &mut Window, cx: &mut App) -> Entity { Contacts::new(window, cx) } pub struct Contacts { contacts: Entity>>, // Panel name: SharedString, closable: bool, zoomable: bool, focus_handle: FocusHandle, } impl Contacts { pub fn new(_window: &mut Window, cx: &mut App) -> Entity { let contacts = cx.new(|_| None); let async_contact = contacts.clone(); cx.spawn(|mut cx| async move { let client = get_client(); let (tx, rx) = oneshot::channel::>(); cx.background_executor() .spawn(async move { let signer = client.signer().await.unwrap(); let public_key = signer.get_public_key().await.unwrap(); if let Ok(profiles) = client.database().contacts(public_key).await { let members: Vec = profiles .into_iter() .map(|profile| { NostrProfile::new(profile.public_key(), profile.metadata()) }) .collect(); _ = tx.send(members); } }) .detach(); if let Ok(contacts) = rx.await { _ = cx.update_entity(&async_contact, |this, cx| { *this = Some(contacts); cx.notify(); }); } }) .detach(); cx.new(|cx| Self { contacts, name: "Contacts".into(), closable: true, zoomable: true, focus_handle: cx.focus_handle(), }) } } impl Panel for Contacts { fn panel_id(&self) -> SharedString { "ContactPanel".into() } fn title(&self, _cx: &App) -> AnyElement { self.name.clone().into_any_element() } fn closable(&self, _cx: &App) -> bool { self.closable } fn zoomable(&self, _cx: &App) -> bool { self.zoomable } fn popup_menu(&self, menu: PopupMenu, _cx: &App) -> PopupMenu { menu.track_focus(&self.focus_handle) } fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec