mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
fix(dm): route Actions calls to the correct controller (open conversation, etc.)
The controller→Actions split left a batch of method calls in ProfileDirectMessageActions pointing at `this` (the Actions instance) instead of the object that actually defines them. Opening a conversation threw "this.setFocusedDmConversation is not a function"; the same latent bug also broke DM send-error messaging, read receipts, settings publish, and the whole attachment flow (generate/render/reset/upload) — the user just hadn't exercised them yet. Route each to its real receiver: - DM-controller methods → this.controller.*: handleDmSettingsPublish, describeDirectMessageSendError, maybePublishReadReceipt, setFocusedDmConversation - main-controller methods → this.mainController.*: generateAttachmentId, renderAttachmentQueue, resetAttachmentQueue, uploadAttachmentQueue, clearProfileMessages (Builds on the earlier handleDmConversationSelect delegation fix.) A static sweep confirms every remaining `this.*` call in Actions resolves to an Actions method or an explicit controller/mainController/helper receiver. Test: tests/dm-conversation-select.test.mjs (cheat-resistant — drives the real handleDmConversationSelect with stub receivers; would throw on the old wrong-receiver code). DM unit + integration suites green; full build OK. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
7951fd0f09
commit
bf30233649
2 files changed
+81
-11
No files matched your search
@@ -88,7 +88,7 @@ export class ProfileDirectMessageActions {
|
||||
}
|
||||
},
|
||||
onPublishRelays: async (urls) => {
|
||||
return this.handleDmSettingsPublish(urls);
|
||||
return this.controller.handleDmSettingsPublish(urls);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -170,7 +170,7 @@ export class ProfileDirectMessageActions {
|
||||
const previewUrl =
|
||||
typeof URL !== "undefined" ? URL.createObjectURL(file) : "";
|
||||
this.controller.dmAttachmentQueue.push({
|
||||
id: this.generateAttachmentId(file),
|
||||
id: this.mainController.generateAttachmentId(file),
|
||||
file,
|
||||
name: file.name,
|
||||
type: file.type || "application/octet-stream",
|
||||
@@ -182,7 +182,7 @@ export class ProfileDirectMessageActions {
|
||||
});
|
||||
|
||||
input.value = "";
|
||||
this.renderAttachmentQueue();
|
||||
this.mainController.renderAttachmentQueue();
|
||||
}
|
||||
|
||||
async handleSendProfileMessage() {
|
||||
@@ -250,7 +250,7 @@ export class ProfileDirectMessageActions {
|
||||
let attachmentPayloads = [];
|
||||
if (hasAttachments) {
|
||||
try {
|
||||
attachmentPayloads = await this.uploadAttachmentQueue(
|
||||
attachmentPayloads = await this.mainController.uploadAttachmentQueue(
|
||||
this.controller.helper.resolveActiveDmActor(),
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -279,7 +279,7 @@ export class ProfileDirectMessageActions {
|
||||
|
||||
if (result?.ok) {
|
||||
input.value = "";
|
||||
this.resetAttachmentQueue({ clearInput: true });
|
||||
this.mainController.resetAttachmentQueue({ clearInput: true });
|
||||
this.mainController.showSuccess("Message sent.");
|
||||
if (this.controller.enableNip17RelayWarning && result?.warning === "dm-relays-fallback") {
|
||||
this.mainController.showStatus(
|
||||
@@ -294,7 +294,7 @@ export class ProfileDirectMessageActions {
|
||||
const errorCode =
|
||||
typeof result?.error === "string" ? result.error : "unknown";
|
||||
userLogger.warn("[profileModal] Failed to send direct message:", errorCode);
|
||||
this.mainController.showError(this.describeDirectMessageSendError(errorCode));
|
||||
this.mainController.showError(this.controller.describeDirectMessageSendError(errorCode));
|
||||
} catch (error) {
|
||||
userLogger.error("[profileModal] Unexpected DM send failure:", error);
|
||||
this.mainController.showError("Unable to send message. Please try again.");
|
||||
@@ -322,7 +322,7 @@ export class ProfileDirectMessageActions {
|
||||
this.setDirectMessageRecipient(remote, { reason: "thread-select" });
|
||||
}
|
||||
|
||||
this.setFocusedDmConversation(conversationId);
|
||||
this.controller.setFocusedDmConversation(conversationId);
|
||||
|
||||
await this.controller.renderer.renderDmAppShell(this.controller.directMessagesCache, {
|
||||
actorPubkey: actor,
|
||||
@@ -364,7 +364,7 @@ export class ProfileDirectMessageActions {
|
||||
const recipient = this.controller.helper.resolveRemoteForConversationId(conversationId, actor);
|
||||
const messages = this.controller.helper.getDirectMessagesForConversation(conversationId, actor);
|
||||
if (recipient && messages.length) {
|
||||
void this.maybePublishReadReceipt(messages, {
|
||||
void this.controller.maybePublishReadReceipt(messages, {
|
||||
recipientPubkey: recipient,
|
||||
});
|
||||
}
|
||||
@@ -425,7 +425,7 @@ export class ProfileDirectMessageActions {
|
||||
const recipient = this.controller.helper.resolveRemoteForConversationId(conversationId, actor);
|
||||
const messages = this.controller.helper.getDirectMessagesForConversation(conversationId, actor);
|
||||
if (recipient && messages.length) {
|
||||
void this.maybePublishReadReceipt(messages, {
|
||||
void this.controller.maybePublishReadReceipt(messages, {
|
||||
recipientPubkey: recipient,
|
||||
});
|
||||
}
|
||||
@@ -561,7 +561,7 @@ export class ProfileDirectMessageActions {
|
||||
const errorCode =
|
||||
typeof result?.error === "string" ? result.error : "unknown";
|
||||
userLogger.warn("[profileModal] Failed to send direct message:", errorCode);
|
||||
this.mainController.showError(this.describeDirectMessageSendError(errorCode));
|
||||
this.mainController.showError(this.controller.describeDirectMessageSendError(errorCode));
|
||||
this.controller.dmComposerState = "error";
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -582,7 +582,7 @@ export class ProfileDirectMessageActions {
|
||||
|
||||
const actor = this.controller.helper.resolveActiveDmActor();
|
||||
if (!actor) {
|
||||
this.clearProfileMessages();
|
||||
this.mainController.clearProfileMessages();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Scenario (SCN-dm-open-conversation):
|
||||
// Given the DM contact list is rendered,
|
||||
// When the user clicks a conversation (onSelectConversation → controller
|
||||
// delegate → ProfileDirectMessageActions.handleDmConversationSelect),
|
||||
// Then it focuses that conversation and re-renders the thread WITHOUT throwing
|
||||
// — i.e. it must call controller/main-controller methods on the right
|
||||
// receiver, not on the Actions instance (the bug that made opening any
|
||||
// conversation throw "… is not a function" — KNOWN_BUGS #0 follow-up).
|
||||
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { ProfileDirectMessageActions } = await import(
|
||||
"../js/ui/profileModal/ProfileDirectMessageActions.js"
|
||||
);
|
||||
|
||||
test("handleDmConversationSelect focuses + renders via the correct receivers", async () => {
|
||||
const calls = [];
|
||||
const renderer = {
|
||||
renderDmAppShell: async (...args) => {
|
||||
calls.push(["renderDmAppShell", args.length]);
|
||||
},
|
||||
};
|
||||
const controller = {
|
||||
dmMobileView: "list",
|
||||
activeDmConversationId: "",
|
||||
directMessagesCache: { conversations: [] },
|
||||
helper: {
|
||||
resolveActiveDmActor: () => "actor-pubkey",
|
||||
resolveRemoteForConversationId: () => "remote-pubkey",
|
||||
},
|
||||
renderer,
|
||||
setDirectMessageRecipient: (...args) => calls.push(["setDirectMessageRecipient", args[0]]),
|
||||
setFocusedDmConversation: (id) => calls.push(["setFocusedDmConversation", id]),
|
||||
};
|
||||
const mainController = {};
|
||||
|
||||
const actions = new ProfileDirectMessageActions(mainController, controller);
|
||||
// Actions.setDirectMessageRecipient delegates to controller; keep the real one.
|
||||
|
||||
await actions.handleDmConversationSelect({ id: "conv-123" });
|
||||
|
||||
assert.equal(controller.dmMobileView, "thread", "switches to thread view");
|
||||
assert.equal(controller.activeDmConversationId, "conv-123", "sets active conversation");
|
||||
assert.ok(
|
||||
calls.some(([m, v]) => m === "setFocusedDmConversation" && v === "conv-123"),
|
||||
"focuses the selected conversation on the controller",
|
||||
);
|
||||
assert.ok(
|
||||
calls.some(([m]) => m === "renderDmAppShell"),
|
||||
"re-renders the DM thread",
|
||||
);
|
||||
});
|
||||
|
||||
test("handleDmConversationSelect ignores an empty conversation id", async () => {
|
||||
const controller = {
|
||||
dmMobileView: "list",
|
||||
helper: {
|
||||
resolveActiveDmActor: () => "actor",
|
||||
resolveRemoteForConversationId: () => "remote",
|
||||
},
|
||||
renderer: { renderDmAppShell: async () => {} },
|
||||
setFocusedDmConversation: () => {
|
||||
throw new Error("should not focus on empty id");
|
||||
},
|
||||
};
|
||||
const actions = new ProfileDirectMessageActions({}, controller);
|
||||
await actions.handleDmConversationSelect({ id: " " });
|
||||
assert.equal(controller.dmMobileView, "list", "no-op for blank conversation id");
|
||||
});
|
||||
Reference in new issue
Block a user