add trash panel and re-tag stale cached rooms

This commit is contained in:
2026-11-13 21:22:42 +07:00
parent 3484c2c71b
commit eef4acc91f
8 changed files with 300 additions and 21 deletions
+57 -10
View File
@@ -397,6 +397,7 @@ impl ChatRegistry {
if !errors.is_empty() {
let mut newly_trashed = 0;
trash.update(cx, |trash, cx| {
newly_trashed = errors
.iter()
@@ -405,16 +406,9 @@ impl ChatRegistry {
cx.notify();
})?;
// Only newly trashed messages emit an error event so repeated
// reprocess attempts of the same message don't re-alert the user
if newly_trashed > 0 {
let reason = if newly_trashed == 1 {
"A message could not be decrypted".to_string()
} else {
format!("{newly_trashed} messages could not be decrypted")
};
this.update(cx, |_, cx| {
cx.emit(ChatEvent::Error(reason));
cx.emit(ChatEvent::Ping);
})?;
}
}
@@ -556,7 +550,7 @@ impl ChatRegistry {
}
/// Retry unwrapping trashed gift wraps with the newly available encryption key.
fn reprocess_trash(&mut self, cx: &mut Context<Self>) {
pub fn reprocess_trash(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer();
@@ -632,6 +626,32 @@ impl ChatRegistry {
.count()
}
/// How many messages are waiting in the trash.
pub fn trashed_count(&self, cx: &App) -> usize {
self.trash.read(cx).len()
}
/// The messages that could not be decrypted, newest first.
pub fn trashed(&self, cx: &App) -> Vec<TrashedMessage> {
let mut messages: Vec<TrashedMessage> = self
.trash
.read(cx)
.iter()
.filter_map(|message| {
let event = Event::from_json(message.raw_event.as_str()).ok()?;
Some(TrashedMessage {
id: event.id,
created_at: event.created_at,
reason: message.reason.clone(),
})
})
.collect();
messages.sort_by_key(|message| std::cmp::Reverse(message.created_at));
messages
}
/// Get the relays that have seen a given rumor id.
pub fn rumor_seen_on(&self, id: &EventId) -> Option<HashSet<RelayUrl>> {
self.event_map
@@ -854,8 +874,9 @@ impl ChatRegistry {
.custom_tags(SingleLetterTag::LOWERCASE_K, ["7", "14", "15"]);
let events = client.database().query(filter).await?;
let mut grouped: HashMap<u64, Vec<UnsignedEvent>> = HashMap::new();
let mut grouped: HashMap<u64, Vec<UnsignedEvent>> = HashMap::new();
let mut stale: Vec<(EventId, UnsignedEvent)> = Vec::new();
// Keep the single-threaded web event loop responsive during large rescans
let mut last_yield = Instant::now();
@@ -869,15 +890,41 @@ impl ChatRegistry {
&& rumor.tags.public_keys().next().is_some()
{
search.write().unwrap().insert(&rumor);
if rumor.pubkey != public_key
&& !rumor.tags.public_keys().any(|k| k == public_key)
{
continue;
}
let room_id = rumor.uniq_id().to_string();
let stored_room = raw
.tags
.iter()
.find(|tag| tag.kind() == "r")
.and_then(|tag| tag.content());
if stored_room.is_none_or(|stored| stored != room_id)
&& let Some(gift_wrap) = raw
.tags
.iter()
.find(|tag| tag.kind() == "d")
.and_then(|tag| tag.content())
.and_then(|value| EventId::from_hex(value).ok())
{
stale.push((gift_wrap, rumor.clone()));
}
grouped.entry(rumor.uniq_id()).or_default().push(rumor);
}
}
for (gift_wrap, rumor) in stale {
if let Err(error) = set_rumor(&client, gift_wrap, &rumor).await {
log::warn!("Failed to re-tag a cached message: {error:#}");
}
}
let mut rooms = HashSet::with_capacity(grouped.len());
for (_id, messages) in grouped.into_iter() {
+11
View File
@@ -219,6 +219,17 @@ impl FailedMessage {
}
}
/// A message that could not be decrypted, as surfaced to the UI.
#[derive(Debug, Clone)]
pub struct TrashedMessage {
/// The undecryptable gift wrap's id, stable across renders
pub id: EventId,
/// When the gift wrap was received
pub created_at: Timestamp,
/// Why it could not be decrypted
pub reason: SharedString,
}
#[derive(Debug, Clone)]
pub struct Mention {
pub public_key: PublicKey,