wip: refactor

This commit is contained in:
2024-12-24 12:08:33 +07:00
parent 063bb97f84
commit 37d810d9e5
5 changed files with 61 additions and 73 deletions

View File

@@ -35,3 +35,15 @@ keyring = { version = "3", features = [
"windows-native", "windows-native",
"sync-secret-service", "sync-secret-service",
] } ] }
[profile.release]
codegen-units = 1
lto = true
panic = "abort"
incremental = false
opt-level = "z"
strip = true
rpath = false
debug = false
debug-assertions = false
overflow-checks = false

View File

@@ -4,3 +4,4 @@ pub const FAKE_SIG: &str = "f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412
pub const NEW_MESSAGE_SUB_ID: &str = "listen_new_giftwrap"; pub const NEW_MESSAGE_SUB_ID: &str = "listen_new_giftwrap";
pub const ALL_MESSAGES_SUB_ID: &str = "listen_all_giftwraps"; pub const ALL_MESSAGES_SUB_ID: &str = "listen_all_giftwraps";
pub const METADATA_DELAY: u64 = 150; pub const METADATA_DELAY: u64 = 150;
pub const IMAGE_SERVICE: &str = "https://wsrv.nl";

View File

@@ -53,8 +53,8 @@ impl AppView {
let dock = cx.new_view(|cx| DockArea::new(DOCK_AREA.id, Some(DOCK_AREA.version), cx)); let dock = cx.new_view(|cx| DockArea::new(DOCK_AREA.id, Some(DOCK_AREA.version), cx));
cx.observe_global::<AccountRegistry>(|view, cx| { cx.observe_global::<AccountRegistry>(|view, cx| {
// TODO: save dock state and load previous state on startup
if cx.global::<AccountRegistry>().is_user_logged_in() { if cx.global::<AccountRegistry>().is_user_logged_in() {
// TODO: save dock state and load previous state on startup
Self::init_layout(view.dock.downgrade(), cx); Self::init_layout(view.dock.downgrade(), cx);
} }
}) })
@@ -71,6 +71,7 @@ impl AppView {
// Change theme // Change theme
Theme::change(mode, cx); Theme::change(mode, cx);
// Rerender // Rerender
cx.refresh(); cx.refresh();
} }

View File

