diff --git a/js/services/nostrService.js b/js/services/nostrService.js index 8b3bf168..fe226e38 100644 --- a/js/services/nostrService.js +++ b/js/services/nostrService.js @@ -2134,6 +2134,18 @@ export class NostrService { async handleEditVideoSubmit({ originalEvent, updatedData, pubkey }) { const result = await this.nostrClient.editVideo(originalEvent, updatedData, pubkey); + // #52: ingest the edited (signed) event locally — the same optimistic path + // new publishes use (#46). The edit is a replaceable event; without this the + // grids re-render from cache showing the PRE-edit version until relays echo + // the update back. Ingesting replaces the old version in the active store + // (newest-wins) and emits videos:updated so grids refresh immediately. + if (result && typeof result === "object" && result.id) { + try { + this.ingestLocalVideoEvent(result); + } catch (error) { + this.log("[nostrService] Failed to ingest edited video locally", error); + } + } this.emit("videos:edited", { originalEvent, updatedData, pubkey, result }); return result; } diff --git a/tests/services/nostr-service.test.mjs b/tests/services/nostr-service.test.mjs index 8d36d9eb..f47ce03a 100644 --- a/tests/services/nostr-service.test.mjs +++ b/tests/services/nostr-service.test.mjs @@ -84,6 +84,37 @@ describe("NostrService", () => { // Account switch must not leave the previous account's DMs on screen: the DM // store lives here (nostrService), so a switch clears the in-memory store. With // keepSnapshot it preserves each account's persisted cache for fast switch-back. + // #52: an edit must refresh the grids immediately — the edited signed event + // is ingested locally (same optimistic path as publish) instead of waiting + // for the relay echo of the replaceable event. + describe("handleEditVideoSubmit", () => { + it("ingests the edited event locally and emits videos:edited", async () => { + const signed = { id: "edited-1", kind: 30078, pubkey: "p", created_at: 2000 }; + mockClient.editVideo = mock.fn(async () => signed); + const ingested = []; + mockClient.ingestLocalVideoEvent = mock.fn((evt) => { + ingested.push(evt); + return { id: evt.id, deleted: false, invalid: false }; + }); + const events = []; + const originalEmit = nostrService.emit; + nostrService.emit = (name, detail) => events.push(name); + try { + const result = await nostrService.handleEditVideoSubmit({ + originalEvent: { id: "orig" }, + updatedData: { title: "t" }, + pubkey: "p", + }); + assert.equal(result, signed); + } finally { + nostrService.emit = originalEmit; + } + assert.deepEqual(ingested, [signed], "edited event ingested for instant grid refresh"); + assert.ok(events.includes("videos:updated"), "grids notified"); + assert.ok(events.includes("videos:edited")); + }); + }); + describe("clearDirectMessages", () => { it("empties the in-memory DM store and resets the active actor", () => { nostrService.dmMessages = [{ id: "m1" }, { id: "m2" }]; diff --git a/todo/TODO_2026-06-20_pre-launch.md b/todo/TODO_2026-06-20_pre-launch.md index 06e6bae0..68004b87 100644 --- a/todo/TODO_2026-06-20_pre-launch.md +++ b/todo/TODO_2026-06-20_pre-launch.md @@ -1629,11 +1629,13 @@ passphrase-encrypted). Items 51, 56, 57 are all facets of that. in the storage/wallet panes for users who set it up there rather than via a prompt. ### 52. After editing a video, the video grids don't refresh to show the update (BUG) -- [ ] Editing a video leaves the grids showing the pre-edit version until a manual - reload. Publishing a NEW video already refreshes the feed (**#46**); the **edit** - path needs the same post-publish grid refresh (title/thumbnail/description/url). - Likely wire the edit-submit success into the same `refreshAllVideoGrids` / - `onVideosShouldRefresh` path #46 uses, keyed by `videoRootId`. +- [x] **FIXED 2026-07-02.** The edit flow already re-ran `loadVideos()`, but the cache + still held the PRE-edit version: unlike new publishes (#46), the edit never + ingested its signed replaceable event locally, so grids re-rendered stale until + relays echoed the update. `nostrService.handleEditVideoSubmit` now ingests the + edited event via `ingestLocalVideoEvent` (newest-wins replace in the active + store + `videos:updated`), so grids show the new title/thumbnail/etc. + immediately. Test: `tests/services/nostr-service.test.mjs` (+1). ### 53. Channel-profile link in the video player modal doesn't work (BUG) - [x] **FIXED 2026-07-02.** Event-name mismatch: `VideoModal.handleCreatorNavigation`