stabilize ids and harden release pipeline

This commit is contained in:
2026-11-12 20:42:50 +07:00
parent fd802a8870
commit e9f29dc398
28 changed files with 1199 additions and 281 deletions
+1
View File
@@ -18,6 +18,7 @@ gpui.workspace = true
instant.workspace = true
nostr-sdk.workspace = true
anyhow.workspace = true
log.workspace = true
itertools.workspace = true
smallvec.workspace = true
serde.workspace = true
+99 -18
View File
@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::sync::{Arc, LazyLock, RwLock};
use std::time::Duration;
@@ -220,7 +221,7 @@ impl ChatPanel {
// Define all functions that will run after the current cycle
cx.defer_in(window, |this, window, cx| {
this.connect(cx);
this.connect(window, cx);
this.subscribe_room_events(window, cx);
this.get_messages(window, cx);
});
@@ -269,11 +270,24 @@ impl ChatPanel {
}
/// Get messaging relays and announcement for each member
fn connect(&mut self, cx: &mut Context<Self>) {
if let Some(room) = self.room.upgrade() {
let task = room.read(cx).connect(cx);
self.tasks.push(task);
}
fn connect(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some(room) = self.room.upgrade() else {
return;
};
let connect = room.read(cx).connect(cx);
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
if let Err(error) = connect.await {
log::error!("Connecting room relays failed: {error}");
this.update_in(cx, |this, window, cx| {
this.notify_failure("Connection failed", error, window, cx);
})?;
}
Ok(())
}));
}
/// Subscribe to room events
@@ -317,23 +331,40 @@ impl ChatPanel {
}
/// Load all messages belonging to this room
fn get_messages(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
fn get_messages(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Ok(get_messages) = self.room.read_with(cx, |this, cx| this.get_messages(cx)) else {
return;
};
self.tasks.push(cx.spawn(async move |this, cx| {
let events = get_messages.await?;
// Update message list
this.update(cx, |this, cx| {
this.insert_messages(&events, cx);
})?;
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
match get_messages.await {
Ok(events) => {
this.update(cx, |this, cx| {
this.insert_messages(&events, cx);
})?;
}
Err(error) => {
log::error!("Loading room messages failed: {error}");
this.update_in(cx, |this, window, cx| {
this.notify_failure("Failed to load messages", error, window, cx);
})?;
}
}
Ok(())
}));
}
/// Show a failure of a background operation to the user until dismissed
fn notify_failure(&self, title: &'static str, error: Error, window: &mut Window, cx: &mut App) {
window.push_notification(
Notification::error(error.to_string())
.title(title)
.autohide(false),
cx,
);
}
/// Get user input content and merged all attachments if available
fn get_input_value(&self, cx: &Context<Self>) -> String {
// Get input's value
@@ -566,8 +597,9 @@ impl ChatPanel {
for (i, message) in self.messages.iter().enumerate().skip(pos) {
self.message_index.insert(message.id, i);
}
let len = self.list_state.item_count();
self.list_state.splice(len..len, 1);
// The welcome row occupies row zero, so message rows sit one ahead
let row = pos + 1;
self.list_state.splice(row..row, 1);
if scroll {
self.list_state.scroll_to(ListOffset {
@@ -582,13 +614,62 @@ impl ChatPanel {
/// Convert and insert a vector of nostr events into the chat panel
fn insert_messages(&mut self, events: &[UnsignedEvent], cx: &mut Context<Self>) {
let mut incoming: Vec<Message> = Vec::with_capacity(events.len());
for event in events.iter() {
if event.kind == Kind::Reaction {
self.insert_reaction(event, cx);
} else {
incoming.push(event.into());
}
}
incoming.sort();
let old_len = self.messages.len();
let mut stored = std::mem::take(&mut self.messages).into_iter().peekable();
let mut merged: Vec<Message> = Vec::with_capacity(old_len + incoming.len());
let mut insert_start = None;
let mut inserted = 0usize;
for message in incoming {
while let Some(old) = stored.next_if(|old| old.cmp(&message) == Ordering::Less) {
merged.push(old);
}
let duplicate = stored
.peek()
.is_some_and(|old| old.cmp(&message) == Ordering::Equal)
|| merged
.last()
.is_some_and(|last| last.cmp(&message) == Ordering::Equal);
if duplicate {
continue;
}
// Bulk inserting messages, so no need to scroll to the latest message
self.insert_message(event, false, cx);
if inserted == 0 {
insert_start = Some(merged.len());
}
inserted += 1;
merged.push(message);
}
merged.extend(stored);
self.messages = merged;
if let Some(start) = insert_start {
self.message_index = self
.messages
.iter()
.enumerate()
.map(|(index, message)| (message.id, index))
.collect();
let row = start + 1;
self.list_state.splice(row..row, inserted);
cx.notify();
}
}