mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-03 11:22:25 +00:00
I have updated the nostr-tools usage and resolved the UI bugs. Here are the details of the changes:
- Updated `js/nostr/adapters/nsecAdapter.js` to ensure private keys are `Uint8Array` bytes before passing to `getPublicKey`, fixing compatibility with `nostr-tools` v2. - Updated `tests/nostr/nip46Client.test.js` to use byte arrays for keys in crypto operations. - Implemented missing `setupModalMorePopover`, `setupModalSharePopover`, and `refreshModalMoreMenuPanel` in `VideoModal.js`. - Fixed a bug in `VideoModal.js` where `setCommentComposerState` was overriding `CommentsController` send button logic; it now delegates to the controller if available. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
7427961a69
commit
a649c341e0
3 files changed
+148
-36
No files matched your search
@@ -6,8 +6,6 @@ import { ensureNostrTools, getCachedNostrTools } from "../toolkit.js";
|
||||
import { normalizeActorKey } from "../watchHistory.js";
|
||||
import { HEX64_REGEX } from "../../utils/hex.js";
|
||||
|
||||
|
||||
|
||||
function normalizePrivateKey(privateKey) {
|
||||
if (typeof privateKey !== "string") {
|
||||
return "";
|
||||
@@ -31,10 +29,28 @@ async function resolvePublicKey(privateKey, pubkey) {
|
||||
|
||||
const tools = (await ensureNostrTools()) || getCachedNostrTools();
|
||||
if (tools?.getPublicKey && typeof tools.getPublicKey === "function") {
|
||||
const derived = tools.getPublicKey(privateKey);
|
||||
const normalized = normalizeActorKey(derived);
|
||||
if (normalized && HEX64_REGEX.test(normalized)) {
|
||||
return normalized;
|
||||
try {
|
||||
// nostr-tools v2 requires Uint8Array
|
||||
let keyInput = privateKey;
|
||||
if (typeof privateKey === "string" && tools.utils?.hexToBytes) {
|
||||
keyInput = tools.utils.hexToBytes(privateKey);
|
||||
}
|
||||
const derived = tools.getPublicKey(keyInput);
|
||||
const normalized = normalizeActorKey(derived);
|
||||
if (normalized && HEX64_REGEX.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback for v1 or other errors
|
||||
try {
|
||||
const derived = tools.getPublicKey(privateKey);
|
||||
const normalized = normalizeActorKey(derived);
|
||||
if (normalized && HEX64_REGEX.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
} catch (e2) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3025,20 +3025,23 @@ export class VideoModal {
|
||||
setCommentComposerState(state) {
|
||||
this._commentComposerState = state;
|
||||
if (this.commentsController) {
|
||||
// Also update the internal controller to keep them in sync if possible,
|
||||
// but primarily we trust the caller (which might be a test controller)
|
||||
// Delegate entirely to the controller if it exists to avoid fighting over DOM state
|
||||
// (especially submit button enablement which depends on input value)
|
||||
if (typeof this.commentsController.setCommentComposerState === 'function') {
|
||||
// Avoid infinite recursion if controller calls back
|
||||
if (this.commentsController.composerState !== state) {
|
||||
this.commentsController.setCommentComposerState(state);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Reflect state to DOM immediately if elements exist
|
||||
|
||||
// Fallback: Manually reflect state to DOM if no controller is attached (e.g. in isolated tests)
|
||||
if (this.commentsInput) {
|
||||
this.commentsInput.disabled = !!state?.disabled;
|
||||
}
|
||||
if (this.commentsSubmitButton) {
|
||||
// Note: This naive check doesn't account for empty input,
|
||||
// but without a controller we can't easily validte.
|
||||
this.commentsSubmitButton.disabled = !!state?.disabled;
|
||||
}
|
||||
if (this.commentsComposer) {
|
||||
@@ -3169,14 +3172,107 @@ export class VideoModal {
|
||||
}
|
||||
|
||||
setupModalMorePopover() {
|
||||
// Stub
|
||||
if (!this.playerModal || !this.modalMoreBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.modalMorePopover?.destroy) {
|
||||
this.modalMorePopover.destroy();
|
||||
}
|
||||
|
||||
this.modalMorePopover = createPopover({
|
||||
trigger: this.modalMoreBtn,
|
||||
content: (container) => {
|
||||
if (!this.activeVideo) {
|
||||
container.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
const panel = createVideoMoreMenuPanel({
|
||||
document: this.document,
|
||||
video: this.activeVideo,
|
||||
pointerInfo: this.modalMoreMenuContext.pointerInfo,
|
||||
playbackUrl: this.modalMoreMenuContext.playbackUrl,
|
||||
playbackMagnet: this.modalMoreMenuContext.playbackMagnet,
|
||||
canManageBlacklist: this.modalMoreMenuContext.canManageBlacklist,
|
||||
context: "modal",
|
||||
});
|
||||
if (panel) {
|
||||
container.appendChild(panel);
|
||||
this.modalMoreMenuPanel = panel;
|
||||
}
|
||||
},
|
||||
placement: "bottom-end",
|
||||
offset: 8,
|
||||
onOpen: () => {
|
||||
if (this.activeVideo) {
|
||||
this.syncMoreMenuData({
|
||||
currentVideo: this.activeVideo,
|
||||
canManageBlacklist: this.modalMoreMenuContext.canManageBlacklist,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setupModalSharePopover() {
|
||||
// Stub
|
||||
if (!this.playerModal || !this.shareBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse popover engine for share button
|
||||
// Using a simpler on-click binding for now to match legacy behavior,
|
||||
// or instantiate a popover if we want the full menu experience.
|
||||
// The E2E test expects a popover with [data-menu="video-share"].
|
||||
|
||||
// We'll create a local property for share popover to clean up later
|
||||
if (this.modalSharePopover?.destroy) {
|
||||
this.modalSharePopover.destroy();
|
||||
}
|
||||
|
||||
this.modalSharePopover = createPopover({
|
||||
trigger: this.shareBtn,
|
||||
content: (container) => {
|
||||
if (!this.activeVideo) {
|
||||
container.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
const panel = createVideoShareMenuPanel({
|
||||
document: this.document,
|
||||
video: this.activeVideo,
|
||||
isLoggedIn: this.shareNostrAuthState.isLoggedIn,
|
||||
hasSigner: this.shareNostrAuthState.hasSigner,
|
||||
hasMagnet: Boolean(this.modalMoreMenuContext.playbackMagnet), // Re-use magnet from context
|
||||
hasCdn: Boolean(this.modalMoreMenuContext.playbackUrl),
|
||||
});
|
||||
if (panel) {
|
||||
container.appendChild(panel);
|
||||
}
|
||||
},
|
||||
placement: "top", // or "top-start" based on layout
|
||||
offset: 8,
|
||||
});
|
||||
}
|
||||
|
||||
refreshModalMoreMenuPanel() {
|
||||
// Stub
|
||||
if (this.modalMoreMenuPanel && this.activeVideo) {
|
||||
// Re-render panel content if open
|
||||
const container = this.modalMoreMenuPanel.parentElement;
|
||||
if(container) {
|
||||
container.innerHTML = "";
|
||||
const panel = createVideoMoreMenuPanel({
|
||||
document: this.document,
|
||||
video: this.activeVideo,
|
||||
pointerInfo: this.modalMoreMenuContext.pointerInfo,
|
||||
playbackUrl: this.modalMoreMenuContext.playbackUrl,
|
||||
playbackMagnet: this.modalMoreMenuContext.playbackMagnet,
|
||||
canManageBlacklist: this.modalMoreMenuContext.canManageBlacklist,
|
||||
context: "modal",
|
||||
});
|
||||
if (panel) {
|
||||
container.appendChild(panel);
|
||||
this.modalMoreMenuPanel = panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,8 @@ test("Nip46RpcClient encrypts payloads with nip44 conversation keys", async () =
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const clientPublicKey = getPublicKey(clientSecret);
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
const clientPublicKey = getPublicKey(utils.hexToBytes(clientSecret));
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret));
|
||||
|
||||
const rpcClient = new Nip46RpcClient({
|
||||
nostrClient: { relays: [] },
|
||||
@@ -43,8 +43,8 @@ test("Nip46RpcClient encrypts payloads with nip44 conversation keys", async () =
|
||||
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remotePubkey);
|
||||
? nip44.v2.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey);
|
||||
const decryptedWithTools = nip44.v2.decrypt(ciphertext, conversationKey);
|
||||
assert.equal(
|
||||
decryptedWithTools,
|
||||
@@ -71,13 +71,13 @@ test("decryptNip46PayloadWithKeys handles nip44.v2 ciphertext", async () => {
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret));
|
||||
|
||||
const serialized = JSON.stringify({ message: "hello", count: 42 });
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remotePubkey);
|
||||
? nip44.v2.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey);
|
||||
const ciphertext = nip44.v2.encrypt(serialized, conversationKey);
|
||||
|
||||
const { plaintext, algorithm } = await decryptNip46PayloadWithKeys(
|
||||
@@ -104,13 +104,13 @@ test("decryptNip46PayloadWithKeys coerces structured handshake payloads", async
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret));
|
||||
|
||||
const serialized = JSON.stringify({ message: "hello", count: 42 });
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remotePubkey);
|
||||
? nip44.v2.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey);
|
||||
const ciphertext = nip44.v2.encrypt(serialized, conversationKey);
|
||||
const [ciphertextPart, nonce] = ciphertext.split("\n");
|
||||
|
||||
@@ -154,13 +154,13 @@ test("decryptNip46PayloadWithKeys decodes buffer-based handshake payloads", asyn
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret));
|
||||
|
||||
const serialized = JSON.stringify({ message: "hello", count: 42 });
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remotePubkey);
|
||||
? nip44.v2.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey)
|
||||
: nip44.v2.utils.getConversationKey(utils.hexToBytes(clientSecret), remotePubkey);
|
||||
const ciphertext = nip44.v2.encrypt(serialized, conversationKey);
|
||||
const [ciphertextPart, nonce] = ciphertext.split("\n");
|
||||
const ciphertextBuffer = Buffer.from(ciphertext, "utf8");
|
||||
@@ -197,7 +197,7 @@ test("decryptNip46PayloadWithKeys supports nip04-style ciphertext wrappers", asy
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret);
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret));
|
||||
|
||||
const serialized = JSON.stringify({ message: "hello", count: 42 });
|
||||
const ciphertext = await nip04.encrypt(clientSecret, remotePubkey, serialized);
|
||||
@@ -254,8 +254,8 @@ test("parseNip46ConnectionString handles remote signer key hints", async () => {
|
||||
const signerSecret = utils.bytesToHex(generateSecretKey());
|
||||
const userSecret = utils.bytesToHex(generateSecretKey());
|
||||
|
||||
const signerPubkey = getPublicKey(signerSecret).toLowerCase();
|
||||
const userPubkey = getPublicKey(userSecret).toLowerCase();
|
||||
const signerPubkey = getPublicKey(utils.hexToBytes(signerSecret)).toLowerCase();
|
||||
const userPubkey = getPublicKey(utils.hexToBytes(userSecret)).toLowerCase();
|
||||
|
||||
const signerNpub = nip19.npubEncode(signerPubkey);
|
||||
const userNpub = nip19.npubEncode(userPubkey);
|
||||
@@ -295,13 +295,13 @@ test(
|
||||
const remoteSignerSecret = utils.bytesToHex(generateSecretKey());
|
||||
const userSecret = utils.bytesToHex(generateSecretKey());
|
||||
|
||||
const remoteSignerPubkey = getPublicKey(remoteSignerSecret).toLowerCase();
|
||||
const userPubkey = getPublicKey(userSecret).toLowerCase();
|
||||
const remoteSignerPubkey = getPublicKey(utils.hexToBytes(remoteSignerSecret)).toLowerCase();
|
||||
const userPubkey = getPublicKey(utils.hexToBytes(userSecret)).toLowerCase();
|
||||
|
||||
const conversationKey =
|
||||
typeof nip44.v2.getConversationKey === "function"
|
||||
? nip44.v2.getConversationKey(clientSecret, remoteSignerPubkey)
|
||||
: nip44.v2.utils.getConversationKey(clientSecret, remoteSignerPubkey);
|
||||
? nip44.v2.getConversationKey(utils.hexToBytes(clientSecret), remoteSignerPubkey)
|
||||
: nip44.v2.utils.getConversationKey(utils.hexToBytes(clientSecret), remoteSignerPubkey);
|
||||
|
||||
const payload = JSON.stringify({ id: "ack", result: "ok" });
|
||||
const ciphertext = nip44.v2.encrypt(payload, conversationKey);
|
||||
@@ -333,12 +333,12 @@ test(
|
||||
const { attemptDecryptNip46HandshakePayload } = await loadNip46Module();
|
||||
|
||||
const clientSecret = utils.bytesToHex(generateSecretKey());
|
||||
const clientPubkey = getPublicKey(clientSecret).toLowerCase();
|
||||
const clientPubkey = getPublicKey(utils.hexToBytes(clientSecret)).toLowerCase();
|
||||
const remoteSecret = utils.bytesToHex(generateSecretKey());
|
||||
const remotePubkey = getPublicKey(remoteSecret).toLowerCase();
|
||||
const remotePubkey = getPublicKey(utils.hexToBytes(remoteSecret)).toLowerCase();
|
||||
|
||||
const payload = JSON.stringify({ id: "ack", result: "ack" });
|
||||
const ciphertext = nip04.encrypt(remoteSecret, clientPubkey, payload);
|
||||
const ciphertext = await nip04.encrypt(remoteSecret, clientPubkey, payload);
|
||||
const [ciphertextPart, ivPart] = ciphertext.split("?iv=");
|
||||
|
||||
assert.ok(ivPart, "nip04 encryption should emit an iv segment");
|
||||
|
||||
Reference in new issue
Block a user