From ee6c95a92e3f1cdb1fe732803338ea1d3fc04eea Mon Sep 17 00:00:00 2001 From: Broque Thomas Date: Sun, 24 Aug 2025 10:23:16 -0700 Subject: [PATCH] adjust styling --- webui/static/script.js | 105 +++++++++++++++++++++++++++++++++-------- webui/static/style.css | 20 ++++++++ 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/webui/static/script.js b/webui/static/script.js index df0f7ef5..6382caea 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -21,6 +21,7 @@ let currentFilterType = 'all'; let currentFilterFormat = 'all'; let currentSortBy = 'quality_score'; let isSortReversed = false; +let searchAbortController = null; // API endpoints const API = { @@ -1219,12 +1220,25 @@ function initializeSearch() { const searchInput = document.getElementById('downloads-search-input'); const searchButton = document.getElementById('downloads-search-btn'); + // Add this line to get the cancel button + const cancelButton = document.getElementById('downloads-cancel-btn'); + if (searchButton && searchInput) { searchButton.addEventListener('click', performDownloadsSearch); searchInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') performDownloadsSearch(); }); } + + // Add this event listener for the cancel button + if (cancelButton) { + cancelButton.addEventListener('click', () => { + if (searchAbortController) { + searchAbortController.abort(); // This cancels the fetch request + console.log("Search cancelled by user."); + } + }); + } } async function performSearch() { @@ -1673,46 +1687,97 @@ async function clearFinishedDownloads() { } } +// REPLACE the old performDownloadsSearch function with this new one. async function performDownloadsSearch() { const query = document.getElementById('downloads-search-input').value.trim(); if (!query) { showToast('Please enter a search term', 'error'); return; } - + + // --- UI Element References --- + const searchInput = document.getElementById('downloads-search-input'); + const searchButton = document.getElementById('downloads-search-btn'); + const cancelButton = document.getElementById('downloads-cancel-btn'); + const statusText = document.getElementById('search-status-text'); + const spinner = document.querySelector('.spinner-animation'); + const dots = document.querySelector('.dots-animation'); + + // --- Start a new AbortController for this search --- + searchAbortController = new AbortController(); + try { - showLoadingOverlay('Searching...'); - + // --- 1. Update UI to "Searching" State --- + searchInput.disabled = true; + searchButton.disabled = true; + cancelButton.classList.remove('hidden'); + spinner.classList.remove('hidden'); + dots.classList.remove('hidden'); + statusText.textContent = `Searching for '${query}'...`; + displayDownloadsResults([]); // Clear previous results + + // --- 2. Perform the Fetch Request --- const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ query }) + body: JSON.stringify({ query }), + signal: searchAbortController.signal // Link fetch to the AbortController }); - + const data = await response.json(); - + if (data.error) { - showToast(`Search error: ${data.error}`, 'error'); - return; + throw new Error(data.error); } - + const results = data.results || []; allSearchResults = results; resetFilters(); applyFiltersAndSort(); - + + // --- 3. Update UI with Success State --- if (results.length === 0) { + statusText.textContent = `No results found for '${query}'`; showToast('No results found', 'error'); } else { document.getElementById('filters-container').classList.remove('hidden'); + + // Count albums and singles like the GUI app + let totalAlbums = 0; + let totalTracks = 0; + + results.forEach(result => { + if (result.result_type === 'album') { + totalAlbums++; + } else { + totalTracks++; + } + }); + + statusText.textContent = `✨ Found ${results.length} results • ${totalAlbums} albums, ${totalTracks} singles`; showToast(`Found ${results.length} results`, 'success'); } - + } catch (error) { - console.error('Search failed:', error); - showToast('Search failed', 'error'); + // --- 4. Handle Errors, Including Cancellation --- + if (error.name === 'AbortError') { + // This specific error is thrown when the user clicks "Cancel" + statusText.textContent = 'Search was cancelled.'; + showToast('Search cancelled', 'info'); + displayDownloadsResults([]); // Clear any partial results + } else { + console.error('Search failed:', error); + statusText.textContent = `Search failed: ${error.message}`; + showToast('Search failed', 'error'); + } } finally { - hideLoadingOverlay(); + // --- 5. Clean Up UI Regardless of Outcome --- + searchInput.disabled = false; + searchButton.disabled = false; + cancelButton.classList.add('hidden'); + spinner.classList.add('hidden'); + dots.classList.add('hidden'); + searchAbortController = null; // Clear the controller } } @@ -1748,8 +1813,8 @@ function displayDownloadsResults(results) {
- - + +
@@ -1772,7 +1837,7 @@ function displayDownloadsResults(results) {
- +
- - - + + +
`; diff --git a/webui/static/style.css b/webui/static/style.css index 43e58de2..0d167c57 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -3116,3 +3116,23 @@ body { background: linear-gradient(to bottom, #a747fe, #903fdd); transform: scale(1.05); } + +.album-matched-btn { + background: linear-gradient(to bottom, #9333ea, #7c28c0); + border: 2px solid rgba(147, 51, 234, 0.3); + border-radius: 23px; + color: #fff; + padding: 10px 20px; + font-size: 15px; + font-weight: bold; + cursor: pointer; + transition: all 0.2s ease; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.album-matched-btn:hover { + background: linear-gradient(to bottom, #a747fe, #903fdd); + transform: scale(1.05); +}