diff --git a/components/profile-modal.html b/components/profile-modal.html index 6a6103cb..997ef0c6 100644 --- a/components/profile-modal.html +++ b/components/profile-modal.html @@ -1067,6 +1067,33 @@ aria-live="polite" aria-label="Direct message threads" > +
+ + +
+ + +
+
{ + if (!(element instanceof HTMLElement) || !("disabled" in element)) { + return; + } + + element.disabled = shouldDisable; + if (shouldDisable) { + element.setAttribute("aria-disabled", "true"); + } else { + element.removeAttribute("aria-disabled"); + } + }; + + applyDisabledState(input); + applyDisabledState(button); + + if (helper instanceof HTMLElement) { + if (shouldDisable) { + helper.textContent = "Sign in to send messages."; + helper.classList.remove("hidden"); + helper.removeAttribute("hidden"); + } else { + helper.classList.add("hidden"); + helper.setAttribute("hidden", ""); + } + } + } + setMessagesAnnouncement(message) { if (!(this.profileMessagesStatus instanceof HTMLElement)) { return; @@ -2000,6 +2044,93 @@ export class ProfileModalController { } } + describeDirectMessageSendError(code) { + switch (code) { + case "sign-event-unavailable": + return "Connect a Nostr signer to send messages."; + case "encryption-unsupported": + return "Your signer does not support NIP-44 or NIP-04 encryption."; + case "extension-permission-denied": + return "Please grant your Nostr extension permission to send messages."; + case "missing-actor-pubkey": + return "We couldn’t determine your public key to send this message."; + case "nostr-uninitialized": + return "Direct messages are still connecting to relays. Please try again."; + case "signature-failed": + return "We couldn’t sign the message. Please reconnect your signer and try again."; + case "encryption-failed": + return "We couldn’t encrypt the message. Please try again."; + case "publish-failed": + return "Failed to deliver this message to any relay. Please try again."; + case "invalid-target": + return "Select a valid recipient before sending."; + case "empty-message": + return "Please enter a message."; + default: + return "Unable to send message. Please try again."; + } + } + + async handleSendProfileMessage() { + const input = this.profileMessageInput; + if (!(input instanceof HTMLTextAreaElement)) { + return; + } + + const message = typeof input.value === "string" ? input.value.trim() : ""; + if (!message) { + this.showError("Please enter a message."); + return; + } + + const targetCandidate = + this.resolveActiveDmActor() || this.directMessagesLastActor; + const target = + typeof targetCandidate === "string" ? targetCandidate.trim() : ""; + if (!target) { + this.showError("Please select a message recipient."); + return; + } + + if ( + !this.services.nostrClient || + typeof this.services.nostrClient.sendDirectMessage !== "function" + ) { + this.showError("Direct message service unavailable."); + return; + } + + const sendButton = this.profileMessageSendButton; + if (sendButton instanceof HTMLElement && "disabled" in sendButton) { + sendButton.disabled = true; + sendButton.setAttribute("aria-disabled", "true"); + } + + try { + const result = await this.services.nostrClient.sendDirectMessage( + target, + message, + ); + + if (result?.ok) { + input.value = ""; + this.showSuccess("Message sent."); + void this.populateProfileMessages({ force: true, reason: "send-message" }); + return; + } + + const errorCode = + typeof result?.error === "string" ? result.error : "unknown"; + userLogger.warn("[profileModal] Failed to send direct message:", errorCode); + this.showError(this.describeDirectMessageSendError(errorCode)); + } catch (error) { + userLogger.error("[profileModal] Unexpected DM send failure:", error); + this.showError("Unable to send message. Please try again."); + } finally { + this.updateMessageComposerState(); + } + } + clearProfileMessages({ message } = {}) { this.directMessagesCache = []; this.directMessagesLastActor = this.resolveActiveDmActor(); @@ -2673,6 +2804,21 @@ export class ProfileModalController { }); } + if (this.profileMessageSendButton instanceof HTMLElement) { + this.profileMessageSendButton.addEventListener("click", () => { + void this.handleSendProfileMessage(); + }); + } + + if (this.profileMessageInput instanceof HTMLElement) { + this.profileMessageInput.addEventListener("keydown", (event) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + void this.handleSendProfileMessage(); + } + }); + } + if (this.walletUriInput instanceof HTMLElement) { this.walletUriInput.addEventListener("focus", () => { this.revealSecretInputValue(this.walletUriInput);