fix(dm): can't open conversations + "helpers unavailable" on a working signer

After switching to a responsive signer (nos2x), two DM-specific bugs surfaced.

1. Clicking a conversation in the list threw
   "this.controller.handleDmConversationSelect is not a function"
   (ProfileDirectMessageRenderer onSelectConversation). The method exists on
   ProfileDirectMessageActions and the controller delegates ~15 sibling Actions
   methods — but the handleDmConversationSelect delegation was missing. Add it.

2. Loading a conversation's messages threw "DM decryption helpers are
   unavailable" even though the signer decrypts blocks/subscriptions/hashtags
   fine. buildDmDecryptContext gated decryptor registration on (a) the dynamic
   `capabilities` getter (which probes window.nostr and false-negatives on lazy
   injection / nos2x's module shape) and (b) the permission-probe result. The
   list services already learned to register by METHOD EXISTENCE and validate at
   call time; align the DM path to do the same so a present, working signer
   never yields zero decryptors. The permission probe still runs.

Tests: tests/dm-decrypt-context.test.mjs (cheat-resistant — a signer whose
capabilities getter reports false and whose permission probe returns ok:false
must still register nip04+nip44 decryptors). Full unit + dm:unit + dm:integration
suites green. KNOWN_BUGS #0.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-16 14:52:11 -04:00
1 parent 8f39b2c479
commit 7951fd0f09
3 files changed
+78 -10

No files matched your search

+12 -10
View File
@@ -1264,12 +1264,17 @@ export class NostrClient {
}
}
if (activeSigner && extensionPermissionResult?.ok !== false) {
const capabilities = resolveSignerCapabilities(activeSigner);
if (
capabilities.nip44 &&
typeof activeSigner.nip44Decrypt === "function"
) {
// Register the active signer's decryptors by METHOD EXISTENCE, not by the
// dynamic capabilities getter or the permission-request result. The getter
// probes window.nostr for injected nip04/nip44 modules and gives false
// negatives (lazy injection, or providers like nos2x that expose decrypt
// methods which validate at call time) — the list services already learned
// this. Hard-gating here returned ZERO decryptors and made the DM load throw
// "DM decryption helpers are unavailable" even though the signer decrypts
// everything else fine (KNOWN_BUGS #0). The permission request above still
// runs (it can grant), it just no longer blocks decryptor registration.
if (activeSigner) {
if (typeof activeSigner.nip44Decrypt === "function") {
addCandidate(
"nip44",
(pubkey, ciphertext, options) =>
@@ -1289,10 +1294,7 @@ export class NostrClient {
},
);
}
if (
capabilities.nip04 &&
typeof activeSigner.nip04Decrypt === "function"
) {
if (typeof activeSigner.nip04Decrypt === "function") {
addCandidate(
"nip04",
(pubkey, ciphertext, options) =>
@@ -599,6 +599,10 @@ export class ProfileDirectMessageController {
return this.actions.pauseProfileMessages(...args);
}
handleDmConversationSelect(...args) {
return this.actions.handleDmConversationSelect(...args);
}
handleSendDmRequest(...args) {
return this.actions.handleSendDmRequest(...args);
}