wip: refactor

This commit is contained in:
2025-01-02 08:34:10 +07:00
parent 3a2e5cb4ab
commit d53e75b775
21 changed files with 188 additions and 211 deletions

46
Cargo.lock generated
View File

@@ -1199,7 +1199,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"coop-ui",
"dirs 5.0.1", "dirs 5.0.1",
"gpui", "gpui",
"itertools 0.13.0", "itertools 0.13.0",
@@ -1214,28 +1213,7 @@ dependencies = [
"smol", "smol",
"tokio", "tokio",
"tracing-subscriber", "tracing-subscriber",
] "ui",
[[package]]
name = "coop-ui"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"gpui",
"image",
"itertools 0.13.0",
"nostr-sdk",
"once_cell",
"paste",
"regex",
"rust-embed",
"serde",
"serde_json",
"smallvec",
"smol",
"unicode-segmentation",
"uuid",
] ]
[[package]] [[package]]
@@ -5941,6 +5919,28 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "ui"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"gpui",
"image",
"itertools 0.13.0",
"nostr-sdk",
"once_cell",
"paste",
"regex",
"rust-embed",
"serde",
"serde_json",
"smallvec",
"smol",
"unicode-segmentation",
"uuid",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.8.1" version = "2.8.1"

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill="#000" fill-rule="evenodd" d="M19.432 2.738c.505.54.728 1.327.443 2.133-.606 1.713-1.798 3.124-2.797 4.087a15.74 15.74 0 0 1-1.045.921l.137.1c.93.684 1.416 1.975.757 3.118-1.221 2.12-4.356 5.803-11.192 5.803a.753.753 0 0 1-.15-.015A32.702 32.702 0 0 0 5.5 21.25a.75.75 0 0 1-1.5 0c0-4.43.821-8.93 2.909-12.485 2.106-3.587 5.49-6.182 10.492-6.749a2.404 2.404 0 0 1 2.031.722Z" clip-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill="#000" d="M3.999 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm9.499.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0ZM7.999 12c-1.765 0-3.236.635-4.365 1.72-1.117 1.074-1.868 2.557-2.282 4.225C.932 19.64 2.351 21 3.9 21h8.197c1.55 0 2.968-1.361 2.548-3.055-.413-1.668-1.164-3.151-2.281-4.225-1.13-1.085-2.6-1.72-4.365-1.72Zm6.174.715c1.21 1.337 1.983 3.011 2.414 4.749.231.934.167 1.79-.103 2.536h3.86c1.538 0 2.996-1.365 2.51-3.075C22.06 14.14 20.103 12 16.997 12c-1.08 0-2.023.26-2.825.715Z"/>
</svg>

After

Width:  |  Height:  |  Size: 595 B

View File

@@ -9,7 +9,7 @@ name = "coop"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
coop-ui = { path = "../ui" } ui = { path = "../ui" }
gpui.workspace = true gpui.workspace = true
reqwest_client.workspace = true reqwest_client.workspace = true

View File

