From 52d2488d17fa782388bf86b707d790f827b31d9a Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sun, 18 Jan 2026 09:15:18 -0500 Subject: [PATCH] Refactor nostr signer access --- js/services/hashtagPreferencesService.js | 70 +++--------------------- js/services/moderationService.js | 23 ++++++-- js/subscriptions.js | 44 ++++----------- js/ui/components/UploadModal.js | 24 ++++++-- js/watchHistoryService.js | 26 ++++----- 5 files changed, 66 insertions(+), 121 deletions(-) diff --git a/js/services/hashtagPreferencesService.js b/js/services/hashtagPreferencesService.js index 4816d877..6984bdd6 100644 --- a/js/services/hashtagPreferencesService.js +++ b/js/services/hashtagPreferencesService.js @@ -2,8 +2,8 @@ import { nostrClient, requestDefaultExtensionPermissions, + getActiveSigner, } from "../nostrClientFacade.js"; -import { getActiveSigner } from "../nostr/index.js"; import { isSessionActor } from "../nostr/sessionActor.js"; import { buildHashtagPreferenceEvent, @@ -622,7 +622,10 @@ class HashtagPreferencesService { return { ok: false, error }; } - const signer = getActiveSigner(); + let signer = getActiveSigner(); + if (!signer && typeof nostrClient?.ensureActiveSignerForPubkey === "function") { + signer = await nostrClient.ensureActiveSignerForPubkey(userPubkey); + } const signerHasNip04 = typeof signer?.nip04Decrypt === "function"; const signerHasNip44 = typeof signer?.nip44Decrypt === "function"; @@ -663,39 +666,6 @@ class HashtagPreferencesService { registerDecryptor("nip04", (payload) => signer.nip04Decrypt(userPubkey, payload)); } - const nostrApi = - typeof window !== "undefined" && window && window.nostr ? window.nostr : null; - if (nostrApi) { - const nip04 = - nostrApi.nip04 && typeof nostrApi.nip04.decrypt === "function" - ? nostrApi.nip04 - : null; - if (nip04 && !decryptors.has("nip04")) { - registerDecryptor("nip04", (payload) => nip04.decrypt(userPubkey, payload)); - } - - const nip44 = - nostrApi.nip44 && typeof nostrApi.nip44 === "object" - ? nostrApi.nip44 - : null; - if (nip44) { - if (typeof nip44.decrypt === "function" && !decryptors.has("nip44")) { - registerDecryptor("nip44", (payload) => nip44.decrypt(userPubkey, payload)); - } - const nip44v2 = nip44.v2 && typeof nip44.v2 === "object" ? nip44.v2 : null; - if (nip44v2 && typeof nip44v2.decrypt === "function") { - registerDecryptor("nip44_v2", (payload) => - nip44v2.decrypt(userPubkey, payload), - ); - if (!decryptors.has("nip44")) { - registerDecryptor("nip44", (payload) => - nip44v2.decrypt(userPubkey, payload), - ); - } - } - } - } - if (!decryptors.size) { const error = new Error( "No decryptors available for hashtag preferences payload.", @@ -759,7 +729,10 @@ class HashtagPreferencesService { signer = await nostrClient.ensureActiveSignerForPubkey(targetPubkey); } - if (!signer || typeof signer.signEvent !== "function") { + const canSign = typeof signer?.canSign === "function" + ? signer.canSign() + : typeof signer?.signEvent === "function"; + if (!canSign || typeof signer?.signEvent !== "function") { const error = new Error("An active signer is required to publish preferences."); error.code = "hashtag-preferences-missing-signer"; throw error; @@ -806,31 +779,6 @@ class HashtagPreferencesService { registerEncryptor("nip04", (value) => signer.nip04Encrypt(targetPubkey, value)); } - const nostrApi = - typeof window !== "undefined" && window && window.nostr ? window.nostr : null; - if (nostrApi) { - const nip44 = - nostrApi.nip44 && typeof nostrApi.nip44 === "object" - ? nostrApi.nip44 - : null; - if (nip44) { - if (typeof nip44.encrypt === "function") { - registerEncryptor("nip44", (value) => nip44.encrypt(targetPubkey, value)); - } - const nip44v2 = nip44.v2 && typeof nip44.v2 === "object" ? nip44.v2 : null; - if (nip44v2 && typeof nip44v2.encrypt === "function") { - registerEncryptor("nip44_v2", (value) => - nip44v2.encrypt(targetPubkey, value), - ); - } - } - if (typeof nostrApi.nip04?.encrypt === "function") { - registerEncryptor("nip04", (value) => - nostrApi.nip04.encrypt(targetPubkey, value), - ); - } - } - if (!encryptors.length) { const error = new Error("No encryptors available to publish preferences."); error.code = "hashtag-preferences-no-encryptor"; diff --git a/js/services/moderationService.js b/js/services/moderationService.js index 59a06c2c..a28d912d 100644 --- a/js/services/moderationService.js +++ b/js/services/moderationService.js @@ -1,4 +1,8 @@ -import { nostrClient } from "../nostrClientFacade.js"; +import { + getActiveSigner, + nostrClient, + requestDefaultExtensionPermissions, +} from "../nostrClientFacade.js"; import { publishEventToRelays, assertAnyRelayAccepted } from "../nostrPublish.js"; import { accessControl } from "../accessControl.js"; import { userBlocks, USER_BLOCK_EVENTS } from "../userBlocks.js"; @@ -1542,15 +1546,22 @@ export class ModerationService { await this.ensurePool(); - const extension = typeof window !== "undefined" ? window.nostr : null; - if (!extension || typeof extension.signEvent !== "function") { + let signer = getActiveSigner(); + if (!signer && typeof this.nostrClient?.ensureActiveSignerForPubkey === "function") { + signer = await this.nostrClient.ensureActiveSignerForPubkey(viewer); + } + + const canSign = typeof signer?.canSign === "function" + ? signer.canSign() + : typeof signer?.signEvent === "function"; + if (!canSign || typeof signer?.signEvent !== "function") { const error = new Error("nostr-extension-missing"); error.code = "nostr-extension-missing"; throw error; } - if (typeof this.nostrClient?.ensureExtensionPermissions === "function") { - const permissionResult = await this.nostrClient.ensureExtensionPermissions([ + if (signer?.type === "extension") { + const permissionResult = await requestDefaultExtensionPermissions([ "sign_event", "get_public_key", ]); @@ -1583,7 +1594,7 @@ export class ModerationService { let signedEvent; try { - signedEvent = await extension.signEvent(event); + signedEvent = await signer.signEvent(event); } catch (error) { const wrapped = new Error("signature-failed"); wrapped.code = "signature-failed"; diff --git a/js/subscriptions.js b/js/subscriptions.js index 9f22cbe8..bd2d7217 100644 --- a/js/subscriptions.js +++ b/js/subscriptions.js @@ -1,12 +1,10 @@ // js/subscriptions.js import { + getActiveSigner, nostrClient, requestDefaultExtensionPermissions, } from "./nostrClientFacade.js"; -import { - getActiveSigner, - convertEventToVideo as sharedConvertEventToVideo, -} from "./nostr/index.js"; +import { convertEventToVideo as sharedConvertEventToVideo } from "./nostr/index.js"; import { normalizeNostrPubkey } from "./nostr/nip46Client.js"; import { listVideoViewEvents, @@ -557,7 +555,10 @@ class SubscriptionsManager { decryptors.set(scheme, handler); }; - const signer = getActiveSigner(); + let signer = getActiveSigner(); + if (!signer && typeof nostrClient?.ensureActiveSignerForPubkey === "function") { + signer = await nostrClient.ensureActiveSignerForPubkey(userPubkey); + } const signerHasNip04 = typeof signer?.nip04Decrypt === "function"; const signerHasNip44 = typeof signer?.nip44Decrypt === "function"; @@ -569,34 +570,6 @@ class SubscriptionsManager { registerDecryptor("nip44", (payload) => signer.nip44Decrypt(userPubkey, payload)); } - const nostrApi = - typeof window !== "undefined" && window && window.nostr ? window.nostr : null; - if (nostrApi) { - if (!signerHasNip04 && nostrApi.nip04 && typeof nostrApi.nip04.decrypt === "function") { - registerDecryptor("nip04", (payload) => - nostrApi.nip04.decrypt(userPubkey, payload) - ); - } - - const nip44 = - nostrApi.nip44 && typeof nostrApi.nip44 === "object" ? nostrApi.nip44 : null; - if (nip44) { - if (!signerHasNip44 && typeof nip44.decrypt === "function") { - registerDecryptor("nip44", (payload) => nip44.decrypt(userPubkey, payload)); - } - - const nip44v2 = nip44.v2 && typeof nip44.v2 === "object" ? nip44.v2 : null; - if (nip44v2 && typeof nip44v2.decrypt === "function") { - registerDecryptor("nip44_v2", (payload) => - nip44v2.decrypt(userPubkey, payload) - ); - if (!decryptors.has("nip44")) { - registerDecryptor("nip44", (payload) => nip44v2.decrypt(userPubkey, payload)); - } - } - } - } - if (!decryptors.size) { const error = new Error( "No active signer or extension decryptors are available for subscriptions." @@ -700,7 +673,10 @@ class SubscriptionsManager { signer = await nostrClient.ensureActiveSignerForPubkey(userPubkey); } - if (!signer) { + const canSign = typeof signer?.canSign === "function" + ? signer.canSign() + : typeof signer?.signEvent === "function"; + if (!canSign) { const error = new Error( "An active signer is required to update subscriptions." ); diff --git a/js/ui/components/UploadModal.js b/js/ui/components/UploadModal.js index 98a525bd..47db1646 100644 --- a/js/ui/components/UploadModal.js +++ b/js/ui/components/UploadModal.js @@ -11,6 +11,10 @@ import { createTorrentMetadata } from "../../utils/torrentHash.js"; import { sanitizeBucketName } from "../../storage/r2-mgmt.js"; import { buildR2Key, buildPublicUrl } from "../../r2.js"; import { PROVIDERS } from "../../services/storageService.js"; +import { + getActiveSigner, + requestDefaultExtensionPermissions, +} from "../../nostrClientFacade.js"; const INFO_HASH_PATTERN = /^[a-f0-9]{40}$/; @@ -557,7 +561,7 @@ export class UploadModal { this.toggleStorageView("form"); } } catch (err) { - console.error("Failed to load connection", err); + userLogger.error("Failed to load connection", err); this.toggleStorageView("form"); } } @@ -577,18 +581,28 @@ export class UploadModal { const pubkey = this.getCurrentPubkey ? this.getCurrentPubkey() : null; if (!pubkey) return; - // We need a signer. Try window.nostr or authService - let signer = window.nostr; + // We need a signer. Try active signer or authService + let signer = getActiveSigner(); if (!signer && this.authService?.signer) { signer = this.authService.signer; } - if (!signer) { + const canSign = typeof signer?.canSign === "function" + ? signer.canSign() + : typeof signer?.signEvent === "function"; + if (!canSign) { alert("No signer available to unlock storage."); return; } try { + if (signer?.type === "extension") { + const permissionResult = await requestDefaultExtensionPermissions(); + if (!permissionResult?.ok) { + alert("Extension permissions are required to unlock storage."); + return; + } + } if (this.toggles.storageUnlock) { this.toggles.storageUnlock.textContent = "Unlocking..."; this.toggles.storageUnlock.disabled = true; @@ -598,7 +612,7 @@ export class UploadModal { this.updateLockUi(); await this.loadFromStorage(); } catch (err) { - console.error("Unlock failed", err); + userLogger.error("Unlock failed", err); alert("Failed to unlock storage: " + err.message); } finally { if (this.toggles.storageUnlock) { diff --git a/js/watchHistoryService.js b/js/watchHistoryService.js index 90f90e47..dcc99d68 100644 --- a/js/watchHistoryService.js +++ b/js/watchHistoryService.js @@ -1,6 +1,7 @@ // js/watchHistoryService.js import { + getActiveSigner, nostrClient, requestDefaultExtensionPermissions, } from "./nostrClientFacade.js"; @@ -73,31 +74,26 @@ function getSessionActorKey() { return normalizeActorKey(nostrClient?.sessionActor?.pubkey); } -function shouldUseExtensionForHistory(actorKey) { +async function ensureWatchHistoryExtensionPermissions(actorKey) { const normalizedActor = normalizeActorKey(actorKey); if (!normalizedActor) { - return false; + return { ok: true }; } const loggedActor = normalizeActorKey(nostrClient?.pubkey); if (!loggedActor || loggedActor !== normalizedActor) { - return false; + return { ok: true }; } - if (typeof window === "undefined") { - return false; + let signer = getActiveSigner(); + if (!signer && typeof nostrClient?.ensureActiveSignerForPubkey === "function") { + signer = await nostrClient.ensureActiveSignerForPubkey(normalizedActor); } - const extension = window.nostr; - if (!extension) { - return false; - } - - return typeof extension.signEvent === "function"; -} - -async function ensureWatchHistoryExtensionPermissions(actorKey) { - if (!shouldUseExtensionForHistory(actorKey)) { + const canSign = typeof signer?.canSign === "function" + ? signer.canSign() + : typeof signer?.signEvent === "function"; + if (!canSign || signer?.type !== "extension") { return { ok: true }; }