diff --git a/js/channelProfile.js b/js/channelProfile.js index 5325cac..05a12c7 100644 --- a/js/channelProfile.js +++ b/js/channelProfile.js @@ -14,7 +14,9 @@ export async function initChannelProfileView() { const hashParams = new URLSearchParams(window.location.hash.slice(1)); const npub = hashParams.get("npub"); if (!npub) { - console.error("No npub found in hash. Example: #view=channel-profile&npub=npub1..."); + console.error( + "No npub found in hash. Example: #view=channel-profile&npub=npub1..." + ); return; } @@ -35,12 +37,12 @@ export async function initChannelProfileView() { // 3) Load user’s profile (banner, avatar, etc.) await loadUserProfile(hexPub); - // 4) Load user’s videos (filtered and rendered like home feed) + // 4) Load user’s videos (filtered + rendered like the home feed) await loadUserVideos(hexPub); } /** - * Fetches and displays the user’s metadata (kind:0). + * Fetches and displays the user’s metadata (kind=0). */ async function loadUserProfile(pubkey) { try { @@ -97,7 +99,8 @@ async function loadUserProfile(pubkey) { // Lightning Address const lnEl = document.getElementById("channelLightning"); if (lnEl) { - lnEl.textContent = meta.lud16 || meta.lud06 || "No lightning address found."; + lnEl.textContent = + meta.lud16 || meta.lud06 || "No lightning address found."; } } else { console.warn("No metadata found for this user."); @@ -109,8 +112,7 @@ async function loadUserProfile(pubkey) { /** * Fetches and displays this user's videos (kind=30078), - * filtering out older/overshadowed events and blacklisted/non‐whitelisted entries. - * Renders the video cards using the same `.ratio-16-9` approach as the home view. + * filtering out older overshadowed notes, blacklisted, non‐whitelisted, etc. */ async function loadUserVideos(pubkey) { try { @@ -133,7 +135,7 @@ async function loadUserVideos(pubkey) { } } - // 3) Convert events to "video" objects + // 3) Convert to "video" objects let videos = []; for (const evt of events) { const vid = localConvertEventToVideo(evt); @@ -142,30 +144,27 @@ async function loadUserVideos(pubkey) { } } - // 4) Deduplicate older overshadowed versions (newest only) + // 4) Deduplicate older overshadowed versions => newest only videos = dedupeToNewestByRoot(videos); - // 5) Filter out blacklisted event IDs and authors + // 5) Filter out blacklisted IDs / authors videos = videos.filter((video) => { // Event-level blacklisting - if (app.blacklistedEventIds.has(video.id)) { - return false; - } - // Author-level checks + if (app.blacklistedEventIds.has(video.id)) return false; + + // Author-level const authorNpub = app.safeEncodeNpub(video.pubkey) || video.pubkey; - if (initialBlacklist.includes(authorNpub)) { - return false; - } + if (initialBlacklist.includes(authorNpub)) return false; if (isWhitelistEnabled && !initialWhitelist.includes(authorNpub)) { return false; } return true; }); - // 6) Sort videos by newest first + // 6) Sort newest first videos.sort((a, b) => b.created_at - a.created_at); - // 7) Render the videos in #channelVideoList + // 7) Render them const container = document.getElementById("channelVideoList"); if (!container) { console.warn("channelVideoList element not found in DOM."); @@ -178,11 +177,13 @@ async function loadUserVideos(pubkey) { } const fragment = document.createDocumentFragment(); - // We'll store them in a local array so gear handlers match the indexes const channelVideos = videos; + // We'll need all known events for revert-check + const allKnownEventsArray = Array.from(nostrClient.allEvents.values()); + channelVideos.forEach((video, index) => { - // If private + the user is the owner => decrypt + // Private => decrypt if owned by the user if ( video.isPrivate && video.pubkey === nostrClient.pubkey && @@ -192,8 +193,28 @@ async function loadUserVideos(pubkey) { video.alreadyDecrypted = true; } - // Determine if the logged-in user can edit + // Check if user can edit const canEdit = video.pubkey === app.pubkey; + let hasOlder = false; + if (canEdit && video.videoRootId) { + // Use the same hasOlderVersion approach as home feed + hasOlder = app.hasOlderVersion(video, allKnownEventsArray); + } + + // If there's an older overshadowed version, show revert + const revertButton = hasOlder + ? ` + + Revert + + ` + : ""; + + // Gear menu let gearMenu = ""; if (canEdit) { gearMenu = ` @@ -224,6 +245,7 @@ async function loadUserVideos(pubkey) { > Edit + ${revertButton} @@ -290,11 +311,11 @@ async function loadUserVideos(pubkey) { container.appendChild(fragment); - // Use app's lazy loader for thumbs + // Lazy-load const lazyEls = container.querySelectorAll("[data-lazy]"); lazyEls.forEach((el) => app.mediaLoader.observe(el)); - // Gear menus + // Gear menu toggles const gearButtons = container.querySelectorAll("[data-settings-dropdown]"); gearButtons.forEach((btn) => { btn.addEventListener("click", (ev) => { @@ -307,22 +328,41 @@ async function loadUserVideos(pubkey) { }); }); - // "Edit" button + // "Edit" handler const editBtns = container.querySelectorAll("[data-edit-index]"); editBtns.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); const idx = parseInt(btn.getAttribute("data-edit-index"), 10); + // Hide the dropdown + const dropdown = document.getElementById(`settingsDropdown-${idx}`); + if (dropdown) dropdown.classList.add("hidden"); app.handleEditVideo(idx); }); }); - // "Delete All" button + // "Revert" handler + const revertBtns = container.querySelectorAll("[data-revert-index]"); + revertBtns.forEach((btn) => { + btn.addEventListener("click", (ev) => { + ev.stopPropagation(); + const idx = parseInt(btn.getAttribute("data-revert-index"), 10); + // Hide the dropdown + const dropdown = document.getElementById(`settingsDropdown-${idx}`); + if (dropdown) dropdown.classList.add("hidden"); + app.handleRevertVideo(idx); + }); + }); + + // "Delete All" handler const deleteAllBtns = container.querySelectorAll("[data-delete-all-index]"); deleteAllBtns.forEach((btn) => { btn.addEventListener("click", (ev) => { ev.stopPropagation(); const idx = parseInt(btn.getAttribute("data-delete-all-index"), 10); + // Hide the dropdown + const dropdown = document.getElementById(`settingsDropdown-${idx}`); + if (dropdown) dropdown.classList.add("hidden"); app.handleFullDeleteVideo(idx); }); });