@@ -1,5 +1,4 @@
use asset::Assets; use asset::Assets;
use coop_ui::Root;
use dirs::config_dir; use dirs::config_dir;
use gpui::*; use gpui::*;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
@@ -14,6 +13,7 @@ use tokio::{
sync::{mpsc, Mutex}, sync::{mpsc, Mutex},
time::sleep, time::sleep,
}; };
use ui::Root;
use constants::{ALL_MESSAGES_SUB_ID, APP_NAME, FAKE_SIG, METADATA_DELAY, NEW_MESSAGE_SUB_ID}; use constants::{ALL_MESSAGES_SUB_ID, APP_NAME, FAKE_SIG, METADATA_DELAY, NEW_MESSAGE_SUB_ID};
use states::{ use states::{
@@ -81,7 +81,7 @@ async fn main() {
_ = client.connect().await; _ = client.connect().await;
// Signal // Signal
let (signal_tx, mut signal_rx) = mpsc::channel::<Signal>(10000); let (signal_tx, mut signal_rx) = mpsc::channel::<Signal>(4000); // TODO: adjust?
let (mta_tx, mut mta_rx) = mpsc::unbounded_channel::<PublicKey>(); let (mta_tx, mut mta_rx) = mpsc::unbounded_channel::<PublicKey>();
// Re use sender // Re use sender
@@ -92,6 +92,7 @@ async fn main() {
tokio::spawn(async move { tokio::spawn(async move {
let sig = Signature::from_str(FAKE_SIG).unwrap(); let sig = Signature::from_str(FAKE_SIG).unwrap();
let new_message = SubscriptionId::new(NEW_MESSAGE_SUB_ID); let new_message = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
let all_messages = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
while let Ok(notification) = notifications.recv().await { while let Ok(notification) = notifications.recv().await {
#[allow(clippy::collapsible_match)] #[allow(clippy::collapsible_match)]
@@ -142,8 +143,10 @@ async fn main() {
} }
} }
} else if let RelayMessage::EndOfStoredEvents(subscription_id) = message { } else if let RelayMessage::EndOfStoredEvents(subscription_id) = message {
if let Err(e) = signal_tx.send(Signal::RecvEose(subscription_id)).await { if subscription_id == all_messages {
println!("Send error: {}", e) if let Err(e) = signal_tx.send(Signal::RecvEose(subscription_id)).await {
println!("Send error: {}", e)
}
} }
} }
} }
@@ -203,50 +206,32 @@ async fn main() {
SignalRegistry::set_global(cx, mta_tx_clone); SignalRegistry::set_global(cx, mta_tx_clone);
// Initialize components // Initialize components
coop_ui::init(cx); ui::init(cx);
// Set quit action // Set quit action
cx.on_action(quit); cx.on_action(quit);
/*
cx.spawn(|async_cx| async move { cx.spawn(|async_cx| async move {
let accounts = get_all_accounts_from_keyring(); let (tx, rx) = smol::channel::unbounded::<Signal>();
// Automatically Login if only have 1 account async_cx
if let Some(account) = accounts.into_iter().next() { .background_executor()
if let Ok(keys) = get_keys_by_account(account) { .spawn(async move {
get_client().set_signer(keys).await; while let Some(signal) = signal_rx.recv().await {
if let Err(e) = tx.send(signal).await {
_ = async_cx.update_global::<AccountRegistry, _>(|state, _| { println!("Send error: {}", e)
state.set_user(Some(account));
});
}
}
})
.detach();
*/
cx.spawn(|async_cx| async move {
let all_messages = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
let mut is_initialized = false;
while let Some(signal) = signal_rx.recv().await {
match signal {
Signal::RecvEose(id) => {
if id == all_messages {
if !is_initialized {
_ = async_cx.update_global::<ChatRegistry, _>(|state, _| {
state.set_init();
});
is_initialized = true;
} else {
_ = async_cx.update_global::<ChatRegistry, _>(|state, _| {
state.set_reload();
});
}
} }
} }
})
.detach();
while let Ok(signal) = rx.recv().await {
match signal {
Signal::RecvEose(_) => {
_ = async_cx.update_global::<ChatRegistry, _>(|state, _| {
state.update();
});
}
Signal::RecvEvent(event) => { Signal::RecvEvent(event) => {
let metadata = async_cx let metadata = async_cx
.background_executor() .background_executor()
@@ -277,7 +262,7 @@ async fn main() {
_ = async_cx.update_global::<MetadataRegistry, _>(|state, _cx| { _ = async_cx.update_global::<MetadataRegistry, _>(|state, _cx| {
state.seen(public_key, metadata); state.seen(public_key, metadata);
}) });
} }
_ => {} _ => {}
} }

View File

@@ -15,6 +15,7 @@ pub struct Room {
pub last_seen: Timestamp, pub last_seen: Timestamp,
pub title: Option<SharedString>, pub title: Option<SharedString>,
pub metadata: Option<Metadata>, pub metadata: Option<Metadata>,
is_initialized: bool,
} }
impl Room { impl Room {
@@ -48,6 +49,7 @@ impl Room {
last_seen, last_seen,
owner, owner,
metadata, metadata,
is_initialized: false,
} }
} }
} }
@@ -71,12 +73,12 @@ impl ChatRegistry {
cx.set_global(Self::new()); cx.set_global(Self::new());
} }
pub fn set_init(&mut self) { pub fn update(&mut self) {
self.is_initialized = true; if !self.is_initialized {
} self.is_initialized = true;
} else {
pub fn set_reload(&mut self) { self.reload = true;
self.reload = true; }
} }
pub fn push(&mut self, event: Event, metadata: Option<Metadata>) { pub fn push(&mut self, event: Event, metadata: Option<Metadata>) {

View File

@@ -1,11 +1,11 @@
use coop_ui::{ use gpui::*;
use nostr_sdk::prelude::*;
use prelude::FluentBuilder;
use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
popup_menu::PopupMenuExt, popup_menu::PopupMenuExt,
Icon, IconName, Sizable, Icon, IconName, Sizable,
}; };
use gpui::*;
use nostr_sdk::prelude::*;
use prelude::FluentBuilder;
use crate::{ use crate::{
constants::IMAGE_SERVICE, constants::IMAGE_SERVICE,

View File

@@ -1,12 +1,12 @@
use coop_ui::{
dock::{DockArea, DockItem, DockPlacement},
theme::Theme,
Root, TitleBar,
};
use gpui::*; use gpui::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use ui::{
dock::{DockArea, DockItem, DockPlacement},
theme::Theme,
Root, TitleBar,
};
use super::{ use super::{
account::Account, chat::ChatPanel, contact::ContactPanel, onboarding::Onboarding, account::Account, chat::ChatPanel, contact::ContactPanel, onboarding::Onboarding,

View File

@@ -1,7 +1,7 @@
use coop_ui::{theme::ActiveTheme, StyledExt};
use gpui::*; use gpui::*;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use ui::{theme::ActiveTheme, StyledExt};
use crate::{ use crate::{
constants::IMAGE_SERVICE, constants::IMAGE_SERVICE,

View File

@@ -1,13 +1,13 @@
use std::sync::Arc; use std::sync::Arc;
use coop_ui::{ use gpui::*;
use nostr_sdk::prelude::*;
use room::RoomPanel;
use ui::{
button::Button, button::Button,
dock::{Panel, PanelEvent, PanelState}, dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu, popup_menu::PopupMenu,
}; };
use gpui::*;
use nostr_sdk::prelude::*;
use room::RoomPanel;
use crate::states::chat::Room; use crate::states::chat::Room;

View File

@@ -1,13 +1,13 @@
use coop_ui::{ use gpui::*;
use itertools::Itertools;
use nostr_sdk::prelude::*;
use std::sync::Arc;
use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
input::{InputEvent, TextInput}, input::{InputEvent, TextInput},
theme::ActiveTheme, theme::ActiveTheme,
v_flex, Icon, IconName, v_flex, Icon, IconName,
}; };
use gpui::*;
use itertools::Itertools;
use nostr_sdk::prelude::*;
use std::sync::Arc;
use super::message::RoomMessage; use super::message::RoomMessage;
use crate::{ use crate::{
@@ -43,7 +43,7 @@ impl RoomPanel {
let input = cx.new_view(|cx| { let input = cx.new_view(|cx| {
TextInput::new(cx) TextInput::new(cx)
.appearance(false) .appearance(false)
.text_size(coop_ui::Size::Small) .text_size(ui::Size::Small)
.placeholder("Message...") .placeholder("Message...")
.cleanable() .cleanable()
}); });

View File

@@ -1,7 +1,7 @@
use coop_ui::StyledExt;
use gpui::*; use gpui::*;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use ui::theme::ActiveTheme;
use crate::{ use crate::{
constants::IMAGE_SERVICE, constants::IMAGE_SERVICE,
@@ -64,7 +64,7 @@ impl ContactListItem {
impl Render for ContactListItem { impl Render for ContactListItem {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let fallback = show_npub(self.public_key, 16); let fallback = show_npub(self.public_key, 16);
let mut content = div(); let mut content = div().h_10().text_sm();
if let Some(metadata) = self.metadata.read(cx).as_ref() { if let Some(metadata) = self.metadata.read(cx).as_ref() {
content = content content = content
@@ -73,15 +73,14 @@ impl Render for ContactListItem {
.gap_2() .gap_2()
.map(|this| { .map(|this| {
if let Some(picture) = metadata.picture.clone() { if let Some(picture) = metadata.picture.clone() {
this.flex_shrink_0().child( this.child(
img(format!("{}/?url={}&w=72&h=72&n=-1", IMAGE_SERVICE, picture)) img(format!("{}/?url={}&w=72&h=72&n=-1", IMAGE_SERVICE, picture))
.size_6() .size_8()
.rounded_full() .rounded_full()
.object_fit(ObjectFit::Cover), .object_fit(ObjectFit::Cover),
) )
} else { } else {
this.flex_shrink_0() this.child(img("brand/avatar.png").size_8().rounded_full())
.child(img("brand/avatar.png").size_6().rounded_full())
} }
}) })
.map(|this| { .map(|this| {
@@ -96,20 +95,18 @@ impl Render for ContactListItem {
.flex() .flex()
.items_center() .items_center()
.gap_2() .gap_2()
.child( .child(img("brand/avatar.png").size_8().rounded_full())
img("brand/avatar.png")
.flex_shrink_0()
.size_6()
.rounded_full(),
)
.child(fallback) .child(fallback)
} }
div() div()
.scrollable( .w_full()
cx.view().entity_id(), .px_2()
coop_ui::scroll::ScrollbarAxis::Vertical, .rounded_md()
) .hover(|this| {
this.bg(cx.theme().muted)
.text_color(cx.theme().muted_foreground)
})
.child(content) .child(content)
} }
} }

View File

@@ -1,57 +0,0 @@
use gpui::*;
use prelude::FluentBuilder;
use std::time::Duration;
use super::item::ContactListItem;
use crate::get_client;
pub struct ContactList {
contacts: Model<Option<Vec<View<ContactListItem>>>>,
}
impl ContactList {
pub fn new(cx: &mut ViewContext<'_, Self>) -> Self {
let contacts = cx.new_model(|_| None);
let async_contacts = contacts.clone();
let mut async_cx = cx.to_async();
cx.foreground_executor()
.spawn({
let client = get_client();
async move {
if let Ok(contacts) = async_cx
.background_executor()
.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(views);
cx.notify();
});
}
}
})
.detach();
Self { contacts }
}
}
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.children(contacts.clone())
})
}
}

View File

@@ -1,13 +1,19 @@
use coop_ui::{ use std::time::Duration;
use gpui::*;
use item::ContactListItem;
use prelude::FluentBuilder;
use ui::{
button::Button, button::Button,
dock::{Panel, PanelEvent, PanelState}, dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu, popup_menu::PopupMenu,
scroll::ScrollbarAxis,
v_flex, StyledExt,
}; };
use gpui::*;
use list::ContactList; use crate::get_client;
mod item; mod item;
mod list;
pub struct ContactPanel { pub struct ContactPanel {
name: SharedString, name: SharedString,
@@ -15,7 +21,8 @@ pub struct ContactPanel {
zoomable: bool, zoomable: bool,
focus_handle: FocusHandle, focus_handle: FocusHandle,
// Contacts // Contacts
list: View<ContactList>, view_id: EntityId,
contacts: Model<Option<Vec<View<ContactListItem>>>>,
} }
impl ContactPanel { impl ContactPanel {
@@ -24,14 +31,46 @@ impl ContactPanel {
} }
fn view(cx: &mut ViewContext<Self>) -> Self { fn view(cx: &mut ViewContext<Self>) -> Self {
let list = cx.new_view(ContactList::new); let contacts = cx.new_model(|_| None);
let async_contacts = contacts.clone();
let mut async_cx = cx.to_async();
cx.foreground_executor()
.spawn({
let client = get_client();
async move {
if let Ok(contacts) = async_cx
.background_executor()
.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(views);
cx.notify();
});
}
}
})
.detach();
Self { Self {
name: "Contacts".into(), name: "Contacts".into(),
closeable: true, closeable: true,
zoomable: true, zoomable: true,
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
list, view_id: cx.entity_id(),
contacts,
} }
} }
} }
@@ -75,7 +114,14 @@ impl FocusableView for ContactPanel {
} }
impl Render for ContactPanel { impl Render for ContactPanel {
fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl IntoElement {
div().size_full().child(self.list.clone()) v_flex()
.scrollable(self.view_id, ScrollbarAxis::Vertical)
.w_full()
.gap_1()
.p_2()
.when_some(self.contacts.read(cx).as_ref(), |this, contacts| {
this.children(contacts.clone())
})
} }
} }

View File

@@ -1,8 +1,8 @@
use coop_ui::{theme::ActiveTheme, StyledExt};
use gpui::*; use gpui::*;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use std::sync::Arc; use std::sync::Arc;
use ui::{theme::ActiveTheme, StyledExt};
use crate::{ use crate::{
constants::IMAGE_SERVICE, constants::IMAGE_SERVICE,
@@ -12,23 +12,18 @@ use crate::{
views::app::{AddPanel, PanelKind}, views::app::{AddPanel, PanelKind},
}; };
pub struct InboxItem { pub struct InboxListItem {
id: SharedString, id: SharedString,
event: Event, event: Event,
metadata: Model<Option<Metadata>>, metadata: Model<Option<Metadata>>,
} }
impl InboxItem { impl InboxListItem {
pub fn new(event: Event, cx: &mut ViewContext<'_, Self>) -> Self { pub fn new(event: Event, cx: &mut ViewContext<'_, Self>) -> Self {
let pubkeys: Vec<PublicKey> = event.tags.public_keys().copied().collect(); let pubkeys: Vec<PublicKey> = event.tags.public_keys().copied().collect();
let id = get_room_id(&event.pubkey, &pubkeys).into(); let id = get_room_id(&event.pubkey, &pubkeys).into();
let metadata = cx.new_model(|_| None); let metadata = cx.new_model(|_| None);
drop(pubkeys);
// Request metadata
_ = cx.global::<SignalRegistry>().tx.send(event.pubkey);
// Reload when received metadata // Reload when received metadata
cx.observe_global::<MetadataRegistry>(|chat, cx| { cx.observe_global::<MetadataRegistry>(|chat, cx| {
chat.load_metadata(cx); chat.load_metadata(cx);
@@ -42,6 +37,10 @@ impl InboxItem {
} }
} }
pub fn request_metadata(&mut self, cx: &mut ViewContext<Self>) {
_ = cx.global::<SignalRegistry>().tx.send(self.event.pubkey);
}
pub fn load_metadata(&mut self, cx: &mut ViewContext<Self>) { pub fn load_metadata(&mut self, cx: &mut ViewContext<Self>) {
let public_key = self.event.pubkey; let public_key = self.event.pubkey;
let async_metadata = self.metadata.clone(); let async_metadata = self.metadata.clone();
@@ -74,12 +73,12 @@ impl InboxItem {
cx.dispatch_action(Box::new(AddPanel { cx.dispatch_action(Box::new(AddPanel {
panel: PanelKind::Room(room), panel: PanelKind::Room(room),
position: coop_ui::dock::DockPlacement::Center, position: ui::dock::DockPlacement::Center,
})) }))
} }
} }
impl Render for InboxItem { impl Render for InboxListItem {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let ago = ago(self.event.created_at.as_u64()); let ago = ago(self.event.created_at.as_u64());
let fallback_name = show_npub(self.event.pubkey, 16); let fallback_name = show_npub(self.event.pubkey, 16);

View File

@@ -1,20 +1,18 @@
use coop_ui::{
skeleton::Skeleton, theme::ActiveTheme, v_flex, Collapsible, Icon, IconName, StyledExt,
};
use gpui::*; use gpui::*;
use item::InboxItem;
use itertools::Itertools; use itertools::Itertools;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use std::cmp::Reverse; use std::cmp::Reverse;
use ui::{skeleton::Skeleton, theme::ActiveTheme, v_flex, Collapsible, Icon, IconName, StyledExt};
use super::inbox::item::InboxListItem;
use crate::{get_client, states::chat::ChatRegistry, utils::get_room_id}; use crate::{get_client, states::chat::ChatRegistry, utils::get_room_id};
pub mod item; pub mod item;
pub struct Inbox { pub struct Inbox {
label: SharedString, label: SharedString,
items: Model<Option<Vec<View<InboxItem>>>>, items: Model<Option<Vec<View<InboxListItem>>>>,
is_loading: bool, is_loading: bool,
is_collapsed: bool, is_collapsed: bool,
} }
@@ -56,7 +54,7 @@ impl Inbox {
// Create view for new chats only // Create view for new chats only
let new = messages let new = messages
.into_iter() .into_iter()
.map(|m| cx.new_view(|cx| InboxItem::new(m.event, cx))) .map(|m| cx.new_view(|cx| InboxListItem::new(m.event, cx)))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
cx.update_model(&this.items, |a, b| { cx.update_model(&this.items, |a, b| {
@@ -70,8 +68,9 @@ impl Inbox {
}) })
.detach(); .detach();
cx.observe_new_views::<InboxItem>(|chat, cx| { cx.observe_new_views::<InboxListItem>(|item, cx| {
chat.load_metadata(cx); item.request_metadata(cx);
item.load_metadata(cx);
}) })
.detach(); .detach();
@@ -124,17 +123,15 @@ impl Inbox {
}) })
.await; .await;
let views: Vec<View<InboxItem>> = events let views: Vec<View<InboxListItem>> = events
.into_iter() .into_iter()
.filter(|ev| { .filter(|ev| {
let keys = ev.tags.public_keys().copied().collect::<Vec<_>>(); let keys = ev.tags.public_keys().copied().collect::<Vec<_>>();
let new_id = get_room_id(&ev.pubkey, &keys); let new_id = get_room_id(&ev.pubkey, &keys);
drop(keys);
!current_rooms.iter().any(|id| id == &new_id) !current_rooms.iter().any(|id| id == &new_id)
}) })
.map(|ev| async_cx.new_view(|cx| InboxItem::new(ev, cx)).unwrap()) .map(|ev| async_cx.new_view(|cx| InboxListItem::new(ev, cx)).unwrap())
.collect(); .collect();
async_cx.update_model(&async_items, |model, cx| { async_cx.update_model(&async_items, |model, cx| {

View File

@@ -1,11 +1,11 @@
use async_utility::task::spawn; use async_utility::task::spawn;
use coop_ui::{
input::{InputEvent, TextInput},
label::Label,
};
use gpui::*; use gpui::*;
use keyring::Entry; use keyring::Entry;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use ui::{
input::{InputEvent, TextInput},
label::Label,
};
use crate::{constants::KEYRING_SERVICE, get_client, states::account::AccountRegistry}; use crate::{constants::KEYRING_SERVICE, get_client, states::account::AccountRegistry};
@@ -17,7 +17,7 @@ impl Onboarding {
pub fn new(cx: &mut ViewContext<'_, Self>) -> Self { pub fn new(cx: &mut ViewContext<'_, Self>) -> Self {
let input = cx.new_view(|cx| { let input = cx.new_view(|cx| {
let mut input = TextInput::new(cx); let mut input = TextInput::new(cx);
input.set_size(coop_ui::Size::Medium, cx); input.set_size(ui::Size::Medium, cx);
input input
}); });

View File

@@ -1,11 +1,11 @@
use coop_ui::{ use gpui::*;
use ui::{
button::{Button, ButtonVariants}, button::{Button, ButtonVariants},
dock::{Panel, PanelEvent, PanelState}, dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu, popup_menu::PopupMenu,
scroll::ScrollbarAxis, scroll::ScrollbarAxis,
v_flex, ContextModal, Icon, IconName, Sizable, StyledExt, v_flex, ContextModal, Icon, IconName, Sizable, StyledExt,
}; };
use gpui::*;
use super::inbox::Inbox; use super::inbox::Inbox;
use crate::views::app::{AddPanel, PanelKind}; use crate::views::app::{AddPanel, PanelKind};
@@ -93,9 +93,8 @@ impl Render for Sidebar {
.small() .small()
.ghost() .ghost()
.not_centered() .not_centered()
.bold() .icon(Icon::new(IconName::ComposeFill))
.icon(Icon::new(IconName::Plus)) .label("New Message")
.label("New")
.on_click(|_, cx| { .on_click(|_, cx| {
cx.open_modal(move |modal, _| modal.child("TODO")); cx.open_modal(move |modal, _| modal.child("TODO"));
}), }),
@@ -105,13 +104,12 @@ impl Render for Sidebar {
.small() .small()
.ghost() .ghost()
.not_centered() .not_centered()
.bold() .icon(Icon::new(IconName::GroupFill))
.icon(Icon::new(IconName::Group))
.label("Contacts") .label("Contacts")
.on_click(|_, cx| { .on_click(|_, cx| {
cx.dispatch_action(Box::new(AddPanel { cx.dispatch_action(Box::new(AddPanel {
panel: PanelKind::Contact, panel: PanelKind::Contact,
position: coop_ui::dock::DockPlacement::Center, position: ui::dock::DockPlacement::Center,
})) }))
}), }),
), ),

View File

@@ -1,11 +1,11 @@
use coop_ui::{ use gpui::*;
use ui::{
button::Button, button::Button,
dock::{Panel, PanelEvent, PanelState}, dock::{Panel, PanelEvent, PanelState},
popup_menu::PopupMenu, popup_menu::PopupMenu,
theme::{ActiveTheme, Colorize}, theme::{ActiveTheme, Colorize},
StyledExt, StyledExt,
}; };
use gpui::*;
pub struct WelcomePanel { pub struct WelcomePanel {
name: SharedString, name: SharedString,

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "coop-ui" name = "ui"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
publish = false publish = false

View File

@@ -30,6 +30,7 @@ pub enum IconName {
CircleX, CircleX,
Close, Close,
Copy, Copy,
ComposeFill,
Dash, Dash,
Delete, Delete,
Ellipsis, Ellipsis,
@@ -41,6 +42,7 @@ pub enum IconName {
GitHub, GitHub,
Globe, Globe,
Group, Group,
GroupFill,
Heart, Heart,
HeartOff, HeartOff,
Inbox, Inbox,
@@ -111,6 +113,7 @@ impl IconName {
Self::CircleX => "icons/circle-x.svg", Self::CircleX => "icons/circle-x.svg",
Self::Close => "icons/close.svg", Self::Close => "icons/close.svg",
Self::Copy => "icons/copy.svg", Self::Copy => "icons/copy.svg",
Self::ComposeFill => "icons/compose-fill.svg",
Self::Dash => "icons/dash.svg", Self::Dash => "icons/dash.svg",
Self::Delete => "icons/delete.svg", Self::Delete => "icons/delete.svg",
Self::Ellipsis => "icons/ellipsis.svg", Self::Ellipsis => "icons/ellipsis.svg",
@@ -122,6 +125,7 @@ impl IconName {
Self::GitHub => "icons/github.svg", Self::GitHub => "icons/github.svg",
Self::Globe => "icons/globe.svg", Self::Globe => "icons/globe.svg",
Self::Group => "icons/group.svg", Self::Group => "icons/group.svg",
Self::GroupFill => "icons/group-fill.svg",
Self::Heart => "icons/heart.svg", Self::Heart => "icons/heart.svg",
Self::HeartOff => "icons/heart-off.svg", Self::HeartOff => "icons/heart-off.svg",
Self::Inbox => "icons/inbox.svg", Self::Inbox => "icons/inbox.svg",