From f85564a2deb8a6150350f4a783830d8f8d1211b9 Mon Sep 17 00:00:00 2001
From: Broque Thomas <26755000+Nezreka@users.noreply.github.com>
Date: Thu, 23 Apr 2026 13:13:26 -0700
Subject: [PATCH] Move enhancedSearchFetch, SOURCE_LABELS, renderCompactSection
to shared-helpers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These three utilities lived inside search.js — the fetch helper at module
scope, and SOURCE_LABELS plus renderCompactSection as closures inside
initializeSearchModeToggle. The global search widget in downloads.js
already depends on enhancedSearchFetch via global scope and re-implements
the rendering inline.
Hoist all three to shared-helpers.js so both surfaces share the same
implementations. No behavior change — this is the refactor step that
precedes the source-picker redesign.
Also adds a 'soulseek' entry to SOURCE_LABELS for the upcoming icon row.
---
webui/static/search.js | 142 ++-----------------------------
webui/static/shared-helpers.js | 149 +++++++++++++++++++++++++++++++++
2 files changed, 154 insertions(+), 137 deletions(-)
diff --git a/webui/static/search.js b/webui/static/search.js
index 0cae5787..42f9b45a 100644
--- a/webui/static/search.js
+++ b/webui/static/search.js
@@ -1,21 +1,8 @@
// SEARCH FUNCTIONALITY
// ===============================
-
-// Shared enhanced-search fetch used by the Search page and the global widget.
-// Pass source to restrict results to a single metadata provider; omit or pass
-// null/'auto' to let the backend fan out across all configured sources.
-async function enhancedSearchFetch(query, { source = null, signal = null } = {}) {
- const body = { query };
- if (source && source !== 'auto') body.source = source;
- const res = await fetch('/api/enhanced-search', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(body),
- signal: signal || undefined,
- });
- if (!res.ok) throw new Error(`Enhanced search failed: ${res.status}`);
- return res.json();
-}
+// `enhancedSearchFetch`, `SOURCE_LABELS`, and `renderCompactSection` live in
+// shared-helpers.js so the Search page and the global widget share the same
+// implementations.
function initializeSearch() {
// --- FIX: Corrected the element IDs to match the HTML ---
@@ -107,15 +94,7 @@ function initializeSearchModeToggle() {
let _activeSearchSource = null; // Currently displayed source tab
let _altSourceController = null; // AbortController for alternate source fetches
- const SOURCE_LABELS = {
- spotify: { text: 'Spotify', tabClass: 'enh-tab-spotify', badgeClass: 'enh-badge-spotify' },
- itunes: { text: 'Apple Music', tabClass: 'enh-tab-itunes', badgeClass: 'enh-badge-itunes' },
- deezer: { text: 'Deezer', tabClass: 'enh-tab-deezer', badgeClass: 'enh-badge-deezer' },
- discogs: { text: 'Discogs', tabClass: 'enh-tab-discogs', badgeClass: 'enh-badge-discogs' },
- hydrabase: { text: 'Hydrabase', tabClass: 'enh-tab-hydrabase', badgeClass: 'enh-badge-hydrabase' },
- youtube_videos: { text: 'Music Videos', tabClass: 'enh-tab-youtube', badgeClass: 'enh-badge-youtube' },
- musicbrainz: { text: 'MusicBrainz', tabClass: 'enh-tab-musicbrainz', badgeClass: 'enh-badge-musicbrainz' },
- };
+ // SOURCE_LABELS now lives in shared-helpers.js.
// Live search with debouncing
if (enhancedInput) {
@@ -808,118 +787,7 @@ function initializeSearchModeToggle() {
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
- function renderCompactSection(sectionId, listId, countId, items, mapItem) {
- const section = document.getElementById(sectionId);
- const list = document.getElementById(listId);
- const count = document.getElementById(countId);
-
- if (!list) return;
-
- list.innerHTML = '';
-
- if (!items || items.length === 0) {
- section.classList.add('hidden');
- return;
- }
-
- section.classList.remove('hidden');
- count.textContent = items.length;
-
- // Determine type based on section ID
- const isArtist = sectionId.includes('artists');
- const isAlbum = sectionId.includes('albums') || sectionId.includes('singles');
- const isTrack = sectionId.includes('tracks');
-
- // Add appropriate grid class to list
- if (isArtist) {
- list.classList.add('enh-artists-grid');
- } else if (isAlbum) {
- list.classList.add('enh-albums-grid');
- } else if (isTrack) {
- list.classList.add('enh-tracks-list');
- }
-
- items.forEach(item => {
- const config = mapItem(item);
- const elem = document.createElement('div');
-
- // Add appropriate card class
- if (isArtist) {
- elem.className = 'enh-compact-item artist-card';
- // Add data attributes for lazy loading
- if (item.id) {
- elem.dataset.artistId = item.id;
- elem.dataset.needsImage = config.image ? 'false' : 'true';
- }
- } else if (isAlbum) {
- elem.className = 'enh-compact-item album-card';
- } else if (isTrack) {
- elem.className = 'enh-compact-item track-item';
- }
-
- // Build image HTML with type-specific classes
- let imageClass = 'enh-item-image';
- let placeholderClass = 'enh-item-image-placeholder';
-
- if (isArtist) {
- imageClass += ' artist-image';
- placeholderClass += ' artist-placeholder';
- } else if (isAlbum) {
- imageClass += ' album-cover';
- placeholderClass += ' album-placeholder';
- } else if (isTrack) {
- imageClass += ' track-cover';
- placeholderClass += ' track-placeholder';
- }
-
- const imageHtml = config.image
- ? ``
- : `