From 71ff5cb5c3e7d59d33573757b49d7ec679f56fd3 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:38:32 -0700 Subject: [PATCH] Retire artists.js and inline Artists page, ship unification at 2.40 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part D + E of the deferred cleanup + the final version bump that publishes the whole Search/Artists unification project. Deletions: - webui/static/artists.js (1903 lines) — removed entirely. The 2 remaining externally-referenced helpers (lazyLoadArtistImages + showCompletionError) moved into shared-helpers.js first. - webui/index.html — 140-line #artists-page HTML block and the - diff --git a/webui/static/artists.js b/webui/static/artists.js deleted file mode 100644 index 7d70c875..00000000 --- a/webui/static/artists.js +++ /dev/null @@ -1,1903 +0,0 @@ -// ARTISTS PAGE FUNCTIONALITY - ELEGANT SEARCH & DISCOVERY -// ============================================================================ - -/** - * Initialize the artists page when navigated to (only runs once) - */ -function initializeArtistsPage() { - console.log('🎵 Initializing Artists Page (first time)'); - - // Get DOM elements - const searchInput = document.getElementById('artists-search-input'); - const headerSearchInput = document.getElementById('artists-header-search-input'); - const searchStatus = document.getElementById('artists-search-status'); - const backButton = document.getElementById('artists-back-button'); - const detailBackButton = document.getElementById('artist-detail-back-button'); - - // Set up event listeners (only need to do this once) - if (searchInput) { - searchInput.addEventListener('input', handleArtistsSearchInput); - searchInput.addEventListener('keypress', handleArtistsSearchKeypress); - } - - if (headerSearchInput) { - headerSearchInput.addEventListener('input', handleArtistsHeaderSearchInput); - headerSearchInput.addEventListener('keypress', handleArtistsSearchKeypress); - } - - if (backButton) { - backButton.addEventListener('click', () => showArtistsSearchState()); - } - - if (detailBackButton) { - detailBackButton.addEventListener('click', () => { - // If the user searched within the Artists page, back returns to the - // results list so they can pick a different artist. - if (artistsPageState.searchResults && artistsPageState.searchResults.length > 0) { - showArtistsResultsState(); - return; - } - // Otherwise the user reached this detail view from elsewhere (Search, - // Discover, watchlist, etc.). The Artists page is no longer a sidebar - // entry, so there's nothing useful to fall back to here — let the - // browser take them back to wherever they came from, or drop them on - // Search (the go-forward way to find another artist). - if (window.history.length > 1) { - window.history.back(); - } else { - navigateToPage('search'); - } - }); - } - - // Initialize tabs (only need to do this once) - initializeArtistTabs(); - - // Mark as initialized - artistsPageState.isInitialized = true; - - // Restore previous state instead of always resetting to search - restoreArtistsPageState(); - console.log('✅ Artists Page initialized successfully (ready for navigation)'); -} - -/** - * Restore the artists page to its previous state - */ -function restoreArtistsPageState() { - console.log(`🔄 Restoring artists page state: ${artistsPageState.currentView}`); - - switch (artistsPageState.currentView) { - case 'results': - // Restore search results state - if (artistsPageState.searchQuery && artistsPageState.searchResults.length > 0) { - console.log(`📦 Restoring search results for: "${artistsPageState.searchQuery}"`); - - // Restore search input values - const searchInput = document.getElementById('artists-search-input'); - const headerSearchInput = document.getElementById('artists-header-search-input'); - - if (searchInput) searchInput.value = artistsPageState.searchQuery; - if (headerSearchInput) headerSearchInput.value = artistsPageState.searchQuery; - - // Display the cached results - displayArtistsResults(artistsPageState.searchQuery, artistsPageState.searchResults); - } else { - // No valid results state, fall back to search - showArtistsSearchState(); - } - break; - - case 'detail': - // Restore artist detail state - if (artistsPageState.selectedArtist && artistsPageState.artistDiscography) { - console.log(`🎤 Restoring artist detail for: ${artistsPageState.selectedArtist.name}`); - - // First restore search results if they exist - if (artistsPageState.searchQuery && artistsPageState.searchResults.length > 0) { - const searchInput = document.getElementById('artists-search-input'); - const headerSearchInput = document.getElementById('artists-header-search-input'); - - if (searchInput) searchInput.value = artistsPageState.searchQuery; - if (headerSearchInput) headerSearchInput.value = artistsPageState.searchQuery; - } - - // Show artist detail state - showArtistDetailState(); - - // Update artist info in header - updateArtistDetailHeader(artistsPageState.selectedArtist); - - // Display cached discography - if (artistsPageState.artistDiscography.albums || artistsPageState.artistDiscography.singles) { - displayArtistDiscography(artistsPageState.artistDiscography); - // Restore cached completion data instead of re-scanning - restoreCachedCompletionData(artistsPageState.selectedArtist.id); - } - } else { - // No valid detail state, fall back to search or results - if (artistsPageState.searchQuery && artistsPageState.searchResults.length > 0) { - displayArtistsResults(artistsPageState.searchQuery, artistsPageState.searchResults); - } else { - showArtistsSearchState(); - } - } - break; - - default: - case 'search': - // Show search state (but preserve any existing search query) - if (artistsPageState.searchQuery) { - const searchInput = document.getElementById('artists-search-input'); - if (searchInput) searchInput.value = artistsPageState.searchQuery; - } - showArtistsSearchState(); - break; - } -} - -/** - * Handle search input with debouncing - */ -function handleArtistsSearchInput(event) { - const query = event.target.value.trim(); - updateArtistsSearchStatus('searching'); - - // Clear existing timeout - if (artistsSearchTimeout) { - clearTimeout(artistsSearchTimeout); - } - - // Cancel any active search - if (artistsSearchController) { - artistsSearchController.abort(); - } - - if (query === '') { - updateArtistsSearchStatus('default'); - return; - } - - // Set up new debounced search - artistsSearchTimeout = setTimeout(() => { - performArtistsSearch(query); - }, 1000); // 1 second debounce -} - -/** - * Handle header search input (already in results state) - */ -function handleArtistsHeaderSearchInput(event) { - const query = event.target.value.trim(); - - // Update main search input to match - const mainInput = document.getElementById('artists-search-input'); - if (mainInput) { - mainInput.value = query; - } - - // Trigger search with same debouncing logic - handleArtistsSearchInput(event); -} - -/** - * Handle Enter key press in search inputs - */ -function handleArtistsSearchKeypress(event) { - if (event.key === 'Enter') { - event.preventDefault(); - const query = event.target.value.trim(); - - if (query && query !== artistsPageState.searchQuery) { - // Clear timeout and search immediately - if (artistsSearchTimeout) { - clearTimeout(artistsSearchTimeout); - } - performArtistsSearch(query); - } - } -} - -/** - * Perform artist search with API call - */ -async function performArtistsSearch(query) { - console.log(`🔍 Searching for artists: "${query}"`); - - // Check cache first - if (artistsPageState.cache.searches[query]) { - console.log('📦 Using cached search results'); - displayArtistsResults(query, artistsPageState.cache.searches[query]); - return; - } - - // Update status - updateArtistsSearchStatus('searching'); - - // Show loading cards immediately if we're in results view - if (artistsPageState.currentView === 'results') { - showSearchLoadingCards(); - } - - try { - // Set up abort controller - artistsSearchController = new AbortController(); - - const response = await fetch('/api/match/search', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: query, - context: 'artist' - }), - signal: artistsSearchController.signal - }); - - if (!response.ok) { - throw new Error(`Search failed: ${response.status}`); - } - - const data = await response.json(); - console.log(`✅ Found ${data.results?.length || 0} artists`); - - // Transform the results to flatten the nested artist data - const transformedResults = (data.results || []).map(result => { - // Extract artist data from the nested structure - const artist = result.artist || result; - return { - id: artist.id, - name: artist.name, - image_url: artist.image_url, - genres: artist.genres, - popularity: artist.popularity, - confidence: result.confidence || 0 - }; - }); - - console.log('🔧 Transformed results:', transformedResults); - - // Cache the transformed results - artistsPageState.cache.searches[query] = transformedResults; - - // Display results - displayArtistsResults(query, transformedResults); - - } catch (error) { - if (error.name !== 'AbortError') { - console.error('❌ Artist search failed:', error); - - // Provide specific error messages based on the error type - let errorMessage = 'Search failed. Please try again.'; - if (error.message.includes('401') || error.message.includes('authentication')) { - errorMessage = 'Spotify not authenticated. Please check your API settings.'; - } else if (error.message.includes('network') || error.message.includes('fetch')) { - errorMessage = 'Network error. Please check your connection.'; - } else if (error.message.includes('timeout')) { - errorMessage = 'Search timed out. Please try again.'; - } - - updateArtistsSearchStatus('error', errorMessage); - } - } finally { - artistsSearchController = null; - } -} - -/** - * Display artist search results - */ -function displayArtistsResults(query, results) { - console.log(`📊 Displaying ${results.length} artist results`); - - // Update state - artistsPageState.searchQuery = query; - artistsPageState.searchResults = results; - artistsPageState.currentView = 'results'; - - // Update header search input if different - const headerInput = document.getElementById('artists-header-search-input'); - if (headerInput && headerInput.value !== query) { - headerInput.value = query; - } - - // Show results state - showArtistsResultsState(); - - // Populate results - const container = document.getElementById('artists-cards-container'); - if (!container) return; - - if (results.length === 0) { - container.innerHTML = ` -