@@ -6,6 +6,7 @@ use nostr_sdk::prelude::*;
use prelude::FluentBuilder; use prelude::FluentBuilder;
use crate::{ use crate::{
constants::IMAGE_SERVICE,
get_client, get_client,
states::{chat::Room, metadata::MetadataRegistry, signal::SignalRegistry}, states::{chat::Room, metadata::MetadataRegistry, signal::SignalRegistry},
utils::{ago, show_npub}, utils::{ago, show_npub},
@@ -70,10 +71,13 @@ impl RenderOnce for Item {
.map(|this| { .map(|this| {
if let Some(picture) = metadata.picture { if let Some(picture) = metadata.picture {
this.flex_shrink_0().child( this.flex_shrink_0().child(
img(picture) img(format!(
.size_6() "{}/?url={}&w=100&h=100&n=-1",
.rounded_full() IMAGE_SERVICE, picture
.object_fit(ObjectFit::Cover), ))
.size_6()
.rounded_full()
.object_fit(ObjectFit::Cover),
) )
} else { } else {
this.flex_shrink_0() this.flex_shrink_0()

View File

@@ -14,33 +14,43 @@ pub mod item;
pub struct Inbox { pub struct Inbox {
label: SharedString, label: SharedString,
events: Model<Option<Vec<Event>>>, items: Model<Option<Vec<View<InboxItem>>>>,
chats: Model<Vec<View<InboxItem>>>,
is_loading: bool, is_loading: bool,
is_fetching: bool,
is_collapsed: bool, is_collapsed: bool,
} }
impl Inbox { impl Inbox {
pub fn new(cx: &mut ViewContext<'_, Self>) -> Self { pub fn new(cx: &mut ViewContext<'_, Self>) -> Self {
let chats = cx.new_model(|_| Vec::new()); let items = cx.new_model(|_| None);
let events = cx.new_model(|_| None);
cx.observe_global::<ChatRegistry>(|inbox, cx| { cx.observe_global::<ChatRegistry>(|this, cx| {
let state = cx.global::<ChatRegistry>(); let state = cx.global::<ChatRegistry>();
if state.reload || (state.is_initialized && state.new_messages.is_empty()) { if state.reload || (state.is_initialized && state.new_messages.is_empty()) {
inbox.load(cx); this.load(cx);
} else { } else {
let new_messages = state.new_messages.clone(); #[allow(clippy::collapsible_if)]
if let Some(items) = this.items.read(cx).as_ref() {
// Get all new messages
let new_messages = state.new_messages.clone();
for message in new_messages.into_iter() { // Get all current chats
cx.update_model(&inbox.events, |model, b| { let current: Vec<PublicKey> = items
if let Some(events) = model { .iter()
if !events.iter().any(|ev| ev.pubkey == message.event.pubkey) { .map(|item| item.model.read(cx).sender)
events.push(message.event); .collect();
b.notify();
} // Create view for only new chats
let new = new_messages
.into_iter()
.filter(|m| current.iter().any(|pk| pk == &m.event.pubkey))
.map(|m| cx.new_view(|cx| InboxItem::new(m.event, cx)))
.collect::<Vec<_>>();
cx.update_model(&this.items, |a, b| {
if let Some(items) = a {
items.extend(new);
b.notify();
} }
}); });
} }
@@ -48,47 +58,15 @@ impl Inbox {
}) })
.detach(); .detach();
cx.observe(&events, |inbox, model, cx| {
// Show fetching indicator
inbox.set_fetching(cx);
let events: Option<Vec<Event>> = model.read(cx).clone();
if let Some(events) = events {
let views = inbox.chats.read(cx);
let public_keys: Vec<PublicKey> = views.iter().map(|v| v.read(cx).sender).collect();
for event in events
.into_iter()
.sorted_by_key(|ev| Reverse(ev.created_at))
{
if !public_keys.contains(&event.pubkey) {
let view = cx.new_view(|cx| InboxItem::new(event, cx));
cx.update_model(&inbox.chats, |a, b| {
a.push(view);
b.notify();
});
}
}
// Hide fetching indicator
inbox.set_fetching(cx);
}
})
.detach();
cx.observe_new_views::<InboxItem>(|chat, cx| { cx.observe_new_views::<InboxItem>(|chat, cx| {
chat.load_metadata(cx); chat.load_metadata(cx);
}) })
.detach(); .detach();
Self { Self {
events, items,
chats,
label: "Inbox".into(), label: "Inbox".into(),
is_loading: true, is_loading: true,
is_fetching: false,
is_collapsed: false, is_collapsed: false,
} }
} }
@@ -97,7 +75,7 @@ impl Inbox {
// Hide loading indicator // Hide loading indicator
self.set_loading(cx); self.set_loading(cx);
let async_events = self.events.clone(); let async_items = self.items.clone();
let mut async_cx = cx.to_async(); let mut async_cx = cx.to_async();
cx.foreground_executor() cx.foreground_executor()
@@ -118,6 +96,7 @@ impl Inbox {
.into_iter() .into_iter()
.filter(|ev| ev.pubkey != public_key) // Filter all messages from current user .filter(|ev| ev.pubkey != public_key) // Filter all messages from current user
.unique_by(|ev| ev.pubkey) .unique_by(|ev| ev.pubkey)
.sorted_by_key(|ev| Reverse(ev.created_at))
.collect::<Vec<_>>() .collect::<Vec<_>>()
} else { } else {
Vec::new() Vec::new()
@@ -125,8 +104,13 @@ impl Inbox {
}) })
.await; .await;
async_cx.update_model(&async_events, |a, b| { let views: Vec<View<InboxItem>> = events
*a = Some(events); .into_iter()
.map(|ev| async_cx.new_view(|cx| InboxItem::new(ev, cx)).unwrap())
.collect();
async_cx.update_model(&async_items, |a, b| {
*a = Some(views);
b.notify(); b.notify();
}) })
}) })
@@ -137,11 +121,6 @@ impl Inbox {
self.is_loading = false; self.is_loading = false;
cx.notify(); cx.notify();
} }
fn set_fetching(&mut self, cx: &mut ViewContext<Self>) {
self.is_fetching = !self.is_fetching;
cx.notify();
}
} }
impl Collapsible for Inbox { impl Collapsible for Inbox {
@@ -158,7 +137,6 @@ impl Collapsible for Inbox {
impl Render for Inbox { impl Render for Inbox {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let mut content = div(); let mut content = div();
let chats = self.chats.read(cx);
if self.is_loading { if self.is_loading {
content = content.children((0..5).map(|_| { content = content.children((0..5).map(|_| {
@@ -171,18 +149,10 @@ impl Render for Inbox {
.child(Skeleton::new().flex_shrink_0().size_6().rounded_full()) .child(Skeleton::new().flex_shrink_0().size_6().rounded_full())
.child(Skeleton::new().w_20().h_3().rounded_sm()) .child(Skeleton::new().w_20().h_3().rounded_sm())
})) }))
} else if let Some(items) = self.items.read(cx).as_ref() {
content = content.children(items.clone())
} else { } else {
content = content // TODO: handle error
.children(chats.clone())
.when(self.is_fetching, |this| {
this.h_8()
.px_1()
.flex()
.items_center()
.gap_2()
.child(Skeleton::new().flex_shrink_0().size_6().rounded_full())
.child(Skeleton::new().w_20().h_3().rounded_sm())
});
} }
v_flex() v_flex()