mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-29 17:02:28 +00:00
feat(my-videos): thumbnail + Edit/Delete row actions (Phase 1, step 5)
Each My Videos row now shows the video's thumbnail (lazy, with a placeholder box that survives a broken/missing image) and, for active videos, Edit and Delete buttons that route to the app's existing handleEditVideo / handleFullDeleteVideo flows (closing the full-screen profile modal first so those modals aren't hidden behind it). Delete reuses the app flow that already cleans the live URL's storage. Deleted videos intentionally have no per-row action yet: the tombstone scrubbed their URL, so reclaiming their orphaned bucket file requires the Phase 2 bucket listing/reconciliation (and Restore) — added there so it's done correctly. Build + full lint green (DOM via createElement/textContent, no innerHTML). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
ff04fcbd4b
commit
f847bce0d5
1 file changed
+71
-2
@@ -1,5 +1,6 @@
|
||||
import { devLogger } from "../../utils/logger.js";
|
||||
import { nostrClient } from "../../nostrClientFacade.js";
|
||||
import { getApplication } from "../../applicationContext.js";
|
||||
import { collapseUserVideos } from "./myVideosData.js";
|
||||
import { classifyVideoHealth, isUrlUnderBase } from "./myVideosHealth.js";
|
||||
|
||||
@@ -179,10 +180,11 @@ export class MyVideosController {
|
||||
});
|
||||
|
||||
const li = document.createElement("li");
|
||||
li.className =
|
||||
"card flex items-center justify-between gap-3 p-3 border border-border/60";
|
||||
li.className = "card flex items-center gap-3 p-3 border border-border/60";
|
||||
li.dataset.health = health.status;
|
||||
|
||||
li.appendChild(this.buildThumb(video));
|
||||
|
||||
const info = document.createElement("div");
|
||||
info.className = "min-w-0 flex-1";
|
||||
|
||||
@@ -208,9 +210,76 @@ export class MyVideosController {
|
||||
info.appendChild(titleRow);
|
||||
info.appendChild(meta);
|
||||
li.appendChild(info);
|
||||
|
||||
li.appendChild(this.buildActions(video));
|
||||
return li;
|
||||
}
|
||||
|
||||
buildThumb(video) {
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className =
|
||||
"h-12 w-20 shrink-0 overflow-hidden rounded bg-surface-strong/40";
|
||||
const url = typeof video.thumbnail === "string" ? video.thumbnail.trim() : "";
|
||||
if (url) {
|
||||
const img = document.createElement("img");
|
||||
img.className = "h-full w-full object-cover";
|
||||
img.loading = "lazy";
|
||||
img.alt = "";
|
||||
img.src = url;
|
||||
// A broken/missing thumbnail leaves the placeholder box rather than the
|
||||
// browser's broken-image glyph.
|
||||
img.addEventListener("error", () => img.remove());
|
||||
wrap.appendChild(img);
|
||||
}
|
||||
return wrap;
|
||||
}
|
||||
|
||||
buildActions(video) {
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "flex items-center gap-2 shrink-0";
|
||||
// Active videos can be edited or deleted. Cleaning up a DELETED video's
|
||||
// orphaned file needs the bucket listing (the tombstone scrubbed its URL),
|
||||
// so that action arrives in Phase 2 (storage reconciliation).
|
||||
if (!video.deleted) {
|
||||
actions.appendChild(
|
||||
this.buildActionButton("Edit", () => this.handleEdit(video)),
|
||||
);
|
||||
actions.appendChild(
|
||||
this.buildActionButton("Delete", () => this.handleDelete(video), true),
|
||||
);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
buildActionButton(label, onClick, danger = false) {
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = danger
|
||||
? "btn-ghost focus-ring text-xs text-status-danger"
|
||||
: "btn-ghost focus-ring text-xs";
|
||||
btn.textContent = label;
|
||||
btn.addEventListener("click", onClick);
|
||||
return btn;
|
||||
}
|
||||
|
||||
handleEdit(video) {
|
||||
const app = getApplication();
|
||||
// The edit modal is a separate top-level surface; close the (full-screen)
|
||||
// profile modal first so it isn't hidden behind it.
|
||||
this.mainController.hide();
|
||||
if (app && typeof app.handleEditVideo === "function") {
|
||||
void app.handleEditVideo({ video });
|
||||
}
|
||||
}
|
||||
|
||||
handleDelete(video) {
|
||||
const app = getApplication();
|
||||
this.mainController.hide();
|
||||
if (app && typeof app.handleFullDeleteVideo === "function") {
|
||||
void app.handleFullDeleteVideo({ video });
|
||||
}
|
||||
}
|
||||
|
||||
describeSource(video) {
|
||||
const parts = [];
|
||||
if (typeof video.url === "string" && video.url.trim()) {
|
||||
|
||||
Reference in new issue
Block a user