From 3bc79f8e48738d936ba30ad3f915faf43e60cf93 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:19:15 -0400 Subject: [PATCH] [edit] Grids refresh immediately after editing a video (#52) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Editing left grids showing the pre-edit version until a manual reload. The edit flow already re-ran loadVideos(), but the cache still held the OLD version: unlike new publishes (#46's optimistic insert), the edit never ingested its signed replaceable event locally, so the cache-first render stayed stale until relays echoed the update back. handleEditVideoSubmit now ingests the edited signed event via ingestLocalVideoEvent — newest-wins replace in the active store + videos:updated — so the grid shows the new title/thumbnail/description immediately, with the relay echo confirming behind it. Test: nostr-service suite +1 (edited event ingested, videos:updated + videos:edited emitted, result passed through). Lint green. Co-Authored-By: Claude Opus 4.8 --- js/services/nostrService.js | 12 +++++++++++ tests/services/nostr-service.test.mjs | 31 +++++++++++++++++++++++++++ todo/TODO_2026-06-20_pre-launch.md | 12 ++++++----- 3 files changed, 50 insertions(+), 5 deletions(-) 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`