diff --git a/js/app.js b/js/app.js index e41f2e3..f6610ad 100644 --- a/js/app.js +++ b/js/app.js @@ -655,6 +655,53 @@ class bitvidApp { } } + async batchFetchProfiles(authorSet) { + const pubkeys = Array.from(authorSet); + if (!pubkeys.length) return; + + const filter = { + kinds: [0], + authors: pubkeys, + limit: pubkeys.length, + }; + + try { + // Query each relay + const results = await Promise.all( + nostrClient.relays.map(relayUrl => + nostrClient.pool.list([relayUrl], [filter]) + ) + ); + const allProfileEvents = results.flat(); + + // Keep only the newest per author + const newestEvents = new Map(); + for (const evt of allProfileEvents) { + if (!newestEvents.has(evt.pubkey) || + evt.created_at > newestEvents.get(evt.pubkey).created_at) { + newestEvents.set(evt.pubkey, evt); + } + } + + // Update the cache & DOM + for (const [pubkey, evt] of newestEvents.entries()) { + try { + const data = JSON.parse(evt.content); + const profile = { + name: data.name || data.display_name || "Unknown", + picture: data.picture || "assets/svg/default-profile.svg", + }; + this.profileCache.set(pubkey, { profile, timestamp: Date.now() }); + this.updateProfileInDOM(pubkey, profile); + } catch (err) { + console.error("Profile parse error:", err); + } + } + } catch (err) { + console.error("Batch profile fetch error:", err); + } + } + updateProfileInDOM(pubkey, profile) { // For any .author-pic[data-pubkey=...] const picEls = document.querySelectorAll( @@ -925,14 +972,11 @@ class bitvidApp { this.renderVideoList(filteredVideos); }); - // *** IMPORTANT ***: Unsubscribe once we get the historical EOSE - // so that we do not hold an open subscription forever: if (this.videoSubscription) { - this.videoSubscription.on("eose", () => { - this.videoSubscription.unsub(); - console.log("[loadVideos] unsubscribed after EOSE"); - }); + + console.log("[loadVideos] subscription remains open to get live updates."); } + } else { // Already subscribed: just show what's cached const allCached = nostrClient.getActiveVideos(); @@ -958,6 +1002,29 @@ class bitvidApp { } } + async loadOlderVideos(lastTimestamp) { + // 1) Use nostrClient to fetch older slices + const olderVideos = await nostrClient.fetchOlderVideos(lastTimestamp); + + if (!olderVideos || olderVideos.length === 0) { + this.showSuccess("No more older videos found."); + return; + } + + // 2) Merge them into the client’s allEvents / activeMap + for (const v of olderVideos) { + nostrClient.allEvents.set(v.id, v); + // If it’s the newest version for its root, update activeMap + const rootKey = v.videoRootId || v.id; + // You can call getActiveKey(v) if you want to match your code’s approach. + // Then re-check if this one is newer than what’s stored, etc. + } + + // 3) Re-render + const all = nostrClient.getActiveVideos(); + this.renderVideoList(all); + } + /** * Returns true if there's at least one strictly older version * (same videoRootId, created_at < current) which is NOT deleted. @@ -977,29 +1044,35 @@ class bitvidApp { async renderVideoList(videos) { if (!this.videoList) return; - + // Check if there's anything to show if (!videos || videos.length === 0) { this.videoList.innerHTML = ` -
- No public videos available yet. Be the first to upload one! -
`; ++ No public videos available yet. Be the first to upload one! +
`; return; } - + // Sort newest first videos.sort((a, b) => b.created_at - a.created_at); - + // Convert allEvents to an array for checking older overshadowed events const fullAllEventsArray = Array.from(nostrClient.allEvents.values()); const fragment = document.createDocumentFragment(); - + + // 1) Collect authors here so we can fetch profiles in one go + const authorSet = new Set(); + videos.forEach((video, index) => { if (!video.id || !video.title) { console.error("Video missing ID/title:", video); return; } - + + // Track this author's pubkey for the batch fetch later + authorSet.add(video.pubkey); + const nevent = window.NostrTools.nip19.neventEncode({ id: video.id }); const shareUrl = `${window.location.pathname}?v=${encodeURIComponent( nevent @@ -1010,138 +1083,134 @@ class bitvidApp { ? "border-2 border-yellow-500" : "border-none"; const timeAgo = this.formatTimeAgo(video.created_at); - + // Check if there's an older version (for revert button) let hasOlder = false; if (canEdit && video.videoRootId) { hasOlder = this.hasOlderVersion(video, fullAllEventsArray); } - + const revertButton = hasOlder ? ` - - ` + + ` : ""; - + // Gear menu (only shown if canEdit) const gearMenu = canEdit ? ` -