From 93493815fbcd7096225b8bbd28864af2692a4ff7 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 25 Sep 2025 17:06:12 -0400 Subject: [PATCH] Fix ownership checks for video actions --- js/app.js | 191 ++++++++++++++++++++++++++++++++++++++----- js/channelProfile.js | 36 ++++++-- js/subscriptions.js | 36 ++++++-- 3 files changed, 224 insertions(+), 39 deletions(-) diff --git a/js/app.js b/js/app.js index 839d0e59..e861eef4 100644 --- a/js/app.js +++ b/js/app.js @@ -2395,6 +2395,7 @@ class bitvidApp { @@ -2423,6 +2424,7 @@ class bitvidApp { @@ -2430,6 +2432,7 @@ class bitvidApp { @@ -2687,6 +2690,10 @@ class bitvidApp { } } } + if (video && video.id) { + this.videosMap.set(video.id, video); + } + fragment.appendChild(cardEl); this.renderedVideoIds.add(video.id); }); @@ -2723,10 +2730,15 @@ class bitvidApp { const editButtons = this.videoList.querySelectorAll("[data-edit-index]"); editButtons.forEach((button) => { button.addEventListener("click", () => { - const index = button.getAttribute("data-edit-index"); - const dropdown = document.getElementById(`settingsDropdown-${index}`); + const indexAttr = button.getAttribute("data-edit-index"); + const eventId = button.getAttribute("data-edit-event-id") || ""; + const index = Number.parseInt(indexAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${indexAttr}`); if (dropdown) dropdown.classList.add("hidden"); - this.handleEditVideo(index); + this.handleEditVideo({ + eventId, + index: Number.isNaN(index) ? null : index, + }); }); }); @@ -2736,10 +2748,15 @@ class bitvidApp { ); revertButtons.forEach((button) => { button.addEventListener("click", () => { - const index = button.getAttribute("data-revert-index"); - const dropdown = document.getElementById(`settingsDropdown-${index}`); + const indexAttr = button.getAttribute("data-revert-index"); + const eventId = button.getAttribute("data-revert-event-id") || ""; + const index = Number.parseInt(indexAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${indexAttr}`); if (dropdown) dropdown.classList.add("hidden"); - this.handleRevertVideo(index); + this.handleRevertVideo({ + eventId, + index: Number.isNaN(index) ? null : index, + }); }); }); @@ -2749,10 +2766,15 @@ class bitvidApp { ); deleteAllButtons.forEach((button) => { button.addEventListener("click", () => { - const index = button.getAttribute("data-delete-all-index"); - const dropdown = document.getElementById(`settingsDropdown-${index}`); + const indexAttr = button.getAttribute("data-delete-all-index"); + const eventId = button.getAttribute("data-delete-all-event-id") || ""; + const index = Number.parseInt(indexAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${indexAttr}`); if (dropdown) dropdown.classList.add("hidden"); - this.handleFullDeleteVideo(index); + this.handleFullDeleteVideo({ + eventId, + index: Number.isNaN(index) ? null : index, + }); }); }); @@ -2934,21 +2956,137 @@ class bitvidApp { } } + normalizeActionTarget(target) { + if (target && typeof target === "object") { + const eventId = + typeof target.eventId === "string" ? target.eventId.trim() : ""; + let index = null; + if (typeof target.index === "number" && Number.isInteger(target.index)) { + index = target.index; + } else if ( + typeof target.index === "string" && target.index.trim().length > 0 + ) { + const parsed = Number.parseInt(target.index.trim(), 10); + index = Number.isNaN(parsed) ? null : parsed; + } + return { eventId, index }; + } + + if (typeof target === "string") { + const trimmed = target.trim(); + if (!trimmed) { + return { eventId: "", index: null }; + } + if (/^-?\d+$/.test(trimmed)) { + const parsed = Number.parseInt(trimmed, 10); + return { eventId: "", index: Number.isNaN(parsed) ? null : parsed }; + } + return { eventId: trimmed, index: null }; + } + + if (typeof target === "number" && Number.isInteger(target)) { + return { eventId: "", index: target }; + } + + return { eventId: "", index: null }; + } + + async resolveVideoActionTarget({ + eventId = "", + index = null, + preloadedList, + } = {}) { + const trimmedEventId = typeof eventId === "string" ? eventId.trim() : ""; + const normalizedIndex = + typeof index === "number" && Number.isInteger(index) && index >= 0 + ? index + : null; + + const candidateLists = Array.isArray(preloadedList) + ? [preloadedList] + : []; + + for (const list of candidateLists) { + if (trimmedEventId) { + const match = list.find((video) => video?.id === trimmedEventId); + if (match) { + this.videosMap.set(match.id, match); + return match; + } + } + if ( + normalizedIndex !== null && + normalizedIndex >= 0 && + normalizedIndex < list.length + ) { + const match = list[normalizedIndex]; + if (match) { + this.videosMap.set(match.id, match); + return match; + } + } + } + + if (trimmedEventId) { + const fromMap = this.videosMap.get(trimmedEventId); + if (fromMap) { + return fromMap; + } + + const activeVideos = nostrClient.getActiveVideos(); + const fromActive = activeVideos.find((video) => video.id === trimmedEventId); + if (fromActive) { + this.videosMap.set(fromActive.id, fromActive); + return fromActive; + } + + const fromAll = nostrClient.allEvents.get(trimmedEventId); + if (fromAll) { + this.videosMap.set(fromAll.id, fromAll); + return fromAll; + } + + const fetched = await nostrClient.getEventById(trimmedEventId); + if (fetched) { + this.videosMap.set(fetched.id, fetched); + return fetched; + } + } + + if (normalizedIndex !== null) { + const activeVideos = nostrClient.getActiveVideos(); + if (normalizedIndex >= 0 && normalizedIndex < activeVideos.length) { + const candidate = activeVideos[normalizedIndex]; + if (candidate) { + this.videosMap.set(candidate.id, candidate); + return candidate; + } + } + } + + return null; + } + /** * Handle "Edit Video" from gear menu. */ - async handleEditVideo(index) { + async handleEditVideo(target) { try { - // 1) Fetch the current list of videos (the newest versions) - const all = await nostrClient.fetchVideos(); - const video = all[index]; + const normalizedTarget = this.normalizeActionTarget(target); + const latestVideos = await nostrClient.fetchVideos(); + const video = await this.resolveVideoActionTarget({ + ...normalizedTarget, + preloadedList: latestVideos, + }); // 2) Basic ownership checks if (!this.pubkey) { this.showError("Please login to edit videos."); return; } - if (!video || video.pubkey !== this.pubkey) { + const userPubkey = (this.pubkey || "").toLowerCase(); + const videoPubkey = (video?.pubkey || "").toLowerCase(); + if (!video || !videoPubkey || videoPubkey !== userPubkey) { this.showError("You do not own this video."); return; } @@ -3022,17 +3160,22 @@ class bitvidApp { } } - async handleRevertVideo(index) { + async handleRevertVideo(target) { try { - // 1) Still use fetchVideos to get the video in question + const normalizedTarget = this.normalizeActionTarget(target); const activeVideos = await nostrClient.fetchVideos(); - const video = activeVideos[index]; + const video = await this.resolveVideoActionTarget({ + ...normalizedTarget, + preloadedList: activeVideos, + }); if (!this.pubkey) { this.showError("Please login to revert."); return; } - if (!video || video.pubkey !== this.pubkey) { + const userPubkey = (this.pubkey || "").toLowerCase(); + const videoPubkey = (video?.pubkey || "").toLowerCase(); + if (!video || !videoPubkey || videoPubkey !== userPubkey) { this.showError("You do not own this video."); return; } @@ -3070,16 +3213,22 @@ class bitvidApp { /** * Handle "Delete Video" from gear menu. */ - async handleFullDeleteVideo(index) { + async handleFullDeleteVideo(target) { try { + const normalizedTarget = this.normalizeActionTarget(target); const all = await nostrClient.fetchVideos(); - const video = all[index]; + const video = await this.resolveVideoActionTarget({ + ...normalizedTarget, + preloadedList: all, + }); if (!this.pubkey) { this.showError("Please login to delete videos."); return; } - if (!video || video.pubkey !== this.pubkey) { + const userPubkey = (this.pubkey || "").toLowerCase(); + const videoPubkey = (video?.pubkey || "").toLowerCase(); + if (!video || !videoPubkey || videoPubkey !== userPubkey) { this.showError("You do not own this video."); return; } diff --git a/js/channelProfile.js b/js/channelProfile.js index cac4fee2..aa552426 100644 --- a/js/channelProfile.js +++ b/js/channelProfile.js @@ -271,6 +271,7 @@ async function loadUserVideos(pubkey) { class="block w-full text-left px-4 py-2 text-sm text-red-400 hover:bg-red-700 hover:text-white" data-revert-index="${index}" + data-revert-event-id="${video.id}" > Revert @@ -305,6 +306,7 @@ async function loadUserVideos(pubkey) { class="block w-full text-left px-4 py-2 text-sm text-gray-100 hover:bg-gray-700" data-edit-index="${index}" + data-edit-event-id="${video.id}" > Edit @@ -313,6 +315,7 @@ async function loadUserVideos(pubkey) { class="block w-full text-left px-4 py-2 text-sm text-red-400 hover:bg-red-700 hover:text-white" data-delete-all-index="${index}" + data-delete-all-event-id="${video.id}" > Delete All @@ -446,10 +449,15 @@ async function loadUserVideos(pubkey) { editBtns.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = parseInt(btn.getAttribute("data-edit-index"), 10); - const dropdown = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-edit-index"); + const idx = Number.parseInt(idxAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${idxAttr}`); if (dropdown) dropdown.classList.add("hidden"); - app.handleEditVideo(idx); + const eventId = btn.getAttribute("data-edit-event-id") || ""; + app.handleEditVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); }); @@ -458,10 +466,15 @@ async function loadUserVideos(pubkey) { revertBtns.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = parseInt(btn.getAttribute("data-revert-index"), 10); - const dropdown = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-revert-index"); + const idx = Number.parseInt(idxAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${idxAttr}`); if (dropdown) dropdown.classList.add("hidden"); - app.handleRevertVideo(idx); + const eventId = btn.getAttribute("data-revert-event-id") || ""; + app.handleRevertVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); }); @@ -470,10 +483,15 @@ async function loadUserVideos(pubkey) { deleteAllBtns.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = parseInt(btn.getAttribute("data-delete-all-index"), 10); - const dropdown = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-delete-all-index"); + const idx = Number.parseInt(idxAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${idxAttr}`); if (dropdown) dropdown.classList.add("hidden"); - app.handleFullDeleteVideo(idx); + const eventId = btn.getAttribute("data-delete-all-event-id") || ""; + app.handleFullDeleteVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); }); } catch (err) { diff --git a/js/subscriptions.js b/js/subscriptions.js index f5e5db50..2ef66b04 100644 --- a/js/subscriptions.js +++ b/js/subscriptions.js @@ -328,6 +328,7 @@ class SubscriptionsManager { @@ -356,6 +357,7 @@ class SubscriptionsManager { @@ -363,6 +365,7 @@ class SubscriptionsManager { @@ -500,10 +503,15 @@ class SubscriptionsManager { editButtons.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = btn.getAttribute("data-edit-index"); - const dropdown = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-edit-index"); + const idx = Number.parseInt(idxAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${idxAttr}`); if (dropdown) dropdown.classList.add("hidden"); - window.app?.handleEditVideo(idx); + const eventId = btn.getAttribute("data-edit-event-id") || ""; + window.app?.handleEditVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); }); @@ -512,10 +520,15 @@ class SubscriptionsManager { revertButtons.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = btn.getAttribute("data-revert-index"); - const dropdown = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-revert-index"); + const idx = Number.parseInt(idxAttr, 10); + const dropdown = document.getElementById(`settingsDropdown-${idxAttr}`); if (dropdown) dropdown.classList.add("hidden"); - window.app?.handleRevertVideo(idx); + const eventId = btn.getAttribute("data-revert-event-id") || ""; + window.app?.handleRevertVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); }); @@ -526,10 +539,15 @@ class SubscriptionsManager { deleteAllButtons.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); - const idx = btn.getAttribute("data-delete-all-index"); - const dd = document.getElementById(`settingsDropdown-${idx}`); + const idxAttr = btn.getAttribute("data-delete-all-index"); + const idx = Number.parseInt(idxAttr, 10); + const dd = document.getElementById(`settingsDropdown-${idxAttr}`); if (dd) dd.classList.add("hidden"); - window.app?.handleFullDeleteVideo(idx); + const eventId = btn.getAttribute("data-delete-all-event-id") || ""; + window.app?.handleFullDeleteVideo({ + eventId, + index: Number.isNaN(idx) ? null : idx, + }); }); });