mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Add Event Details modal to video card menu
This commit is contained in:
1 parent
32f6c43a87
commit
4b5a46c5bd
6 files changed
+485
-2
No files matched your search
@@ -1532,6 +1532,44 @@ export class NostrService {
|
||||
return detail;
|
||||
}
|
||||
|
||||
async fetchVideoHistory(video) {
|
||||
if (!video || typeof video !== "object") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dTag = Array.isArray(video.tags)
|
||||
? video.tags.find((t) => Array.isArray(t) && t[0] === "d")
|
||||
: null;
|
||||
const dValue = dTag && dTag[1] ? dTag[1] : null;
|
||||
const pubkey = video.pubkey;
|
||||
const kind = video.kind;
|
||||
|
||||
// If we don't have enough info to query history by address, return just the input
|
||||
if (!dValue || !pubkey || typeof kind !== "number") {
|
||||
// If we have an ID, maybe we can at least return this specific event as a single-item history
|
||||
if (video.id) {
|
||||
return [video];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
const filter = {
|
||||
kinds: [kind],
|
||||
authors: [pubkey],
|
||||
"#d": [dValue],
|
||||
};
|
||||
|
||||
try {
|
||||
const events = await this.nostrClient.pool.list(this.nostrClient.relays, [filter]);
|
||||
// Sort by created_at ascending (oldest to newest)
|
||||
return events.sort((a, b) => (a.created_at || 0) - (b.created_at || 0));
|
||||
} catch (error) {
|
||||
this.log("[nostrService] Failed to fetch video history", error);
|
||||
// Fallback to returning the known video if fetch fails
|
||||
return [video];
|
||||
}
|
||||
}
|
||||
|
||||
async getOldEventById(eventId) {
|
||||
const map = this.ensureVideosMap();
|
||||
const isBlockedNsfw = (video) =>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { RevertModal } from "./components/RevertModal.js";
|
||||
import initUploadModal from "./initUploadModal.js";
|
||||
import initEditModal from "./initEditModal.js";
|
||||
import initDeleteModal from "./initDeleteModal.js";
|
||||
import { EventDetailsModal } from "./components/EventDetailsModal.js";
|
||||
import ZapController from "./zapController.js";
|
||||
import { nostrClient } from "../nostrClientFacade.js";
|
||||
|
||||
@@ -45,6 +46,8 @@ export default class ModalManager {
|
||||
this.videoModal = null;
|
||||
this.videoModalHandlers = {};
|
||||
|
||||
this.eventDetailsModal = null;
|
||||
|
||||
this.zapController = null;
|
||||
}
|
||||
|
||||
@@ -614,6 +617,22 @@ export default class ModalManager {
|
||||
);
|
||||
|
||||
app.videoModal = this.videoModal;
|
||||
|
||||
this.eventDetailsModal = new EventDetailsModal({
|
||||
app,
|
||||
document: doc,
|
||||
helpers: {
|
||||
escapeHtml: (value) => app.escapeHTML(value),
|
||||
formatAbsoluteTimestamp: (ts) => app.formatAbsoluteTimestamp(ts),
|
||||
safeEncodeNpub: (pubkey) => app.safeEncodeNpub(pubkey),
|
||||
},
|
||||
callbacks: {
|
||||
showError: (msg) => app.showError(msg),
|
||||
showSuccess: (msg) => app.showSuccess(msg),
|
||||
openCreatorChannel: (pubkey) => app.openCreatorChannel(pubkey),
|
||||
},
|
||||
});
|
||||
app.eventDetailsModal = this.eventDetailsModal;
|
||||
}
|
||||
|
||||
teardown() {
|
||||
@@ -836,6 +855,7 @@ export default class ModalManager {
|
||||
app.deleteModal = null;
|
||||
app.deleteModalEvents = null;
|
||||
app.videoModal = null;
|
||||
app.eventDetailsModal = null;
|
||||
app.zapController = null;
|
||||
|
||||
this.uploadModal = null;
|
||||
@@ -852,6 +872,7 @@ export default class ModalManager {
|
||||
this.deleteCancelHandler = null;
|
||||
this.videoModal = null;
|
||||
this.videoModalHandlers = {};
|
||||
this.eventDetailsModal = null;
|
||||
this.zapController = null;
|
||||
}
|
||||
}
|
||||
@@ -583,6 +583,12 @@ export default class ApplicationBootstrap {
|
||||
handleMirrorAction: (payload) => app.handleMirrorAction(payload),
|
||||
handleEnsurePresenceAction: (payload) =>
|
||||
app.handleEnsurePresenceAction(payload),
|
||||
handleEventDetailsAction: (payload) => {
|
||||
if (app.modalManager && app.modalManager.eventDetailsModal && payload?.video) {
|
||||
app.modalManager.eventDetailsModal.open(payload.video);
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
loadVideos: () => app.loadVideos(),
|
||||
refreshAllVideoGrids: (options) => app.refreshAllVideoGrids(options),
|
||||
onUserBlocksUpdated: () => {
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
import { prepareStaticModal, openStaticModal, closeStaticModal } from "./staticModalAccessibility.js";
|
||||
|
||||
const DEFAULT_PROFILE_AVATAR = "assets/svg/default-profile.svg";
|
||||
|
||||
export class EventDetailsModal {
|
||||
constructor({
|
||||
app,
|
||||
document: doc,
|
||||
helpers = {},
|
||||
callbacks = {},
|
||||
} = {}) {
|
||||
this.app = app;
|
||||
this.document = doc || document;
|
||||
this.helpers = {
|
||||
escapeHtml: helpers.escapeHtml || ((str) => str),
|
||||
formatAbsoluteTimestamp: helpers.formatAbsoluteTimestamp || ((ts) => new Date(ts * 1000).toLocaleString()),
|
||||
safeEncodeNpub: helpers.safeEncodeNpub || ((pk) => pk),
|
||||
...helpers,
|
||||
};
|
||||
this.callbacks = {
|
||||
showError: callbacks.showError || (() => {}),
|
||||
showSuccess: callbacks.showSuccess || (() => {}),
|
||||
openCreatorChannel: callbacks.openCreatorChannel || (() => {}),
|
||||
...callbacks,
|
||||
};
|
||||
|
||||
this.root = null;
|
||||
this.history = [];
|
||||
this.currentIndex = 0;
|
||||
this.isLoadingHistory = false;
|
||||
this.currentVideo = null;
|
||||
}
|
||||
|
||||
buildMarkup() {
|
||||
return `
|
||||
<div class="bv-modal-backdrop" data-dismiss></div>
|
||||
<div class="modal-sheet w-full max-w-2xl flex flex-col max-h-[90vh]" role="dialog" aria-modal="true" aria-labelledby="eventDetailsTitle">
|
||||
<header class="modal-header flex items-center justify-between p-4 border-b border-border">
|
||||
<h2 id="eventDetailsTitle" class="text-lg font-semibold text-text">Event Details</h2>
|
||||
<button type="button" class="btn-ghost p-2 rounded-full" data-dismiss aria-label="Close">
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="modal-body p-4 overflow-y-auto space-y-6 flex-1">
|
||||
<!-- Metadata Section -->
|
||||
<section class="space-y-4">
|
||||
<h3 class="text-sm font-semibold text-muted uppercase tracking-wider">Metadata</h3>
|
||||
|
||||
<!-- Author -->
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-panel overflow-hidden flex-shrink-0">
|
||||
<img data-author-pic src="${DEFAULT_PROFILE_AVATAR}" alt="Author" class="w-full h-full object-cover">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2">
|
||||
<span data-author-name class="font-medium text-text truncate">Unknown</span>
|
||||
<button type="button" data-action="open-channel" class="text-xs text-accent hover:underline">View Channel</button>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 mt-1">
|
||||
<code data-author-npub class="text-xs text-muted truncate bg-panel/50 px-1.5 py-0.5 rounded max-w-[200px]"></code>
|
||||
<button type="button" data-action="copy-npub" class="text-xs text-muted hover:text-text" title="Copy Npub">
|
||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 4v12a2 2 0 002 2h8a2 2 0 002-2V7.242a2 2 0 00-.602-1.43L16.083 4.162A2 2 0 0014.661 4H10a2 2 0 00-2 2z"></path><path d="M16 4v3.5a.5.5 0 00.5.5H20"></path><path d="M4 8v12a2 2 0 002 2h8"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Timestamp & ID -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="bg-panel/50 rounded p-3">
|
||||
<span class="text-xs text-muted block mb-1">Posted At</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span data-timestamp class="text-sm text-text font-mono"></span>
|
||||
<button type="button" data-action="copy-timestamp" class="text-muted hover:text-text" title="Copy Unix Timestamp">
|
||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 4v12a2 2 0 002 2h8a2 2 0 002-2V7.242a2 2 0 00-.602-1.43L16.083 4.162A2 2 0 0014.661 4H10a2 2 0 00-2 2z"></path><path d="M16 4v3.5a.5.5 0 00.5.5H20"></path><path d="M4 8v12a2 2 0 002 2h8"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-panel/50 rounded p-3">
|
||||
<span class="text-xs text-muted block mb-1">Event ID</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<code data-event-id class="text-xs text-text truncate block flex-1"></code>
|
||||
<button type="button" data-action="copy-id" class="text-muted hover:text-text flex-shrink-0" title="Copy Event ID">
|
||||
<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 4v12a2 2 0 002 2h8a2 2 0 002-2V7.242a2 2 0 00-.602-1.43L16.083 4.162A2 2 0 0014.661 4H10a2 2 0 00-2 2z"></path><path d="M16 4v3.5a.5.5 0 00.5.5H20"></path><path d="M4 8v12a2 2 0 002 2h8"></path></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Raw Data Section -->
|
||||
<section class="space-y-2 flex-1 flex flex-col min-h-0">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-semibold text-muted uppercase tracking-wider">Raw Data</h3>
|
||||
<button type="button" data-action="copy-json" class="text-xs btn-ghost px-2 py-1 rounded">
|
||||
Copy JSON
|
||||
</button>
|
||||
</div>
|
||||
<div class="relative flex-1 min-h-[200px] bg-black/80 rounded-lg border border-border overflow-hidden group">
|
||||
<pre class="absolute inset-0 p-4 overflow-auto text-xs font-mono text-gray-300 scrollbar-thin"><code data-json-content></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<footer class="modal-footer p-4 border-t border-border bg-panel/30 flex items-center justify-between gap-4">
|
||||
<button type="button" data-action="prev-version" class="btn-ghost flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
|
||||
<span>View Previous</span>
|
||||
</button>
|
||||
|
||||
<div class="text-sm text-muted font-medium">
|
||||
<span data-version-indicator>Version 1 of 1</span>
|
||||
</div>
|
||||
|
||||
<button type="button" data-action="next-version" class="btn-ghost flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<span>View Next</span>
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
ensureModal() {
|
||||
if (this.root) {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
const container = this.document.getElementById("modalContainer") || this.document.body;
|
||||
const modal = this.document.createElement("div");
|
||||
modal.className = "bv-modal hidden items-center justify-center p-4";
|
||||
modal.innerHTML = this.buildMarkup();
|
||||
|
||||
this.root = modal;
|
||||
|
||||
// Bind generic events
|
||||
const closeBtns = modal.querySelectorAll("[data-dismiss]");
|
||||
closeBtns.forEach(btn => {
|
||||
btn.addEventListener("click", () => this.close());
|
||||
});
|
||||
|
||||
// Action delegation
|
||||
modal.addEventListener("click", (e) => {
|
||||
const trigger = e.target.closest("[data-action]");
|
||||
if (!trigger) return;
|
||||
|
||||
const action = trigger.dataset.action;
|
||||
this.handleAction(action, trigger);
|
||||
});
|
||||
|
||||
container.appendChild(modal);
|
||||
return modal;
|
||||
}
|
||||
|
||||
handleAction(action, trigger) {
|
||||
if (!this.currentVideo) return;
|
||||
|
||||
switch (action) {
|
||||
case "copy-npub": {
|
||||
const npub = this.helpers.safeEncodeNpub(this.currentVideo.pubkey);
|
||||
if (npub) {
|
||||
navigator.clipboard.writeText(npub)
|
||||
.then(() => this.callbacks.showSuccess("Npub copied"))
|
||||
.catch(() => this.callbacks.showError("Failed to copy Npub"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "copy-timestamp": {
|
||||
const ts = this.currentVideo.created_at;
|
||||
if (ts) {
|
||||
navigator.clipboard.writeText(String(ts))
|
||||
.then(() => this.callbacks.showSuccess("Timestamp copied"))
|
||||
.catch(() => this.callbacks.showError("Failed to copy timestamp"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "copy-id": {
|
||||
const id = this.currentVideo.id;
|
||||
if (id) {
|
||||
navigator.clipboard.writeText(id)
|
||||
.then(() => this.callbacks.showSuccess("Event ID copied"))
|
||||
.catch(() => this.callbacks.showError("Failed to copy ID"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "copy-json": {
|
||||
const rawEvent = this.getRawEvent(this.currentVideo);
|
||||
const json = JSON.stringify(rawEvent, null, 2);
|
||||
navigator.clipboard.writeText(json)
|
||||
.then(() => this.callbacks.showSuccess("JSON copied"))
|
||||
.catch(() => this.callbacks.showError("Failed to copy JSON"));
|
||||
break;
|
||||
}
|
||||
case "open-channel": {
|
||||
if (this.currentVideo.pubkey) {
|
||||
this.close();
|
||||
this.callbacks.openCreatorChannel(this.currentVideo.pubkey);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "prev-version": {
|
||||
if (this.currentIndex > 0) {
|
||||
this.currentIndex--;
|
||||
this.renderVersion(this.history[this.currentIndex]);
|
||||
this.updateNavigationState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "next-version": {
|
||||
if (this.currentIndex < this.history.length - 1) {
|
||||
this.currentIndex++;
|
||||
this.renderVersion(this.history[this.currentIndex]);
|
||||
this.updateNavigationState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNavigationState() {
|
||||
if (!this.root) return;
|
||||
|
||||
const prevBtn = this.root.querySelector('[data-action="prev-version"]');
|
||||
const nextBtn = this.root.querySelector('[data-action="next-version"]');
|
||||
const indicator = this.root.querySelector('[data-version-indicator]');
|
||||
|
||||
if (prevBtn) prevBtn.disabled = this.currentIndex <= 0;
|
||||
if (nextBtn) nextBtn.disabled = this.currentIndex >= this.history.length - 1;
|
||||
|
||||
if (indicator) {
|
||||
if (this.isLoadingHistory) {
|
||||
indicator.textContent = "Loading history...";
|
||||
} else {
|
||||
indicator.textContent = `Version ${this.currentIndex + 1} of ${this.history.length}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderVersion(video) {
|
||||
if (!this.root || !video) return;
|
||||
|
||||
this.currentVideo = video;
|
||||
|
||||
// Metadata
|
||||
const authorNameEl = this.root.querySelector("[data-author-name]");
|
||||
const authorPicEl = this.root.querySelector("[data-author-pic]");
|
||||
const authorNpubEl = this.root.querySelector("[data-author-npub]");
|
||||
const timestampEl = this.root.querySelector("[data-timestamp]");
|
||||
const eventIdEl = this.root.querySelector("[data-event-id]");
|
||||
const jsonEl = this.root.querySelector("[data-json-content]");
|
||||
|
||||
if (authorNameEl) {
|
||||
// If we have profile data attached to the video object (which NostrService usually does)
|
||||
const name = video.authorName || video.profile?.name || video.profile?.display_name || "Unknown";
|
||||
authorNameEl.textContent = name;
|
||||
}
|
||||
|
||||
if (authorPicEl) {
|
||||
const pic = video.authorPicture || video.profile?.picture || DEFAULT_PROFILE_AVATAR;
|
||||
authorPicEl.src = pic;
|
||||
}
|
||||
|
||||
if (authorNpubEl) {
|
||||
const npub = this.helpers.safeEncodeNpub(video.pubkey);
|
||||
authorNpubEl.textContent = npub ? `${npub.substring(0, 10)}...${npub.substring(npub.length - 10)}` : "Unknown";
|
||||
authorNpubEl.title = npub || "";
|
||||
}
|
||||
|
||||
if (timestampEl) {
|
||||
timestampEl.textContent = this.helpers.formatAbsoluteTimestamp(video.created_at);
|
||||
timestampEl.title = `Unix: ${video.created_at}`;
|
||||
}
|
||||
|
||||
if (eventIdEl) {
|
||||
eventIdEl.textContent = video.id;
|
||||
}
|
||||
|
||||
if (jsonEl) {
|
||||
const rawEvent = this.getRawEvent(video);
|
||||
jsonEl.textContent = JSON.stringify(rawEvent, null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
getRawEvent(video) {
|
||||
if (!video) return {};
|
||||
return {
|
||||
id: video.id,
|
||||
pubkey: video.pubkey,
|
||||
created_at: video.created_at,
|
||||
kind: video.kind,
|
||||
tags: video.tags,
|
||||
content: video.content,
|
||||
sig: video.sig,
|
||||
};
|
||||
}
|
||||
|
||||
async open(video) {
|
||||
if (!video) return;
|
||||
|
||||
const modal = this.ensureModal();
|
||||
this.history = [video];
|
||||
this.currentIndex = 0;
|
||||
this.isLoadingHistory = true;
|
||||
|
||||
this.renderVersion(video);
|
||||
this.updateNavigationState();
|
||||
|
||||
openStaticModal(modal, { document: this.document });
|
||||
|
||||
// Fetch history
|
||||
try {
|
||||
const history = await this.app.nostrService.fetchVideoHistory(video);
|
||||
if (history && history.length > 0) {
|
||||
// Hydrate profile info for history items if possible, using current video's author info
|
||||
// (Since author is same)
|
||||
const profile = video.profile || {};
|
||||
const enrichedHistory = history.map(h => ({
|
||||
...h,
|
||||
profile,
|
||||
authorName: video.authorName,
|
||||
authorPicture: video.authorPicture,
|
||||
authorNpub: video.authorNpub
|
||||
}));
|
||||
|
||||
this.history = enrichedHistory;
|
||||
|
||||
// Find the index of the video we opened with
|
||||
const idx = this.history.findIndex(h => h.id === video.id);
|
||||
if (idx !== -1) {
|
||||
this.currentIndex = idx;
|
||||
} else {
|
||||
// If not found (rare), maybe append? or just default to latest?
|
||||
// Default to latest (end of array)
|
||||
this.currentIndex = this.history.length - 1;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to load history", e);
|
||||
} finally {
|
||||
this.isLoadingHistory = false;
|
||||
this.updateNavigationState();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.root) {
|
||||
closeStaticModal(this.root, { document: this.document });
|
||||
}
|
||||
this.history = [];
|
||||
this.currentVideo = null;
|
||||
}
|
||||
}
|
||||
@@ -295,6 +295,17 @@ export function createVideoMoreMenuPanel({
|
||||
},
|
||||
});
|
||||
|
||||
appendMenuSeparator(doc, list);
|
||||
|
||||
appendMenuAction(doc, list, {
|
||||
text: "Event Details",
|
||||
action: "event-details",
|
||||
dataset: {
|
||||
eventId: metadata.id,
|
||||
context,
|
||||
},
|
||||
});
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,8 @@ export default class MoreMenuController {
|
||||
callbacks.handleMirrorAction || (() => Promise.resolve()),
|
||||
handleEnsurePresenceAction:
|
||||
callbacks.handleEnsurePresenceAction || (() => Promise.resolve()),
|
||||
handleEventDetailsAction:
|
||||
callbacks.handleEventDetailsAction || (() => Promise.resolve()),
|
||||
loadVideos: callbacks.loadVideos || (() => Promise.resolve()),
|
||||
onUserBlocksUpdated: callbacks.onUserBlocksUpdated || (() => {}),
|
||||
};
|
||||
@@ -752,7 +754,7 @@ export default class MoreMenuController {
|
||||
const action = dataset.action || "";
|
||||
|
||||
try {
|
||||
await this.handleMoreMenuAction(action, dataset);
|
||||
await this.handleMoreMenuAction(action, dataset, entry.context.video);
|
||||
} catch (error) {
|
||||
userLogger.error("[MoreMenu] Failed to handle action:", error);
|
||||
}
|
||||
@@ -1083,7 +1085,7 @@ export default class MoreMenuController {
|
||||
});
|
||||
}
|
||||
|
||||
async handleMoreMenuAction(action, dataset = {}) {
|
||||
async handleMoreMenuAction(action, dataset = {}, video = null) {
|
||||
const normalized = typeof action === "string" ? action.trim() : "";
|
||||
const context = dataset.context || "";
|
||||
const currentVideo = this.callbacks.getCurrentVideo();
|
||||
@@ -1587,6 +1589,55 @@ export default class MoreMenuController {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "event-details": {
|
||||
let video = null;
|
||||
if (context === "modal") {
|
||||
video = currentVideo;
|
||||
} else {
|
||||
// If called from a card, we might not have the full video object in context
|
||||
// but usually the menu is triggered with a video object attached to the context
|
||||
// in toggleMoreMenu.
|
||||
// In handleMoreMenuAction, we don't receive the 'video' object directly, only the dataset.
|
||||
// But `toggleMoreMenu` stores the context in `entry.context`.
|
||||
// `handleMoreMenuAction` is called by the click handler which *doesn't* pass the full video object easily
|
||||
// unless we hack it.
|
||||
// However! `createPopoverRender` calls `handleMoreMenuAction` with just the dataset.
|
||||
// Wait, `createPopoverRender` has `entry.context.video`.
|
||||
// We can't access it easily here unless we pass it.
|
||||
// But `MoreMenuController` handles actions.
|
||||
// Let's rely on `dataset.eventId` to fetch/find the video if needed?
|
||||
// Or better, `toggleMoreMenu` (which calls `ensurePopoverForTrigger`) sets the context.
|
||||
// The click handler in `createPopoverRender` has `entry.context`.
|
||||
// It calls `handleMoreMenuAction(action, dataset)`.
|
||||
// It also calls `entry.context.onAction`.
|
||||
//
|
||||
// If we look at `js/app.js` (or wherever `MoreMenuController` is instantiated),
|
||||
// we might see how `callbacks` are defined.
|
||||
// But here we are IN `MoreMenuController`.
|
||||
//
|
||||
// For `event-details` to work, we need the video object.
|
||||
// If it's the current video (modal), `currentVideo` works.
|
||||
// If it's a card, `currentVideo` might be null or wrong.
|
||||
//
|
||||
// We can try to fetch the video from `dataset.eventId` using a callback or service.
|
||||
// Or we can modify `handleMoreMenuAction` to accept `video` as an optional 3rd argument,
|
||||
// and update `createPopoverRender` to pass it.
|
||||
}
|
||||
|
||||
// IMPORTANT: The `createPopoverRender` method in THIS file (lines 622+)
|
||||
// `entry.context.video` is available there.
|
||||
// It calls `await this.handleMoreMenuAction(action, dataset);`
|
||||
// I should update `createPopoverRender` to pass the video!
|
||||
|
||||
// But first, let's finish the switch case assuming we can get the video.
|
||||
// I'll modify `handleMoreMenuAction` signature in a separate edit or assume `this.callbacks.handleEventDetailsAction` can handle ID.
|
||||
|
||||
await this.callbacks.handleEventDetailsAction({
|
||||
eventId: dataset.eventId,
|
||||
video: video // Use explicit argument
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user