feat: add support for NIP-29 #56
@@ -485,6 +485,18 @@ impl Group {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn forget(&mut self, id: EventId, cx: &mut Context<Self>) {
|
||||
self.window.remove(id);
|
||||
|
||||
if !self.index.remove(&id) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.messages.retain(|message| message.id != id);
|
||||
cx.emit(GroupEvent::Updated);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn eose(&mut self, cx: &mut Context<Self>) {
|
||||
self.loading = false;
|
||||
cx.emit(GroupEvent::Eose);
|
||||
|
||||
+40
-2
@@ -506,8 +506,12 @@ impl GroupsRegistry {
|
||||
key.clone(),
|
||||
cx.subscribe(&group, |this, group, event, cx| match event {
|
||||
GroupEvent::Sent(event) => {
|
||||
let key = group.read(cx).key().clone();
|
||||
this.reload(key, Some(event.clone()), cx);
|
||||
if event.kind == Kind::GroupDeleteEvent {
|
||||
this.forget_event(group, event.clone(), cx);
|
||||
} else {
|
||||
let key = group.read(cx).key().clone();
|
||||
this.reload(key, Some(event.clone()), cx);
|
||||
}
|
||||
}
|
||||
GroupEvent::Updated => {
|
||||
this.refresh_entry(group, cx);
|
||||
@@ -676,6 +680,40 @@ impl GroupsRegistry {
|
||||
}));
|
||||
}
|
||||
|
||||
fn forget_event(&mut self, group: Entity<Group>, event: Event, cx: &mut Context<Self>) {
|
||||
let Some(target) = event.tags.event_ids().next() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let key = group.read(cx).key().clone();
|
||||
let cache_tag = key.cache_tag();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let purge = cx.background_spawn(async move {
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::ApplicationSpecificData)
|
||||
.identifier(target.to_hex())
|
||||
.custom_tag(SingleLetterTag::LOWERCASE_R, cache_tag);
|
||||
|
||||
client.database().delete(filter).await
|
||||
});
|
||||
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
if let Err(error) = purge.await {
|
||||
log::warn!("nip29: dropping the deleted envelope failed: {error}");
|
||||
}
|
||||
this.update(cx, |this, cx| {
|
||||
group.update(cx, |group, cx| {
|
||||
group.forget(target, cx);
|
||||
});
|
||||
this.reload(key, Some(event), cx);
|
||||
})
|
||||
.ok();
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, id: SubscriptionId, event: Event, cx: &mut Context<Self>) {
|
||||
if id == list_subscription_id() {
|
||||
match GroupList::parse(&event) {
|
||||
|
||||
@@ -819,6 +819,10 @@ impl TimelineWindow {
|
||||
self.0.truncate(TIMELINE_WINDOW);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: EventId) {
|
||||
self.0.retain(|event| event.id != id);
|
||||
}
|
||||
|
||||
pub fn previous_tag(&self, me: PublicKey) -> Option<Tag> {
|
||||
let references: Vec<String> = self
|
||||
.0
|
||||
|
||||
+5
-8
@@ -353,10 +353,9 @@ reject anything it does not want.
|
||||
must carry every child tag, because a `9002` that drops one is rejected by the
|
||||
relay; the builder re-emits them from the struct.
|
||||
|
||||
`delete_event` is a **relay** instruction only. The relay stops serving the event,
|
||||
but the local timeline keeps it: the backend does not evict the envelope or the
|
||||
message, so a deletion the relay accepted is still visible locally until the UI
|
||||
phase drops it. That is the one moderation write left incomplete (see below).
|
||||
`delete_event` sends `9005`, and once the relay accepts it the registry drops the
|
||||
target's cached envelope, evicts the row and re-reads the group, so the timeline stops
|
||||
showing it. Remote deletions are not folded in: the standing REQ does not carry `9005`.
|
||||
|
||||
## Pins
|
||||
|
||||
@@ -551,10 +550,8 @@ before they reach the consumer, so a replay cannot double-hydrate.
|
||||
`chat::Message` rendering lifted (or copied) out of `ChatPanel`: the data type
|
||||
is already shared, only the row rendering is not. `crates/workspace/src/panels/browse.rs`
|
||||
is the stub for browsing.
|
||||
- **`9005` delete does not touch local state.** The relay stops serving the
|
||||
event, but the envelope and the row stay until a reload. Dropping it needs a
|
||||
filter on the target's `d` tag plus the group's `r` cache tag, and an eviction
|
||||
from `messages` and the id index; nothing does that today.
|
||||
- **Remote deletions are not folded in.** A `9005` from another admin never reaches the
|
||||
standing REQ, so only our own accepted deletes evict the row.
|
||||
- **The LiveKit response shape is a guess.** The NIP names no fields, so
|
||||
`LivekitSession::parse` reads `token` (or `jwt`) and `url` (or `server_url`) and
|
||||
fails loudly on anything else. Re-check it against the relay actually targeted
|
||||
|
||||
Reference in New Issue
Block a user