mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-04 11:52:25 +00:00
Align channel grid playback with main view
This commit is contained in:
1 parent
a97ce36c70
commit
0843c70123
1 file changed
+189
-419
+189
-419
@@ -8,8 +8,9 @@ import { subscriptions } from "./subscriptions.js"; // <-- NEW import
|
||||
import { attachHealthBadges } from "./gridHealth.js";
|
||||
import { attachUrlHealthBadges } from "./urlHealthObserver.js";
|
||||
import { accessControl } from "./accessControl.js";
|
||||
import { escapeHTML } from "./utils/domUtils.js";
|
||||
import { getApplication } from "./applicationContext.js";
|
||||
import { escapeHTML } from "./utils/domUtils.js";
|
||||
import { VideoCard } from "./ui/components/VideoCard.js";
|
||||
import {
|
||||
calculateZapShares,
|
||||
describeShareType,
|
||||
@@ -1704,9 +1705,97 @@ async function loadUserVideos(pubkey) {
|
||||
container.dataset.hasChannelVideos = "true";
|
||||
const fragment = document.createDocumentFragment();
|
||||
const allKnownEventsArray = Array.from(nostrClient.allEvents.values());
|
||||
const loadedThumbnails =
|
||||
app?.loadedThumbnails instanceof Map ? app.loadedThumbnails : null;
|
||||
const unsupportedBtihMessage =
|
||||
typeof app?.getUnsupportedBtihMessage === "function"
|
||||
? app.getUnsupportedBtihMessage()
|
||||
: "This magnet link is missing a compatible BitTorrent v1 info hash.";
|
||||
|
||||
const extractPlaybackDetail = (trigger, video) => {
|
||||
if (
|
||||
app?.videoListView &&
|
||||
typeof app.videoListView.extractPlaybackDetail === "function"
|
||||
) {
|
||||
return app.videoListView.extractPlaybackDetail(trigger, video);
|
||||
}
|
||||
|
||||
const element = trigger instanceof HTMLElement ? trigger : null;
|
||||
const target =
|
||||
element?.closest("[data-play-url],[data-play-magnet]") || element;
|
||||
|
||||
const rawUrlValue =
|
||||
(target?.dataset && typeof target.dataset.playUrl === "string"
|
||||
? target.dataset.playUrl
|
||||
: null) ?? target?.getAttribute?.("data-play-url") ?? "";
|
||||
const rawMagnetValue =
|
||||
(target?.dataset && typeof target.dataset.playMagnet === "string"
|
||||
? target.dataset.playMagnet
|
||||
: null) ?? target?.getAttribute?.("data-play-magnet") ?? "";
|
||||
|
||||
let url = "";
|
||||
if (rawUrlValue) {
|
||||
try {
|
||||
url = decodeURIComponent(rawUrlValue);
|
||||
} catch (error) {
|
||||
url = rawUrlValue;
|
||||
}
|
||||
}
|
||||
|
||||
const magnet = typeof rawMagnetValue === "string" ? rawMagnetValue : "";
|
||||
const videoId =
|
||||
target?.dataset?.videoId ||
|
||||
target?.getAttribute?.("data-video-id") ||
|
||||
video?.id ||
|
||||
"";
|
||||
|
||||
return { videoId, url, magnet, video };
|
||||
};
|
||||
|
||||
const playbackHandler =
|
||||
typeof app?.videoListViewPlaybackHandler === "function"
|
||||
? app.videoListViewPlaybackHandler.bind(app)
|
||||
: null;
|
||||
|
||||
const startPlayback = (detail) => {
|
||||
if (playbackHandler) {
|
||||
playbackHandler(detail);
|
||||
return;
|
||||
}
|
||||
|
||||
if (detail.videoId && typeof app?.playVideoByEventId === "function") {
|
||||
Promise.resolve(
|
||||
app.playVideoByEventId(detail.videoId, {
|
||||
url: detail.url,
|
||||
magnet: detail.magnet,
|
||||
title: detail.video?.title,
|
||||
description: detail.video?.description,
|
||||
})
|
||||
).catch((error) => {
|
||||
console.error("Failed to play channel video via event id:", error);
|
||||
if (typeof app?.playVideoWithFallback === "function") {
|
||||
app.playVideoWithFallback({
|
||||
url: detail.url,
|
||||
magnet: detail.magnet,
|
||||
});
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof app?.playVideoWithFallback === "function") {
|
||||
Promise.resolve(
|
||||
app.playVideoWithFallback({ url: detail.url, magnet: detail.magnet })
|
||||
).catch((error) => {
|
||||
console.error(
|
||||
"Failed to start fallback playback for channel video:",
|
||||
error
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
videos.forEach((video, index) => {
|
||||
// Decrypt if user owns a private video
|
||||
if (
|
||||
video.isPrivate &&
|
||||
video.pubkey === nostrClient.pubkey &&
|
||||
@@ -1716,304 +1805,118 @@ async function loadUserVideos(pubkey) {
|
||||
video.alreadyDecrypted = true;
|
||||
}
|
||||
|
||||
// Ensure the global videos map is kept up to date so delegated handlers
|
||||
// have the freshest metadata for this event.
|
||||
app?.videosMap?.set(video.id, video);
|
||||
|
||||
// Check if user can edit
|
||||
const canEdit = video.pubkey === app.pubkey;
|
||||
let hasOlder = false;
|
||||
if (canEdit && video.videoRootId) {
|
||||
hasOlder = app.hasOlderVersion(video, allKnownEventsArray);
|
||||
}
|
||||
|
||||
const revertButton = hasOlder
|
||||
? `
|
||||
<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>
|
||||
`
|
||||
: "";
|
||||
|
||||
let gearMenu = "";
|
||||
if (canEdit) {
|
||||
gearMenu = `
|
||||
<div class="relative inline-block ml-3 overflow-visible">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center
|
||||
w-10 h-10 p-2
|
||||
rounded-full text-gray-400 hover:text-gray-200 hover:bg-gray-800
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
data-settings-dropdown="${index}"
|
||||
>
|
||||
<img
|
||||
src="assets/svg/video-settings-gear.svg"
|
||||
alt="Settings"
|
||||
class="w-5 h-5 object-contain"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
id="settingsDropdown-${index}"
|
||||
class="hidden absolute right-0 bottom-full mb-2 w-32
|
||||
rounded-md shadow-lg bg-gray-800 ring-1 ring-black ring-opacity-5 z-50"
|
||||
>
|
||||
<div class="py-1">
|
||||
<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>
|
||||
${revertButton}
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const pointerInfo =
|
||||
typeof app?.deriveVideoPointerInfo === "function"
|
||||
? app.deriveVideoPointerInfo(video)
|
||||
: null;
|
||||
if (pointerInfo && typeof app?.persistWatchHistoryMetadataForVideo === "function") {
|
||||
app.persistWatchHistoryMetadataForVideo(video, pointerInfo);
|
||||
}
|
||||
|
||||
const moreMenu = `
|
||||
<div class="relative inline-block ml-1 overflow-visible" data-more-menu-wrapper="true">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center w-10 h-10 p-2 rounded-full text-gray-400 hover:text-gray-200 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
data-more-dropdown="${index}"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-label="More options"
|
||||
>
|
||||
<img src="assets/svg/ellipsis.svg" alt="More" class="w-5 h-5 object-contain" />
|
||||
</button>
|
||||
<div
|
||||
id="moreDropdown-${index}"
|
||||
class="hidden absolute right-0 bottom-full mb-2 w-40 rounded-md shadow-lg bg-gray-800 ring-1 ring-black ring-opacity-5 z-50"
|
||||
role="menu"
|
||||
data-more-menu="true"
|
||||
>
|
||||
<div class="py-1">
|
||||
<button class="block w-full text-left px-4 py-2 text-sm text-gray-100 hover:bg-gray-700" data-action="open-channel" data-author="${video.pubkey || ""}">
|
||||
Open channel
|
||||
</button>
|
||||
<button class="block w-full text-left px-4 py-2 text-sm text-gray-100 hover:bg-gray-700" data-action="copy-link" data-event-id="${video.id || ""}">
|
||||
Copy link
|
||||
</button>
|
||||
<button class="block w-full text-left px-4 py-2 text-sm text-red-400 hover:bg-red-700 hover:text-white" data-action="block-author" data-author="${video.pubkey || ""}">
|
||||
Block creator
|
||||
</button>
|
||||
<button class="block w-full text-left px-4 py-2 text-sm text-gray-100 hover:bg-gray-700" data-action="report" data-event-id="${video.id || ""}">
|
||||
Report
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const shareUrl =
|
||||
typeof app?.buildShareUrlFromEventId === "function"
|
||||
? app.buildShareUrlFromEventId(video.id)
|
||||
: "#";
|
||||
const timeAgo =
|
||||
typeof app?.formatTimeAgo === "function"
|
||||
? app.formatTimeAgo(video.created_at)
|
||||
: new Date(video.created_at * 1000).toLocaleString();
|
||||
|
||||
const cardControls = `
|
||||
<div class="flex items-center">
|
||||
${moreMenu}${gearMenu}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Fallback thumbnail
|
||||
const fallbackThumb = "assets/jpg/video-thumbnail-fallback.jpg";
|
||||
const safeThumb = video.thumbnail || fallbackThumb;
|
||||
const safeTitle = escapeHTML(video.title);
|
||||
|
||||
const cardEl = document.createElement("div");
|
||||
cardEl.classList.add(
|
||||
"video-card",
|
||||
"bg-gray-900",
|
||||
"rounded-lg",
|
||||
"overflow-hidden",
|
||||
"shadow-lg",
|
||||
"hover:shadow-2xl",
|
||||
"transition-all",
|
||||
"duration-300"
|
||||
);
|
||||
|
||||
cardEl.dataset.ownerIsViewer = canEdit ? "true" : "false";
|
||||
if (typeof video.pubkey === "string" && video.pubkey) {
|
||||
cardEl.dataset.ownerPubkey = video.pubkey;
|
||||
} else if (cardEl.dataset.ownerPubkey) {
|
||||
delete cardEl.dataset.ownerPubkey;
|
||||
}
|
||||
if (typeof video.id === "string" && video.id) {
|
||||
cardEl.dataset.videoId = video.id;
|
||||
} else if (cardEl.dataset.videoId) {
|
||||
delete cardEl.dataset.videoId;
|
||||
}
|
||||
|
||||
const rawMagnet =
|
||||
typeof video.magnet === "string" ? video.magnet : "";
|
||||
const trimmedMagnet = rawMagnet ? rawMagnet.trim() : "";
|
||||
const legacyInfoHash =
|
||||
typeof video.infoHash === "string" ? video.infoHash.trim() : "";
|
||||
const playbackUrl =
|
||||
typeof video.url === "string" ? video.url : "";
|
||||
const trimmedUrl = playbackUrl ? playbackUrl.trim() : "";
|
||||
const playbackMagnet = trimmedMagnet || legacyInfoHash || "";
|
||||
const magnetProvided = playbackMagnet.length > 0;
|
||||
const magnetSupported = app.isMagnetUriSupported(playbackMagnet);
|
||||
const showUnsupportedTorrentBadge =
|
||||
!trimmedUrl && magnetProvided && !magnetSupported;
|
||||
const urlBadgeHtml = trimmedUrl
|
||||
? app.getUrlHealthPlaceholderMarkup({ includeMargin: false })
|
||||
: "";
|
||||
const torrentHealthBadgeHtml =
|
||||
magnetSupported && magnetProvided
|
||||
? app.getTorrentHealthBadgeMarkup({ includeMargin: false })
|
||||
: "";
|
||||
const connectionBadgesHtml =
|
||||
urlBadgeHtml || torrentHealthBadgeHtml
|
||||
? `
|
||||
<div class="mt-3 flex flex-wrap items-center gap-2">
|
||||
${urlBadgeHtml}${torrentHealthBadgeHtml}
|
||||
</div>
|
||||
`
|
||||
: "";
|
||||
|
||||
cardEl.innerHTML = `
|
||||
<div
|
||||
class="cursor-pointer relative group"
|
||||
data-video-id="${video.id}"
|
||||
data-play-url=""
|
||||
data-play-magnet=""
|
||||
>
|
||||
<div class="ratio-16-9">
|
||||
<img
|
||||
src="${fallbackThumb}"
|
||||
data-lazy="${escapeHTML(safeThumb)}"
|
||||
alt="${safeTitle}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h3
|
||||
class="text-lg font-bold text-white mb-2 line-clamp-2"
|
||||
data-video-id="${video.id}"
|
||||
data-play-url=""
|
||||
data-play-magnet=""
|
||||
>
|
||||
${safeTitle}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500">
|
||||
${new Date(video.created_at * 1000).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
${cardControls}
|
||||
</div>
|
||||
${connectionBadgesHtml}
|
||||
</div>
|
||||
`;
|
||||
|
||||
if (showUnsupportedTorrentBadge) {
|
||||
cardEl.dataset.torrentSupported = "false";
|
||||
} else if (magnetProvided && magnetSupported) {
|
||||
cardEl.dataset.torrentSupported = "true";
|
||||
} else if (cardEl.dataset.torrentSupported) {
|
||||
delete cardEl.dataset.torrentSupported;
|
||||
}
|
||||
|
||||
if (trimmedUrl) {
|
||||
cardEl.dataset.urlHealthState = "checking";
|
||||
if (cardEl.dataset.urlHealthReason) {
|
||||
delete cardEl.dataset.urlHealthReason;
|
||||
}
|
||||
cardEl.dataset.urlHealthEventId = video.id || "";
|
||||
cardEl.dataset.urlHealthUrl = encodeURIComponent(trimmedUrl);
|
||||
} else {
|
||||
cardEl.dataset.urlHealthState = "offline";
|
||||
cardEl.dataset.urlHealthReason = "missing-source";
|
||||
if (cardEl.dataset.urlHealthEventId) {
|
||||
delete cardEl.dataset.urlHealthEventId;
|
||||
}
|
||||
if (cardEl.dataset.urlHealthUrl) {
|
||||
delete cardEl.dataset.urlHealthUrl;
|
||||
}
|
||||
}
|
||||
if (magnetProvided && magnetSupported) {
|
||||
cardEl.dataset.streamHealthState = "checking";
|
||||
if (cardEl.dataset.streamHealthReason) {
|
||||
delete cardEl.dataset.streamHealthReason;
|
||||
}
|
||||
} else {
|
||||
cardEl.dataset.streamHealthState = "unhealthy";
|
||||
cardEl.dataset.streamHealthReason = magnetProvided
|
||||
? "unsupported"
|
||||
: "missing-source";
|
||||
}
|
||||
|
||||
if (magnetProvided) {
|
||||
cardEl.dataset.magnet = playbackMagnet;
|
||||
} else if (cardEl.dataset.magnet) {
|
||||
delete cardEl.dataset.magnet;
|
||||
}
|
||||
|
||||
if (trimmedUrl) {
|
||||
cardEl.dataset.playUrl = encodeURIComponent(trimmedUrl);
|
||||
} else if (cardEl.dataset.playUrl) {
|
||||
delete cardEl.dataset.playUrl;
|
||||
}
|
||||
|
||||
cardEl.dataset.playMagnet = playbackMagnet || "";
|
||||
|
||||
// Leave the data-play-* attributes empty in the template markup so the raw
|
||||
// URL/magnet strings can be assigned after parsing without HTML entity
|
||||
// escaping, keeping this renderer consistent with app.js. The stored URL is
|
||||
// encoded so it stays intact within data-* attributes, and the click
|
||||
// handler decodes it while leaving magnets untouched until
|
||||
// safeDecodeMagnet() runs.
|
||||
const interactiveEls = cardEl.querySelectorAll("[data-video-id]");
|
||||
interactiveEls.forEach((el) => {
|
||||
if (!el.dataset) return;
|
||||
|
||||
if (trimmedUrl) {
|
||||
el.dataset.playUrl = encodeURIComponent(trimmedUrl);
|
||||
} else {
|
||||
delete el.dataset.playUrl;
|
||||
}
|
||||
|
||||
el.dataset.playMagnet = playbackMagnet || "";
|
||||
if (magnetProvided) {
|
||||
el.dataset.torrentSupported = magnetSupported ? "true" : "false";
|
||||
} else if (el.dataset.torrentSupported) {
|
||||
delete el.dataset.torrentSupported;
|
||||
}
|
||||
const videoCard = new VideoCard({
|
||||
document,
|
||||
video,
|
||||
index,
|
||||
shareUrl,
|
||||
pointerInfo,
|
||||
timeAgo,
|
||||
capabilities: {
|
||||
canEdit,
|
||||
canDelete: canEdit,
|
||||
canRevert: hasOlder,
|
||||
canManageBlacklist:
|
||||
typeof app?.canCurrentUserManageBlacklist === "function"
|
||||
? app.canCurrentUserManageBlacklist()
|
||||
: false,
|
||||
},
|
||||
helpers: {
|
||||
escapeHtml: (value) => escapeHTML(value),
|
||||
isMagnetSupported: (magnet) => app.isMagnetUriSupported(magnet),
|
||||
toLocaleString: (value) =>
|
||||
typeof value === "number" ? value.toLocaleString() : value,
|
||||
},
|
||||
assets: {
|
||||
fallbackThumbnailSrc: "assets/jpg/video-thumbnail-fallback.jpg",
|
||||
unsupportedBtihMessage,
|
||||
},
|
||||
state: { loadedThumbnails },
|
||||
ensureGlobalMoreMenuHandlers: () =>
|
||||
app?.ensureGlobalMoreMenuHandlers?.(),
|
||||
onRequestCloseAllMenus: () => app?.closeAllMoreMenus?.(),
|
||||
formatters: {
|
||||
formatTimeAgo: (ts) =>
|
||||
typeof app?.formatTimeAgo === "function"
|
||||
? app.formatTimeAgo(ts)
|
||||
: new Date(ts * 1000).toLocaleString(),
|
||||
},
|
||||
});
|
||||
|
||||
const badgeEl = cardEl.querySelector("[data-url-health-state]");
|
||||
if (badgeEl) {
|
||||
if (trimmedUrl) {
|
||||
badgeEl.dataset.urlHealthEventId = video.id || "";
|
||||
badgeEl.dataset.urlHealthUrl = encodeURIComponent(trimmedUrl);
|
||||
} else {
|
||||
if (badgeEl.dataset.urlHealthEventId) {
|
||||
delete badgeEl.dataset.urlHealthEventId;
|
||||
}
|
||||
if (badgeEl.dataset.urlHealthUrl) {
|
||||
delete badgeEl.dataset.urlHealthUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
videoCard.onPlay = ({ event: domEvent, video: cardVideo }) => {
|
||||
const trigger = domEvent?.currentTarget || domEvent?.target;
|
||||
const detail = extractPlaybackDetail(trigger, cardVideo || video);
|
||||
detail.video = detail.video || video;
|
||||
startPlayback(detail);
|
||||
};
|
||||
|
||||
fragment.appendChild(cardEl);
|
||||
videoCard.onEdit = ({ video: editVideo, index: editIndex }) => {
|
||||
app?.handleEditVideo?.({
|
||||
eventId: editVideo?.id || "",
|
||||
index: Number.isFinite(editIndex) ? editIndex : null,
|
||||
});
|
||||
};
|
||||
|
||||
videoCard.onRevert = ({ video: revertVideo, index: revertIndex }) => {
|
||||
app?.handleRevertVideo?.({
|
||||
eventId: revertVideo?.id || "",
|
||||
index: Number.isFinite(revertIndex) ? revertIndex : null,
|
||||
});
|
||||
};
|
||||
|
||||
videoCard.onDelete = ({ video: deleteVideo, index: deleteIndex }) => {
|
||||
app?.handleFullDeleteVideo?.({
|
||||
eventId: deleteVideo?.id || "",
|
||||
index: Number.isFinite(deleteIndex) ? deleteIndex : null,
|
||||
});
|
||||
};
|
||||
|
||||
videoCard.onMoreAction = ({ dataset = {} }) => {
|
||||
const action = dataset.action || "";
|
||||
const detail = {
|
||||
...dataset,
|
||||
eventId: dataset.eventId || video.id || "",
|
||||
context: dataset.context || "channel-grid",
|
||||
};
|
||||
app?.handleMoreMenuAction?.(action || "copy-link", detail);
|
||||
};
|
||||
|
||||
videoCard.onAuthorNavigate = ({ pubkey }) => {
|
||||
const targetPubkey = pubkey || video.pubkey || "";
|
||||
if (targetPubkey && typeof app?.goToProfile === "function") {
|
||||
app.goToProfile(targetPubkey);
|
||||
}
|
||||
};
|
||||
|
||||
const cardEl = videoCard.getRoot();
|
||||
if (cardEl) {
|
||||
fragment.appendChild(cardEl);
|
||||
}
|
||||
});
|
||||
|
||||
container.appendChild(fragment);
|
||||
@@ -2026,144 +1929,11 @@ async function loadUserVideos(pubkey) {
|
||||
|
||||
app?.mountVideoListView?.();
|
||||
|
||||
// Lazy-load images
|
||||
const lazyEls = container.querySelectorAll("[data-lazy]");
|
||||
lazyEls.forEach((el) => app?.mediaLoader?.observe?.(el));
|
||||
|
||||
app?.attachMoreMenuHandlers?.(container);
|
||||
|
||||
if (!container.dataset.playHandlerBound) {
|
||||
container.addEventListener("click", async (event) => {
|
||||
if (!event || !(event.target instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = event.target.closest("[data-play-url],[data-play-magnet]");
|
||||
if (!trigger || !(trigger instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isPrimaryClick =
|
||||
typeof event.button !== "number" || event.button === 0;
|
||||
if (!isPrimaryClick || event.ctrlKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!container.contains(trigger)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const dataset = trigger.dataset || {};
|
||||
const videoId = dataset.videoId || trigger.getAttribute("data-video-id") || "";
|
||||
const rawUrl = dataset.playUrl || trigger.getAttribute("data-play-url") || "";
|
||||
let url = "";
|
||||
if (typeof rawUrl === "string" && rawUrl) {
|
||||
try {
|
||||
url = decodeURIComponent(rawUrl);
|
||||
} catch (error) {
|
||||
url = rawUrl;
|
||||
}
|
||||
}
|
||||
|
||||
const magnetValue =
|
||||
dataset.playMagnet || trigger.getAttribute("data-play-magnet") || "";
|
||||
const magnet = typeof magnetValue === "string" ? magnetValue : "";
|
||||
|
||||
const playWithUrlAndMagnet = () => {
|
||||
if (typeof app?.playVideoWithFallback === "function") {
|
||||
app.playVideoWithFallback({ url, magnet });
|
||||
}
|
||||
};
|
||||
|
||||
if (videoId && typeof app?.playVideoByEventId === "function") {
|
||||
try {
|
||||
const result = await app.playVideoByEventId(videoId, {
|
||||
url,
|
||||
magnet,
|
||||
});
|
||||
if (!result || result.error) {
|
||||
playWithUrlAndMagnet();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to play via event id from channel grid:",
|
||||
error
|
||||
);
|
||||
playWithUrlAndMagnet();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
playWithUrlAndMagnet();
|
||||
});
|
||||
container.dataset.playHandlerBound = "true";
|
||||
}
|
||||
|
||||
// Gear menu toggles
|
||||
const gearButtons = container.querySelectorAll("[data-settings-dropdown]");
|
||||
gearButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
const idx = btn.getAttribute("data-settings-dropdown");
|
||||
const dropdown = document.getElementById(`settingsDropdown-${idx}`);
|
||||
if (dropdown) {
|
||||
dropdown.classList.toggle("hidden");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Edit handler
|
||||
const editBtns = container.querySelectorAll("[data-edit-index]");
|
||||
editBtns.forEach((btn) => {
|
||||
btn.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
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");
|
||||
const eventId = btn.getAttribute("data-edit-event-id") || "";
|
||||
app?.handleEditVideo?.({
|
||||
eventId,
|
||||
index: Number.isNaN(idx) ? null : idx,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Revert handler
|
||||
const revertBtns = container.querySelectorAll("[data-revert-index]");
|
||||
revertBtns.forEach((btn) => {
|
||||
btn.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
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");
|
||||
const eventId = btn.getAttribute("data-revert-event-id") || "";
|
||||
app?.handleRevertVideo?.({
|
||||
eventId,
|
||||
index: Number.isNaN(idx) ? null : idx,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Delete All handler
|
||||
const deleteAllBtns = container.querySelectorAll("[data-delete-all-index]");
|
||||
deleteAllBtns.forEach((btn) => {
|
||||
btn.addEventListener("click", (ev) => {
|
||||
ev.stopPropagation();
|
||||
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");
|
||||
const eventId = btn.getAttribute("data-delete-all-event-id") || "";
|
||||
app?.handleFullDeleteVideo?.({
|
||||
eventId,
|
||||
index: Number.isNaN(idx) ? null : idx,
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
if (loadToken === currentVideoLoadToken && container) {
|
||||
container.dataset.hasChannelVideos = "false";
|
||||
|
||||
Reference in new issue
Block a user