import { Controller } from '@hotwired/stimulus'; /* * Media Loader Controller * Handles "Load More" functionality for author media galleries * Fetches additional media items from cache and appends to masonry grid */ export default class extends Controller { static targets = ['grid', 'button', 'status']; static values = { npub: String, page: { type: Number, default: 2 }, total: Number }; connect() { this.isLoading = false; } async loadMore() { if (this.isLoading) return; this.isLoading = true; this.buttonTarget.disabled = true; this.buttonTarget.textContent = 'Loading...'; try { const url = `/p/${this.npubValue}/media/load-more?page=${this.pageValue}`; const response = await fetch(url); const data = await response.json(); // Add new media items to the grid data.events.forEach(event => { const item = this.createMediaItem(event); this.gridTarget.insertAdjacentHTML('beforeend', item); }); this.pageValue++; // Update status const currentCount = this.gridTarget.querySelectorAll('.masonry-item').length; this.statusTarget.textContent = `Showing ${currentCount} of ${data.total} media items`; // Hide button if no more items if (!data.hasMore) { this.buttonTarget.style.display = 'none'; this.statusTarget.textContent = `All ${data.total} media items loaded`; } } catch (error) { console.error('Error loading more media:', error); this.buttonTarget.textContent = 'Error - Click to retry'; } finally { this.isLoading = false; this.buttonTarget.disabled = false; if (this.buttonTarget.textContent === 'Loading...') { this.buttonTarget.textContent = 'Load More'; } } } createMediaItem(event) { // Extract title let title = null; let firstImageUrl = null; let firstVideoUrl = null; let imageAlt = null; let isVideo = false; // Find title tag event.tags.forEach(tag => { if (tag[0] === 'title') { title = tag[1]; } }); // Extract first image from imeta tags event.tags.forEach(tag => { if (tag[0] === 'imeta') { let videoUrl = null; let imageUrl = null; let previewImage = null; for (let i = 1; i < tag.length; i++) { const param = tag[i]; if (param.startsWith('url ')) { const potentialUrl = param.substring(4); if (/\.(mp4|webm|ogg|mov)$/i.test(potentialUrl) || /video/i.test(potentialUrl)) { videoUrl = potentialUrl; isVideo = true; } else { imageUrl = potentialUrl; } } else if (param.startsWith('image ')) { previewImage = param.substring(6); } else if (param.startsWith('alt ')) { imageAlt = param.substring(4); } } if (videoUrl && !firstVideoUrl) { firstVideoUrl = videoUrl; if (previewImage && !firstImageUrl) { firstImageUrl = previewImage; } else if (imageUrl && !firstImageUrl) { firstImageUrl = imageUrl; } } if (!videoUrl && !firstImageUrl) { if (imageUrl) { firstImageUrl = imageUrl; } else if (previewImage) { firstImageUrl = previewImage; } } } }); const eventDate = new Date(event.created_at * 1000).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); const contentPreview = event.content && event.content.length > 100 ? event.content.substring(0, 100) + '...' : event.content || ''; let imageHtml = ''; if (firstImageUrl) { imageHtml = `
${this.escapeHtml(contentPreview)}
` : ''}${this.escapeHtml(contentPreview)}
` : ''}