//js/ui-interactions.js import { SVG_CLOSE, SVG_BIN, SVG_HEART, SVG_DOWNLOAD, formatTime, trackDataStore, getTrackTitle, getTrackArtists, escapeHtml, } from './utils.js'; import { sidePanelManager } from './side-panel.js'; import { downloadQualitySettings } from './storage.js'; export function initializeUIInteractions(player, api) { const sidebar = document.querySelector('.sidebar'); const sidebarOverlay = document.getElementById('sidebar-overlay'); const hamburgerBtn = document.getElementById('hamburger-btn'); const queueBtn = document.getElementById('queue-btn'); let draggedQueueIndex = null; // Sidebar mobile hamburgerBtn.addEventListener('click', () => { sidebar.classList.add('is-open'); sidebarOverlay.classList.add('is-visible'); }); const closeSidebar = () => { sidebar.classList.remove('is-open'); sidebarOverlay.classList.remove('is-visible'); }; sidebarOverlay.addEventListener('click', closeSidebar); sidebar.addEventListener('click', (e) => { if (e.target.closest('a')) { closeSidebar(); } }); // Queue panel const renderQueueControls = (container) => { const currentQueue = player.getCurrentQueue(); const showActionBtns = currentQueue.length > 0; container.innerHTML = ` `; container.querySelector('#close-side-panel-btn').addEventListener('click', () => { sidePanelManager.close(); }); const downloadBtn = container.querySelector('#download-queue-btn'); if (downloadBtn) { downloadBtn.addEventListener('click', async () => { const { downloadTracks } = await import('./downloads.js'); downloadTracks(currentQueue, api, downloadQualitySettings.getQuality()); }); } const likeBtn = container.querySelector('#like-queue-btn'); if (likeBtn) { likeBtn.addEventListener('click', async () => { const { db } = await import('./db.js'); const { syncManager } = await import('./firebase/sync.js'); const { showNotification } = await import('./downloads.js'); let addedCount = 0; for (const track of currentQueue) { const wasAdded = await db.toggleFavorite('track', track); if (wasAdded) { syncManager.syncLibraryItem('track', track, true); addedCount++; } } if (addedCount > 0) { showNotification(`Added ${addedCount} track${addedCount > 1 ? 's' : ''} to Liked`); } else { showNotification('All tracks in queue are already liked'); } refreshQueuePanel(); }); } const addToPlaylistBtn = container.querySelector('#add-queue-to-playlist-btn'); if (addToPlaylistBtn) { addToPlaylistBtn.addEventListener('click', async () => { const { db } = await import('./db.js'); const { syncManager } = await import('./firebase/sync.js'); const { showNotification } = await import('./downloads.js'); const playlists = await db.getPlaylists(); if (playlists.length === 0) { showNotification('No playlists yet. Create one first.'); return; } const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); const closeModal = () => { modal.remove(); }; modal.addEventListener('click', async (e) => { if (e.target.classList.contains('modal-overlay') || e.target.classList.contains('cancel-btn')) { closeModal(); return; } const option = e.target.closest('.modal-option'); if (option) { const playlistId = option.dataset.id; const playlistName = option.textContent; try { let addedCount = 0; for (const track of currentQueue) { const playlist = await db.addTrackToPlaylist(playlistId, track); addedCount++; } const updatedPlaylist = await db.getPlaylist(playlistId); syncManager.syncUserPlaylist(updatedPlaylist, 'update'); showNotification(`Added ${addedCount} tracks to playlist: ${playlistName}`); } catch (error) { console.error('Failed to add tracks to playlist:', error); showNotification('Failed to add tracks to playlist'); } closeModal(); } }); }); } const clearBtn = container.querySelector('#clear-queue-btn'); if (clearBtn) { clearBtn.addEventListener('click', () => { player.clearQueue(); refreshQueuePanel(); }); } }; const renderQueueContent = (container) => { const currentQueue = player.getCurrentQueue(); if (currentQueue.length === 0) { container.innerHTML = '
Queue is empty.
'; return; } const html = currentQueue .map((track, index) => { const isPlaying = index === player.currentQueueIndex; const trackTitle = getTrackTitle(track); const trackArtists = getTrackArtists(track, { fallback: 'Unknown' }); return `
${escapeHtml(trackTitle)}
${escapeHtml(trackArtists)}
${formatTime(track.duration)}
`; }) .join(''); container.innerHTML = html; container.querySelectorAll('.queue-track-item').forEach(async (item) => { const index = parseInt(item.dataset.queueIndex); const track = player.getCurrentQueue()[index]; // Update like button state const likeBtn = item.querySelector('.queue-like-btn'); if (likeBtn && track) { const { db } = await import('./db.js'); const isLiked = await db.isFavorite('track', track.id); likeBtn.classList.toggle('active', isLiked); likeBtn.innerHTML = isLiked ? SVG_HEART.replace('class="heart-icon"', 'class="heart-icon filled"') : SVG_HEART; } item.addEventListener('click', async (e) => { const removeBtn = e.target.closest('.queue-remove-btn'); if (removeBtn) { e.stopPropagation(); player.removeFromQueue(index); refreshQueuePanel(); return; } const likeBtn = e.target.closest('.queue-like-btn'); if (likeBtn && likeBtn.dataset.action === 'toggle-like') { e.stopPropagation(); const track = player.getCurrentQueue()[index]; if (track) { const { db } = await import('./db.js'); const { syncManager } = await import('./firebase/sync.js'); const { showNotification } = await import('./downloads.js'); const added = await db.toggleFavorite('track', track); syncManager.syncLibraryItem('track', track, added); // Update button state likeBtn.classList.toggle('active', added); likeBtn.innerHTML = added ? SVG_HEART.replace('class="heart-icon"', 'class="heart-icon filled"') : SVG_HEART; showNotification( added ? `Added to Liked: ${track.title}` : `Removed from Liked: ${track.title}` ); } return; } player.playAtIndex(index); refreshQueuePanel(); }); item.addEventListener('contextmenu', async (e) => { e.preventDefault(); const contextMenu = document.getElementById('context-menu'); if (contextMenu) { const track = player.getCurrentQueue()[index]; if (track) { const { db } = await import('./db.js'); const isLiked = await db.isFavorite('track', track.id); const likeItem = contextMenu.querySelector('li[data-action="toggle-like"]'); if (likeItem) { likeItem.textContent = isLiked ? 'Unlike' : 'Like'; } const trackMixItem = contextMenu.querySelector('li[data-action="track-mix"]'); if (trackMixItem) { const hasMix = track.mixes && track.mixes.TRACK_MIX; trackMixItem.style.display = hasMix ? 'block' : 'none'; } const rect = item.getBoundingClientRect(); const menuWidth = 150; const menuHeight = 200; let left = e.clientX; let top = e.clientY; if (left + menuWidth > window.innerWidth) { left = window.innerWidth - menuWidth - 10; } if (top + menuHeight > window.innerHeight) { top = e.clientY - menuHeight - 10; } contextMenu.style.left = `${left}px`; contextMenu.style.top = `${top}px`; contextMenu.style.display = 'block'; contextMenu._contextTrack = track; } } }); item.addEventListener('dragstart', (e) => { draggedQueueIndex = index; item.style.opacity = '0.5'; }); item.addEventListener('dragend', () => { item.style.opacity = '1'; }); item.addEventListener('dragover', (e) => { e.preventDefault(); }); item.addEventListener('drop', (e) => { e.preventDefault(); if (draggedQueueIndex !== null && draggedQueueIndex !== index) { player.moveInQueue(draggedQueueIndex, index); refreshQueuePanel(); } }); }); }; const refreshQueuePanel = () => { sidePanelManager.refresh('queue', renderQueueControls, renderQueueContent); }; const openQueuePanel = () => { sidePanelManager.open('queue', 'Queue', renderQueueControls, renderQueueContent); }; queueBtn.addEventListener('click', openQueuePanel); // Expose renderQueue for external updates (e.g. shuffle, add to queue) window.renderQueueFunction = () => { if (sidePanelManager.isActive('queue')) { refreshQueuePanel(); } }; // Search and Library tabs document.querySelectorAll('.search-tab').forEach((tab) => { tab.addEventListener('click', () => { const page = tab.closest('.page'); if (!page) return; page.querySelectorAll('.search-tab').forEach((t) => t.classList.remove('active')); page.querySelectorAll('.search-tab-content').forEach((c) => c.classList.remove('active')); tab.classList.add('active'); const prefix = page.id === 'page-library' ? 'library-tab-' : 'search-tab-'; const contentId = `${prefix}${tab.dataset.tab}`; document.getElementById(contentId)?.classList.add('active'); }); }); }