mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-02 02:42:28 +00:00
Merge pull request #779 from PR0M3TH3AN/codex/implement-subscription-decryption-improvements
Allow direct signers to manage subscriptions without extension prompts
This commit is contained in:
2 files changed
+200
-6
No files matched your search
+23
-5
@@ -230,7 +230,22 @@ class SubscriptionsManager {
|
||||
const newest = events[0];
|
||||
this.subsEventId = newest.id;
|
||||
|
||||
const permissionResult = await requestDefaultExtensionPermissions();
|
||||
const signer = getActiveSigner();
|
||||
const signerHasNip04 = typeof signer?.nip04Decrypt === "function";
|
||||
const signerHasNip44 = typeof signer?.nip44Decrypt === "function";
|
||||
const hints = extractEncryptionHints(newest);
|
||||
const requiresNip44 = hints.includes("nip44") || hints.includes("nip44_v2");
|
||||
const requiresNip04 =
|
||||
!hints.length || hints.includes("nip04") || !requiresNip44;
|
||||
|
||||
let permissionResult = { ok: true };
|
||||
const signerCoversRequiredSchemes =
|
||||
(!requiresNip04 || signerHasNip04) && (!requiresNip44 || signerHasNip44);
|
||||
|
||||
if (!signerCoversRequiredSchemes) {
|
||||
permissionResult = await requestDefaultExtensionPermissions();
|
||||
}
|
||||
|
||||
if (!permissionResult.ok) {
|
||||
userLogger.warn(
|
||||
"[SubscriptionsManager] Extension permissions denied while loading subscriptions; treating list as empty.",
|
||||
@@ -300,18 +315,21 @@ class SubscriptionsManager {
|
||||
};
|
||||
|
||||
const signer = getActiveSigner();
|
||||
if (signer?.nip04Decrypt) {
|
||||
const signerHasNip04 = typeof signer?.nip04Decrypt === "function";
|
||||
const signerHasNip44 = typeof signer?.nip44Decrypt === "function";
|
||||
|
||||
if (signerHasNip04) {
|
||||
registerDecryptor("nip04", (payload) => signer.nip04Decrypt(userPubkey, payload));
|
||||
}
|
||||
|
||||
if (signer?.nip44Decrypt) {
|
||||
if (signerHasNip44) {
|
||||
registerDecryptor("nip44", (payload) => signer.nip44Decrypt(userPubkey, payload));
|
||||
}
|
||||
|
||||
const nostrApi =
|
||||
typeof window !== "undefined" && window && window.nostr ? window.nostr : null;
|
||||
if (nostrApi) {
|
||||
if (nostrApi.nip04 && typeof nostrApi.nip04.decrypt === "function") {
|
||||
if (!signerHasNip04 && nostrApi.nip04 && typeof nostrApi.nip04.decrypt === "function") {
|
||||
registerDecryptor("nip04", (payload) =>
|
||||
nostrApi.nip04.decrypt(userPubkey, payload)
|
||||
);
|
||||
@@ -320,7 +338,7 @@ class SubscriptionsManager {
|
||||
const nip44 =
|
||||
nostrApi.nip44 && typeof nostrApi.nip44 === "object" ? nostrApi.nip44 : null;
|
||||
if (nip44) {
|
||||
if (typeof nip44.decrypt === "function") {
|
||||
if (!signerHasNip44 && typeof nip44.decrypt === "function") {
|
||||
registerDecryptor("nip44", (payload) => nip44.decrypt(userPubkey, payload));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
||||
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import { nostrClient } from "../js/nostr.js";
|
||||
import { nostrClient, getActiveSigner, setActiveSigner } from "../js/nostr.js";
|
||||
import { subscriptions } from "../js/subscriptions.js";
|
||||
import { setApplication } from "../js/applicationContext.js";
|
||||
import nostrService from "../js/services/nostrService.js";
|
||||
@@ -311,6 +311,93 @@ test("loadSubscriptions handles nip44.v2 decryptors", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
"loadSubscriptions uses active signer decryptors without requesting extension permissions",
|
||||
async () => {
|
||||
const SubscriptionsManager = subscriptions.constructor;
|
||||
const manager = new SubscriptionsManager();
|
||||
|
||||
const originalRelays = Array.isArray(nostrClient.relays)
|
||||
? [...nostrClient.relays]
|
||||
: nostrClient.relays;
|
||||
const originalWriteRelays = Array.isArray(nostrClient.writeRelays)
|
||||
? [...nostrClient.writeRelays]
|
||||
: nostrClient.writeRelays;
|
||||
const originalPool = nostrClient.pool;
|
||||
const originalEnsurePermissions = nostrClient.ensureExtensionPermissions;
|
||||
const originalSigner = getActiveSigner();
|
||||
|
||||
const hadWindow = typeof globalThis.window !== "undefined";
|
||||
if (!hadWindow) {
|
||||
globalThis.window = {};
|
||||
}
|
||||
const originalWindowNostr = globalThis.window.nostr;
|
||||
delete globalThis.window.nostr;
|
||||
|
||||
const relayUrls = ["wss://relay-direct.example"];
|
||||
nostrClient.relays = relayUrls;
|
||||
nostrClient.writeRelays = relayUrls;
|
||||
|
||||
const event = {
|
||||
id: "event-direct",
|
||||
created_at: 600,
|
||||
content: "cipher-direct",
|
||||
};
|
||||
|
||||
nostrClient.pool = {
|
||||
list() {
|
||||
return Promise.resolve([event]);
|
||||
},
|
||||
};
|
||||
|
||||
let permissionCalls = 0;
|
||||
nostrClient.ensureExtensionPermissions = async () => {
|
||||
permissionCalls += 1;
|
||||
return { ok: true };
|
||||
};
|
||||
|
||||
const decryptCalls = [];
|
||||
setActiveSigner({
|
||||
pubkey: "user-pubkey-123",
|
||||
async nip04Decrypt(pubkey, ciphertext) {
|
||||
decryptCalls.push({ pubkey, ciphertext });
|
||||
return JSON.stringify({ subPubkeys: ["pub-direct"] });
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await manager.loadSubscriptions("user-pubkey-123");
|
||||
|
||||
assert.equal(permissionCalls, 0, "should not request extension permissions");
|
||||
assert.equal(decryptCalls.length, 1, "signer decrypt should be invoked once");
|
||||
assert.deepEqual(
|
||||
decryptCalls[0],
|
||||
{ pubkey: "user-pubkey-123", ciphertext: "cipher-direct" },
|
||||
"signer decrypt should receive the expected arguments",
|
||||
);
|
||||
assert.deepEqual(
|
||||
Array.from(manager.subscribedPubkeys),
|
||||
["pub-direct"],
|
||||
"direct signer decrypt should populate subscriptions",
|
||||
);
|
||||
} finally {
|
||||
nostrClient.relays = originalRelays;
|
||||
nostrClient.writeRelays = originalWriteRelays;
|
||||
nostrClient.pool = originalPool;
|
||||
nostrClient.ensureExtensionPermissions = originalEnsurePermissions;
|
||||
setActiveSigner(originalSigner);
|
||||
if (typeof originalWindowNostr === "undefined") {
|
||||
delete globalThis.window.nostr;
|
||||
} else {
|
||||
globalThis.window.nostr = originalWindowNostr;
|
||||
}
|
||||
if (!hadWindow) {
|
||||
delete globalThis.window;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"showSubscriptionVideos waits for nostrService warm-up and refreshes after updates",
|
||||
async () => {
|
||||
@@ -453,6 +540,95 @@ test(
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"publishSubscriptionList succeeds with direct signer without requesting extension permissions",
|
||||
async () => {
|
||||
const SubscriptionsManager = subscriptions.constructor;
|
||||
const manager = new SubscriptionsManager();
|
||||
|
||||
const originalRelays = Array.isArray(nostrClient.relays)
|
||||
? [...nostrClient.relays]
|
||||
: nostrClient.relays;
|
||||
const originalWriteRelays = Array.isArray(nostrClient.writeRelays)
|
||||
? [...nostrClient.writeRelays]
|
||||
: nostrClient.writeRelays;
|
||||
const originalPool = nostrClient.pool;
|
||||
const originalEnsurePermissions = nostrClient.ensureExtensionPermissions;
|
||||
const originalSigner = getActiveSigner();
|
||||
|
||||
const relayUrls = ["wss://relay-direct.example"];
|
||||
nostrClient.relays = relayUrls;
|
||||
nostrClient.writeRelays = relayUrls;
|
||||
|
||||
let permissionCalls = 0;
|
||||
nostrClient.ensureExtensionPermissions = async () => {
|
||||
permissionCalls += 1;
|
||||
return { ok: true };
|
||||
};
|
||||
|
||||
const publishCalls = [];
|
||||
nostrClient.pool = {
|
||||
publish(urls, event) {
|
||||
publishCalls.push({ urls, event });
|
||||
return {
|
||||
on(eventName, handler) {
|
||||
if (eventName === "ok") {
|
||||
handler();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const encryptCalls = [];
|
||||
const signCalls = [];
|
||||
setActiveSigner({
|
||||
type: "nsec",
|
||||
pubkey: "user-pubkey-123",
|
||||
async nip04Encrypt(pubkey, plaintext) {
|
||||
encryptCalls.push({ pubkey, plaintext });
|
||||
return "cipher-direct";
|
||||
},
|
||||
async signEvent(event) {
|
||||
signCalls.push(event);
|
||||
return { ...event, id: "signed-direct-event" };
|
||||
},
|
||||
});
|
||||
|
||||
manager.subscribedPubkeys = new Set(["pub-direct"]);
|
||||
|
||||
try {
|
||||
await manager.publishSubscriptionList("user-pubkey-123");
|
||||
|
||||
assert.equal(permissionCalls, 0, "should not request extension permissions");
|
||||
assert.equal(encryptCalls.length, 1, "nip04Encrypt should be called once");
|
||||
assert.equal(signCalls.length, 1, "signEvent should be called once");
|
||||
assert.deepEqual(
|
||||
encryptCalls[0],
|
||||
{
|
||||
pubkey: "user-pubkey-123",
|
||||
plaintext: JSON.stringify({ subPubkeys: ["pub-direct"] }),
|
||||
},
|
||||
"nip04Encrypt should receive the serialized subscription list",
|
||||
);
|
||||
assert.equal(
|
||||
signCalls[0].content,
|
||||
"cipher-direct",
|
||||
"signEvent should receive the encrypted content",
|
||||
);
|
||||
assert.equal(publishCalls.length, relayUrls.length, "should publish to each relay");
|
||||
assert.equal(manager.subsEventId, "signed-direct-event");
|
||||
} finally {
|
||||
nostrClient.relays = originalRelays;
|
||||
nostrClient.writeRelays = originalWriteRelays;
|
||||
nostrClient.pool = originalPool;
|
||||
nostrClient.ensureExtensionPermissions = originalEnsurePermissions;
|
||||
setActiveSigner(originalSigner);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test("renderSameGridStyle shows empty state message", async () => {
|
||||
const SubscriptionsManager = subscriptions.constructor;
|
||||
|
||||
|
||||
Reference in new issue
Block a user