diff --git a/js/ui/profileModal/ProfileDirectMessageController.js b/js/ui/profileModal/ProfileDirectMessageController.js index 41180786..cf0290da 100644 --- a/js/ui/profileModal/ProfileDirectMessageController.js +++ b/js/ui/profileModal/ProfileDirectMessageController.js @@ -623,6 +623,33 @@ export class ProfileDirectMessageController { return this.actions.handleSendProfileMessage(...args); } + // DM app-shell callbacks. The renderer wires these on `this.controller`, but the + // delegations were missing — so Send / mark-read / settings / toggles all threw + // "is not a function" and the app-shell DM UI couldn't send a message. + handleDmAppShellSendMessage(...args) { + return this.actions.handleDmAppShellSendMessage(...args); + } + + handleDmConversationMarkRead(...args) { + return this.actions.handleDmConversationMarkRead(...args); + } + + handleDmMarkAllConversationsRead(...args) { + return this.actions.handleDmMarkAllConversationsRead(...args); + } + + handleReadReceiptsToggle(...args) { + return this.actions.handleReadReceiptsToggle(...args); + } + + handleTypingIndicatorsToggle(...args) { + return this.actions.handleTypingIndicatorsToggle(...args); + } + + openDmSettingsModal(...args) { + return this.actions.openDmSettingsModal(...args); + } + handleAttachmentSelection(...args) { return this.actions.handleAttachmentSelection(...args); } diff --git a/tests/dm-controller-delegations.test.mjs b/tests/dm-controller-delegations.test.mjs new file mode 100644 index 00000000..36024463 --- /dev/null +++ b/tests/dm-controller-delegations.test.mjs @@ -0,0 +1,51 @@ +// Regression (#19): the DM app-shell renderer wires its callbacks on +// `this.controller` (the ProfileDirectMessageController), but the controller was +// missing the delegation methods that forward to `this.actions` — so clicking Send +// threw "this.controller.handleDmAppShellSendMessage is not a function" and no DM +// could be sent (same class for mark-read / settings / toggles). + +import test from "node:test"; +import assert from "node:assert/strict"; + +const { ProfileDirectMessageController } = await import( + "../js/ui/profileModal/ProfileDirectMessageController.js" +); + +// These are the app-shell callbacks the renderer invokes on the controller; each +// must exist and forward to the matching method on `this.actions`. +const DELEGATED = [ + "handleDmAppShellSendMessage", + "handleDmConversationMarkRead", + "handleDmMarkAllConversationsRead", + "handleReadReceiptsToggle", + "handleTypingIndicatorsToggle", + "openDmSettingsModal", +]; + +test("controller delegates every DM app-shell callback to this.actions", () => { + const proto = ProfileDirectMessageController.prototype; + for (const name of DELEGATED) { + assert.equal( + typeof proto[name], + "function", + `controller.${name} delegation must exist (renderer calls it)`, + ); + + const received = []; + const fakeController = { + actions: { + [name]: (...args) => { + received.push(args); + return `ok:${name}`; + }, + }, + }; + const result = proto[name].call(fakeController, "arg-a", "arg-b"); + assert.equal(result, `ok:${name}`, `${name} must return the actions result`); + assert.deepEqual( + received[0], + ["arg-a", "arg-b"], + `${name} must forward its arguments to this.actions.${name}`, + ); + } +}); diff --git a/todo/TODO_2026-06-20_pre-launch.md b/todo/TODO_2026-06-20_pre-launch.md index 751d910e..2a9df65e 100644 --- a/todo/TODO_2026-06-20_pre-launch.md +++ b/todo/TODO_2026-06-20_pre-launch.md @@ -238,15 +238,17 @@ un-hides when WebTorrent flips green). The real gaps: popover fallback, so the standalone copy-magnet button was also dead. Now dispatches `"video:copy-magnet"`. Covered by the same regression test. -### 19. Direct messages: can read but cannot SEND — deep audit (BUG) -- [ ] DMs are pulled in and readable, but sending appears to do nothing / fails. - Do a focused audit of the send path (compose → encrypt → sign → publish): - NIP-17/gift-wrap vs NIP-04 path selection, the signer's encrypt budget / - circuit-breaker, which write relays the DM publishes to (and whether any - accept), and the optimistic-vs-confirmed UI state. Reuse the - `tests/dm-*` suites and add a send→publish→assert-accepted regression. (Note: - the NIP-07 signer reliability invariants in AGENTS.md §17 apply — a dead - extension worker can make sends hang silently.) +### 19. Direct messages: can read but cannot SEND — FIXED 2026-06-23 +- [x] **Root cause: missing controller delegations.** Clicking Send threw + `this.controller.handleDmAppShellSendMessage is not a function`. The DM + app-shell renderer wires its callbacks on `this.controller` (the + `ProfileDirectMessageController`), but the controller was missing the + delegation methods that forward to `this.actions` — so Send AND mark-read, + mark-all-read, read-receipts/typing toggles, and open-settings all threw and + did nothing. Added the six delegations. The send path itself (encrypt → sign → + publish, NIP-04 default / NIP-17) was fine. Regression test + `tests/dm-controller-delegations.test.mjs`. VERIFY live: typing + Send actually + delivers a message. ### 20. Feed / grid loading reliability — some videos missing (BUG) Two related intermittent symptoms; likely the same root cause (a one-shot,