Merge pull request #94 from PR0M3TH3AN/codex/fix-video-ownership-error-on-edit

Fix ownership checks for video actions
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-09-25 17:06:49 -04:00
commit 8ea130fb22
3 files changed
+224 -39

No files matched your search

+170 -21
View File
@@ -2395,6 +2395,7 @@ class bitvidApp {
<button
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
</button>
@@ -2423,6 +2424,7 @@ class bitvidApp {
<button
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
</button>
@@ -2430,6 +2432,7 @@ class bitvidApp {
<button
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
</button>
@@ -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;
}
+27 -9
View File
@@ -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
</button>
@@ -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
</button>
@@ -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
</button>
@@ -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) {
+27 -9
View File
@@ -328,6 +328,7 @@ class SubscriptionsManager {
<button
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
</button>
@@ -356,6 +357,7 @@ class SubscriptionsManager {
<button
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
</button>
@@ -363,6 +365,7 @@ class SubscriptionsManager {
<button
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
</button>
@@ -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,
});
});
});