chore: fix rooms out of order while loading (#139)
* fix room out of order while loading * . * .
This commit is contained in:
28
Cargo.lock
generated
28
Cargo.lock
generated
@@ -76,12 +76,6 @@ dependencies = [
|
|||||||
"equator",
|
"equator",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "allocator-api2"
|
|
||||||
version = "0.2.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1255,6 +1249,7 @@ dependencies = [
|
|||||||
"gpui",
|
"gpui",
|
||||||
"gpui_tokio",
|
"gpui_tokio",
|
||||||
"i18n",
|
"i18n",
|
||||||
|
"indexset",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"nostr",
|
"nostr",
|
||||||
@@ -2164,6 +2159,15 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ftree"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ae0379499242d3b9355c5069b43b9417def8c9b09903b930db1fe49318dc9e9"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futf"
|
name = "futf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -2637,8 +2641,6 @@ version = "0.15.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
|
||||||
"equivalent",
|
|
||||||
"foldhash",
|
"foldhash",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3127,6 +3129,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexset"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff794cab64c942437d60272e215f923d466b23dfa6c999cdd0cafe5b6d170805"
|
||||||
|
dependencies = [
|
||||||
|
"ftree",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inout"
|
name = "inout"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -5061,7 +5072,6 @@ dependencies = [
|
|||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"global",
|
"global",
|
||||||
"gpui",
|
"gpui",
|
||||||
"hashbrown 0.15.5",
|
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"nostr",
|
"nostr",
|
||||||
|
|||||||
@@ -62,3 +62,4 @@ oneshot.workspace = true
|
|||||||
webbrowser.workspace = true
|
webbrowser.workspace = true
|
||||||
|
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||||
|
indexset = "0.12.3"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use common::display::{ReadableProfile, ReadableTimestamp};
|
use common::display::{ReadableProfile, ReadableTimestamp};
|
||||||
use common::nip96::nip96_upload;
|
use common::nip96::nip96_upload;
|
||||||
@@ -14,6 +12,7 @@ use gpui::{
|
|||||||
};
|
};
|
||||||
use gpui_tokio::Tokio;
|
use gpui_tokio::Tokio;
|
||||||
use i18n::{shared_t, t};
|
use i18n::{shared_t, t};
|
||||||
|
use indexset::{BTreeMap, BTreeSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use registry::message::{Message, RenderedMessage};
|
use registry::message::{Message, RenderedMessage};
|
||||||
@@ -51,14 +50,13 @@ pub struct Chat {
|
|||||||
// Chat Room
|
// Chat Room
|
||||||
room: Entity<Room>,
|
room: Entity<Room>,
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
messages: Vec<Message>,
|
messages: BTreeSet<Message>,
|
||||||
rendered_texts_by_id: HashMap<EventId, RenderedText>,
|
rendered_texts_by_id: BTreeMap<EventId, RenderedText>,
|
||||||
reports_by_id: HashMap<EventId, Vec<SendReport>>,
|
reports_by_id: BTreeMap<EventId, Vec<SendReport>>,
|
||||||
|
|
||||||
// New Message
|
// New Message
|
||||||
input: Entity<InputState>,
|
input: Entity<InputState>,
|
||||||
replies_to: Entity<Vec<EventId>>,
|
replies_to: Entity<Vec<EventId>>,
|
||||||
sending: bool,
|
|
||||||
|
|
||||||
// Media Attachment
|
// Media Attachment
|
||||||
attachments: Entity<Vec<Url>>,
|
attachments: Entity<Vec<Url>>,
|
||||||
@@ -75,7 +73,8 @@ pub struct Chat {
|
|||||||
|
|
||||||
impl Chat {
|
impl Chat {
|
||||||
pub fn new(room: Entity<Room>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
pub fn new(room: Entity<Room>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let list_state = ListState::new(1, ListAlignment::Bottom, px(1024.));
|
let attachments = cx.new(|_| vec![]);
|
||||||
|
let replies_to = cx.new(|_| vec![]);
|
||||||
let input = cx.new(|cx| {
|
let input = cx.new(|cx| {
|
||||||
InputState::new(window, cx)
|
InputState::new(window, cx)
|
||||||
.placeholder(t!("chat.placeholder"))
|
.placeholder(t!("chat.placeholder"))
|
||||||
@@ -87,8 +86,9 @@ impl Chat {
|
|||||||
.clean_on_escape()
|
.clean_on_escape()
|
||||||
});
|
});
|
||||||
|
|
||||||
let attachments = cx.new(|_| vec![]);
|
let messages = BTreeSet::from([Message::system()]);
|
||||||
let replies_to = cx.new(|_| vec![]);
|
let list_state = ListState::new(messages.len(), ListAlignment::Bottom, px(1024.));
|
||||||
|
|
||||||
let load_messages = room.read(cx).load_messages(cx);
|
let load_messages = room.read(cx).load_messages(cx);
|
||||||
|
|
||||||
let mut subscriptions = smallvec![];
|
let mut subscriptions = smallvec![];
|
||||||
@@ -154,10 +154,9 @@ impl Chat {
|
|||||||
image_cache: RetainAllImageCache::new(cx),
|
image_cache: RetainAllImageCache::new(cx),
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
uploading: false,
|
uploading: false,
|
||||||
sending: false,
|
rendered_texts_by_id: BTreeMap::new(),
|
||||||
messages: vec![Message::System],
|
reports_by_id: BTreeMap::new(),
|
||||||
rendered_texts_by_id: HashMap::new(),
|
messages,
|
||||||
reports_by_id: HashMap::new(),
|
|
||||||
room,
|
room,
|
||||||
list_state,
|
list_state,
|
||||||
input,
|
input,
|
||||||
@@ -223,35 +222,26 @@ impl Chat {
|
|||||||
css().sent_ids.read_blocking().contains(gift_wrap_id)
|
css().sent_ids.read_blocking().contains(gift_wrap_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the sending state of the chat panel
|
|
||||||
fn set_sending(&mut self, sending: bool, cx: &mut Context<Self>) {
|
|
||||||
self.sending = sending;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a message to all members of the chat
|
/// Send a message to all members of the chat
|
||||||
fn send_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn send_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
// Get the message which includes all attachments
|
// Get the message which includes all attachments
|
||||||
let content = self.input_content(cx);
|
let content = self.input_content(cx);
|
||||||
|
|
||||||
// Get the backup setting
|
|
||||||
let backup = AppSettings::get_backup_messages(cx);
|
|
||||||
|
|
||||||
// Return if message is empty
|
// Return if message is empty
|
||||||
if content.trim().is_empty() {
|
if content.trim().is_empty() {
|
||||||
window.push_notification(t!("chat.empty_message_error"), cx);
|
window.push_notification(t!("chat.empty_message_error"), cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark sending in progress
|
|
||||||
self.set_sending(true, cx);
|
|
||||||
|
|
||||||
// Temporary disable input
|
// Temporary disable input
|
||||||
self.input.update(cx, |this, cx| {
|
self.input.update(cx, |this, cx| {
|
||||||
this.set_loading(true, cx);
|
this.set_loading(true, cx);
|
||||||
this.set_disabled(true, cx);
|
this.set_disabled(true, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get the backup setting
|
||||||
|
let backup = AppSettings::get_backup_messages(cx);
|
||||||
|
|
||||||
// Get replies_to if it's present
|
// Get replies_to if it's present
|
||||||
let replies = self.replies_to.read(cx).clone();
|
let replies = self.replies_to.read(cx).clone();
|
||||||
|
|
||||||
@@ -266,17 +256,23 @@ impl Chat {
|
|||||||
// Create a task for sending the message in the background
|
// Create a task for sending the message in the background
|
||||||
let send_message = room.send_in_background(&content, replies, backup, cx);
|
let send_message = room.send_in_background(&content, replies, backup, cx);
|
||||||
|
|
||||||
// Optimistically update message list
|
cx.defer_in(window, |this, window, cx| {
|
||||||
self.insert_message(temp_message, cx);
|
// Optimistically update message list
|
||||||
|
this.insert_message(temp_message, cx);
|
||||||
|
|
||||||
// Remove all replies
|
// Scroll to reveal the new message
|
||||||
self.remove_all_replies(cx);
|
this.list_state
|
||||||
|
.scroll_to_reveal_item(this.messages.len() + 1);
|
||||||
|
|
||||||
// Reset the input state
|
// Remove all replies
|
||||||
self.input.update(cx, |this, cx| {
|
this.remove_all_replies(cx);
|
||||||
this.set_loading(false, cx);
|
|
||||||
this.set_disabled(false, cx);
|
// Reset the input state
|
||||||
this.set_value("", window, cx);
|
this.input.update(cx, |this, cx| {
|
||||||
|
this.set_loading(false, cx);
|
||||||
|
this.set_disabled(false, cx);
|
||||||
|
this.set_value("", window, cx);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Continue sending the message in the background
|
// Continue sending the message in the background
|
||||||
@@ -284,15 +280,20 @@ impl Chat {
|
|||||||
match send_message.await {
|
match send_message.await {
|
||||||
Ok(reports) => {
|
Ok(reports) => {
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
// Don't change the room kind if send failed
|
|
||||||
this.room.update(cx, |this, cx| {
|
this.room.update(cx, |this, cx| {
|
||||||
if this.kind != RoomKind::Ongoing {
|
if this.kind != RoomKind::Ongoing {
|
||||||
this.kind = RoomKind::Ongoing;
|
// Update the room kind to ongoing
|
||||||
cx.notify();
|
// But keep the room kind if send failed
|
||||||
|
if reports.iter().all(|r| !r.is_sent_success()) {
|
||||||
|
this.kind = RoomKind::Ongoing;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Insert the sent reports
|
||||||
this.reports_by_id.insert(temp_id, reports);
|
this.reports_by_id.insert(temp_id, reports);
|
||||||
this.sending = false;
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
@@ -340,62 +341,23 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert and insert a nostr event into the chat panel
|
/// Convert and insert a nostr event into the chat panel
|
||||||
fn insert_message<E>(&mut self, event: E, cx: &mut Context<Self>)
|
fn insert_message<E>(&mut self, event: E, _cx: &mut Context<Self>)
|
||||||
where
|
where
|
||||||
E: Into<RenderedMessage>,
|
E: Into<RenderedMessage>,
|
||||||
{
|
{
|
||||||
let old_len = self.messages.len();
|
let old_len = self.messages.len();
|
||||||
let new_len = 1;
|
|
||||||
|
|
||||||
// Extend the messages list with the new events
|
// Extend the messages list with the new events
|
||||||
self.messages.push(Message::user(event));
|
if self.messages.insert(Message::user(event)) {
|
||||||
|
self.list_state.splice(old_len..old_len, 1);
|
||||||
// Update list state with the new messages
|
}
|
||||||
self.list_state.splice(old_len..old_len, new_len);
|
|
||||||
|
|
||||||
cx.notify();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert and insert bulk nostr events into the chat panel
|
/// Convert and insert a vector of nostr events into the chat panel
|
||||||
fn insert_messages<E>(&mut self, events: E, cx: &mut Context<Self>)
|
fn insert_messages(&mut self, events: Vec<Event>, cx: &mut Context<Self>) {
|
||||||
where
|
for event in events.into_iter() {
|
||||||
E: IntoIterator,
|
self.insert_message(event, cx);
|
||||||
E::Item: Into<RenderedMessage>,
|
}
|
||||||
{
|
|
||||||
let old_events: HashSet<EventId> = self
|
|
||||||
.messages
|
|
||||||
.iter()
|
|
||||||
.filter_map(|msg| {
|
|
||||||
if let Message::User(rendered) = msg {
|
|
||||||
Some(rendered.id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let events: Vec<Message> = events
|
|
||||||
.into_iter()
|
|
||||||
.map(|ev| ev.into())
|
|
||||||
.filter(|msg: &RenderedMessage| !old_events.contains(&msg.id))
|
|
||||||
.map(Message::User)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let old_len = self.messages.len();
|
|
||||||
let new_len = events.len();
|
|
||||||
|
|
||||||
// Extend the messages list with the new events
|
|
||||||
self.messages.extend(events);
|
|
||||||
self.messages.sort_by(|a, b| match (a, b) {
|
|
||||||
(Message::System, Message::System) => std::cmp::Ordering::Equal,
|
|
||||||
(Message::System, Message::User(_)) => std::cmp::Ordering::Less,
|
|
||||||
(Message::User(_), Message::System) => std::cmp::Ordering::Greater,
|
|
||||||
(Message::User(a_msg), Message::User(b_msg)) => a_msg.created_at.cmp(&b_msg.created_at),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update list state with the new messages
|
|
||||||
self.list_state.splice(old_len..old_len, new_len);
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,15 +522,18 @@ impl Chat {
|
|||||||
.into_any_element()
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_message_not_found(&self, cx: &Context<Self>) -> AnyElement {
|
fn render_message_not_found(&self, ix: usize, cx: &Context<Self>) -> AnyElement {
|
||||||
div()
|
div()
|
||||||
|
.id(ix)
|
||||||
.w_full()
|
.w_full()
|
||||||
.py_1()
|
.py_1()
|
||||||
.px_3()
|
.px_3()
|
||||||
.child(
|
.child(
|
||||||
div()
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
.text_xs()
|
.text_xs()
|
||||||
.text_color(cx.theme().danger_foreground)
|
.text_color(cx.theme().danger_foreground)
|
||||||
|
.child(SharedString::from(ix.to_string()))
|
||||||
.child(shared_t!("chat.not_found")),
|
.child(shared_t!("chat.not_found")),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
@@ -1172,7 +1137,7 @@ impl Render for Chat {
|
|||||||
list(
|
list(
|
||||||
self.list_state.clone(),
|
self.list_state.clone(),
|
||||||
cx.processor(move |this, ix: usize, window, cx| {
|
cx.processor(move |this, ix: usize, window, cx| {
|
||||||
if let Some(message) = this.messages.get(ix) {
|
if let Some(message) = this.messages.get_index(ix) {
|
||||||
match message {
|
match message {
|
||||||
Message::User(rendered) => {
|
Message::User(rendered) => {
|
||||||
let text = this
|
let text = this
|
||||||
@@ -1183,10 +1148,10 @@ impl Render for Chat {
|
|||||||
|
|
||||||
this.render_message(ix, rendered, text, cx)
|
this.render_message(ix, rendered, text, cx)
|
||||||
}
|
}
|
||||||
Message::System => this.render_announcement(ix, cx),
|
Message::System(_) => this.render_announcement(ix, cx),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.render_message_not_found(cx)
|
this.render_message_not_found(ix, cx)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -117,6 +117,17 @@ pub struct CoopSimpleStorage {
|
|||||||
pub resend_queue: RwLock<HashMap<EventId, RelayUrl>>,
|
pub resend_queue: RwLock<HashMap<EventId, RelayUrl>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CoopSimpleStorage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
init_at: Timestamp::now(),
|
||||||
|
sent_ids: RwLock::new(HashSet::new()),
|
||||||
|
resent_ids: RwLock::new(Vec::new()),
|
||||||
|
resend_queue: RwLock::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
|
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
|
||||||
static INGESTER: OnceLock<Ingester> = OnceLock::new();
|
static INGESTER: OnceLock<Ingester> = OnceLock::new();
|
||||||
static COOP_SIMPLE_STORAGE: OnceLock<CoopSimpleStorage> = OnceLock::new();
|
static COOP_SIMPLE_STORAGE: OnceLock<CoopSimpleStorage> = OnceLock::new();
|
||||||
@@ -150,7 +161,7 @@ pub fn ingester() -> &'static Ingester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn css() -> &'static CoopSimpleStorage {
|
pub fn css() -> &'static CoopSimpleStorage {
|
||||||
COOP_SIMPLE_STORAGE.get_or_init(CoopSimpleStorage::default)
|
COOP_SIMPLE_STORAGE.get_or_init(CoopSimpleStorage::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_run() -> &'static bool {
|
pub fn first_run() -> &'static bool {
|
||||||
|
|||||||
@@ -19,4 +19,3 @@ smol.workspace = true
|
|||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
hashbrown = "0.15"
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use common::event::EventUtils;
|
use common::event::EventUtils;
|
||||||
@@ -6,7 +7,6 @@ use fuzzy_matcher::skim::SkimMatcherV2;
|
|||||||
use fuzzy_matcher::FuzzyMatcher;
|
use fuzzy_matcher::FuzzyMatcher;
|
||||||
use global::nostr_client;
|
use global::nostr_client;
|
||||||
use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Task, WeakEntity, Window};
|
use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Task, WeakEntity, Window};
|
||||||
use hashbrown::{HashMap, HashSet};
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use room::RoomKind;
|
use room::RoomKind;
|
||||||
@@ -251,9 +251,15 @@ impl Registry {
|
|||||||
|
|
||||||
/// Reset the registry.
|
/// Reset the registry.
|
||||||
pub fn reset(&mut self, cx: &mut Context<Self>) {
|
pub fn reset(&mut self, cx: &mut Context<Self>) {
|
||||||
self.rooms = vec![];
|
// Reset the loading status (default: true)
|
||||||
self.loading = true;
|
self.loading = true;
|
||||||
|
|
||||||
|
// Clear the current identity
|
||||||
self.identity = None;
|
self.identity = None;
|
||||||
|
|
||||||
|
// Clear all current rooms
|
||||||
|
self.rooms.clear();
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,12 +268,13 @@ impl Registry {
|
|||||||
log::info!("Starting to load chat rooms...");
|
log::info!("Starting to load chat rooms...");
|
||||||
|
|
||||||
// Get the contact bypass setting
|
// Get the contact bypass setting
|
||||||
let contact_bypass = AppSettings::get_contact_bypass(cx);
|
let bypass_setting = AppSettings::get_contact_bypass(cx);
|
||||||
|
|
||||||
let task: Task<Result<HashSet<Room>, Error>> = cx.background_spawn(async move {
|
let task: Task<Result<HashSet<Room>, Error>> = cx.background_spawn(async move {
|
||||||
let client = nostr_client();
|
let client = nostr_client();
|
||||||
let signer = client.signer().await?;
|
let signer = client.signer().await?;
|
||||||
let public_key = signer.get_public_key().await?;
|
let public_key = signer.get_public_key().await?;
|
||||||
|
let contacts = client.database().contacts_public_keys(public_key).await?;
|
||||||
|
|
||||||
// Get messages sent by the user
|
// Get messages sent by the user
|
||||||
let send = Filter::new()
|
let send = Filter::new()
|
||||||
@@ -300,13 +307,12 @@ impl Registry {
|
|||||||
public_keys.retain(|pk| pk != &public_key);
|
public_keys.retain(|pk| pk != &public_key);
|
||||||
|
|
||||||
// Bypass screening flag
|
// Bypass screening flag
|
||||||
let mut bypass = false;
|
let mut bypassed = false;
|
||||||
|
|
||||||
// If user enabled bypass screening for contacts
|
// If the user has enabled bypass screening in settings,
|
||||||
// Check if room's members are in contact with current user
|
// check if any of the room's members are contacts of the current user
|
||||||
if contact_bypass {
|
if bypass_setting {
|
||||||
let contacts = client.database().contacts_public_keys(public_key).await?;
|
bypassed = public_keys.iter().any(|k| contacts.contains(k));
|
||||||
bypass = public_keys.iter().any(|k| contacts.contains(k));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the current user has sent at least one message to this room
|
// Check if the current user has sent at least one message to this room
|
||||||
@@ -321,7 +327,7 @@ impl Registry {
|
|||||||
// Create a new room
|
// Create a new room
|
||||||
let room = Room::new(&event).rearrange_by(public_key);
|
let room = Room::new(&event).rearrange_by(public_key);
|
||||||
|
|
||||||
if is_ongoing || bypass {
|
if is_ongoing || bypassed {
|
||||||
rooms.insert(room.kind(RoomKind::Ongoing));
|
rooms.insert(room.kind(RoomKind::Ongoing));
|
||||||
} else {
|
} else {
|
||||||
rooms.insert(room);
|
rooms.insert(room);
|
||||||
@@ -349,23 +355,28 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn extend_rooms(&mut self, rooms: HashSet<Room>, cx: &mut Context<Self>) {
|
pub(crate) fn extend_rooms(&mut self, rooms: HashSet<Room>, cx: &mut Context<Self>) {
|
||||||
let mut room_map: HashMap<u64, usize> = HashMap::with_capacity(self.rooms.len());
|
let mut room_map: HashMap<u64, usize> = self
|
||||||
|
.rooms
|
||||||
for (index, room) in self.rooms.iter().enumerate() {
|
.iter()
|
||||||
room_map.insert(room.read(cx).id, index);
|
.enumerate()
|
||||||
}
|
.map(|(idx, room)| (room.read(cx).id, idx))
|
||||||
|
.collect();
|
||||||
|
|
||||||
for new_room in rooms.into_iter() {
|
for new_room in rooms.into_iter() {
|
||||||
// Check if we already have a room with this ID
|
// Check if we already have a room with this ID
|
||||||
if let Some(&index) = room_map.get(&new_room.id) {
|
if let Some(&index) = room_map.get(&new_room.id) {
|
||||||
self.rooms[index].update(cx, |this, cx| {
|
self.rooms[index].update(cx, |this, cx| {
|
||||||
*this = new_room;
|
if new_room.created_at > this.created_at {
|
||||||
cx.notify();
|
*this = new_room;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let new_index = self.rooms.len();
|
let new_room_id = new_room.id;
|
||||||
room_map.insert(new_room.id, new_index);
|
|
||||||
self.rooms.push(cx.new(|_| new_room));
|
self.rooms.push(cx.new(|_| new_room));
|
||||||
|
|
||||||
|
let new_index = self.rooms.len();
|
||||||
|
room_map.insert(new_room_id, new_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,9 +429,13 @@ impl Registry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(room) = self.rooms.iter().find(|room| room.read(cx).id == id) {
|
if let Some(room) = self.rooms.iter().find(|room| room.read(cx).id == id) {
|
||||||
|
let is_new_event = event.created_at > room.read(cx).created_at;
|
||||||
|
|
||||||
// Update room
|
// Update room
|
||||||
room.update(cx, |this, cx| {
|
room.update(cx, |this, cx| {
|
||||||
this.created_at(event.created_at, cx);
|
if is_new_event {
|
||||||
|
this.created_at(event.created_at, cx);
|
||||||
|
}
|
||||||
|
|
||||||
// Set this room is ongoing if the new message is from current user
|
// Set this room is ongoing if the new message is from current user
|
||||||
if author == identity {
|
if author == identity {
|
||||||
@@ -433,8 +448,10 @@ impl Registry {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-sort the rooms registry by their created at
|
// Resort all rooms in the registry by their created at (after updated)
|
||||||
self.sort(cx);
|
if is_new_event {
|
||||||
|
self.sort(cx);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let room = Room::new(&event)
|
let room = Room::new(&event)
|
||||||
.kind(RoomKind::default())
|
.kind(RoomKind::default())
|
||||||
|
|||||||
@@ -2,16 +2,37 @@ use std::hash::Hash;
|
|||||||
|
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
User(RenderedMessage),
|
User(RenderedMessage),
|
||||||
System,
|
System(Timestamp),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Message {
|
impl Message {
|
||||||
pub fn user(user: impl Into<RenderedMessage>) -> Self {
|
pub fn user(user: impl Into<RenderedMessage>) -> Self {
|
||||||
Self::User(user.into())
|
Self::User(user.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn system() -> Self {
|
||||||
|
Self::System(Timestamp::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Message {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
match (self, other) {
|
||||||
|
(Message::User(a), Message::User(b)) => a.cmp(b),
|
||||||
|
(Message::System(a), Message::System(b)) => a.cmp(b),
|
||||||
|
(Message::User(a), Message::System(b)) => a.created_at.cmp(b),
|
||||||
|
(Message::System(a), Message::User(b)) => a.cmp(&b.created_at),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Message {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
Reference in New Issue
Block a user