Improve scan overlay UX

- Show overlay immediately when rescan is clicked (before API response)
- Add click-outside-to-close on overlay background
- Add click on rescan-status indicator to reopen overlay
- Add cursor pointer to rescan-status for clickability feedback
- All 1024 tests passing
This commit is contained in:
Lukas 2025-12-24 21:27:32 +01:00
parent b6d44ca7d8
commit 458ca1d776
7 changed files with 87 additions and 38 deletions

Binary file not shown.

Binary file not shown.

View File

@ -17,7 +17,7 @@
"keep_days": 30 "keep_days": 30
}, },
"other": { "other": {
"master_password_hash": "$pbkdf2-sha256$29000$1TrHeM8Z45yTsjZG6B1DKA$w4AoTXhgcGh90quXesvoRqVkH720fYXEu8LI2L4nUFM", "master_password_hash": "$pbkdf2-sha256$29000$vrf2npNSKqVU6r137t27tw$XkJLRylHf84bQFQtXUKVSY7QsaMV1Z1Wgh41H3girGc",
"anime_directory": "/mnt/server/serien/Serien/" "anime_directory": "/mnt/server/serien/Serien/"
}, },
"version": "1.0.0" "version": "1.0.0"

View File

@ -1,24 +0,0 @@
{
"name": "Aniworld",
"data_dir": "data",
"scheduler": {
"enabled": true,
"interval_minutes": 60
},
"logging": {
"level": "INFO",
"file": null,
"max_bytes": null,
"backup_count": 3
},
"backup": {
"enabled": false,
"path": "data/backups",
"keep_days": 30
},
"other": {
"master_password_hash": "$pbkdf2-sha256$29000$tTYmJCSEUArhnLN2TmktpQ$mhMKshEetPJfjRqYYUUvOGNRxcnIMNIto73IRKw4hPM",
"anime_directory": "/mnt/server/serien/Serien/"
},
"version": "1.0.0"
}

View File

@ -17,7 +17,7 @@
"keep_days": 30 "keep_days": 30
}, },
"other": { "other": {
"master_password_hash": "$pbkdf2-sha256$29000$GKP0fo.RspaScm5trRWiFA$cEeWdNCea5O7PAF21LyHVK.Xxj8sw3/9bbEdapRbCrw", "master_password_hash": "$pbkdf2-sha256$29000$tpaSEqI0BmCMsbb2HgOA0A$zF52br5qvqyA2ciQlDt7.Cdny2d2qaq1BPqNEntoMeY",
"anime_directory": "/mnt/server/serien/Serien/" "anime_directory": "/mnt/server/serien/Serien/"
}, },
"version": "1.0.0" "version": "1.0.0"

View File

@ -1500,6 +1500,10 @@ body {
} }
/* Rescan icon specific styling */ /* Rescan icon specific styling */
#rescan-status {
cursor: pointer;
}
#rescan-status i { #rescan-status i {
color: var(--color-text-disabled); color: var(--color-text-disabled);
/* Gray when idle */ /* Gray when idle */
@ -1511,6 +1515,10 @@ body {
animation: iconPulse 2s infinite; animation: iconPulse 2s infinite;
} }
#rescan-status.running {
cursor: pointer;
}
/* Status text removed - using tooltips only */ /* Status text removed - using tooltips only */
.status-dot { .status-dot {

View File

@ -417,6 +417,11 @@ class AniWorldApp {
this.rescanSeries(); this.rescanSeries();
}); });
// Click on rescan status indicator to reopen scan overlay
document.getElementById('rescan-status').addEventListener('click', () => {
this.reopenScanOverlay();
});
// Configuration modal // Configuration modal
document.getElementById('config-btn').addEventListener('click', () => { document.getElementById('config-btn').addEventListener('click', () => {
this.showConfigModal(); this.showConfigModal();
@ -1016,33 +1021,39 @@ class AniWorldApp {
async rescanSeries() { async rescanSeries() {
try { try {
this.showToast('Scanning directory...', 'info'); // Show the overlay immediately before making the API call
this.showScanProgressOverlay({
directory: 'Starting scan...',
total_items: 0
});
this.updateProcessStatus('rescan', true);
const response = await this.makeAuthenticatedRequest('/api/anime/rescan', { const response = await this.makeAuthenticatedRequest('/api/anime/rescan', {
method: 'POST' method: 'POST'
}); });
if (!response) return; if (!response) {
this.removeScanProgressOverlay();
this.updateProcessStatus('rescan', false);
return;
}
const data = await response.json(); const data = await response.json();
// Debug logging // Debug logging
console.log('Rescan response:', data); console.log('Rescan response:', data);
console.log('Success value:', data.success, 'Type:', typeof data.success); console.log('Success value:', data.success, 'Type:', typeof data.success);
if (data.success === true) { // Note: The scan progress will be updated via WebSocket events
const seriesCount = data.series_count || 0; // The overlay will be closed when scan_completed is received
this.showToast( if (data.success !== true) {
`Rescan complete! Found ${seriesCount} series with missing episodes.`, this.removeScanProgressOverlay();
'success' this.updateProcessStatus('rescan', false);
);
// Reload the series list to show the updated data
await this.loadSeries();
} else {
this.showToast(`Rescan error: ${data.message}`, 'error'); this.showToast(`Rescan error: ${data.message}`, 'error');
} }
} catch (error) { } catch (error) {
console.error('Rescan error:', error); console.error('Rescan error:', error);
this.removeScanProgressOverlay();
this.updateProcessStatus('rescan', false);
this.showToast('Failed to start rescan', 'error'); this.showToast('Failed to start rescan', 'error');
} }
} }
@ -1091,6 +1102,9 @@ class AniWorldApp {
// Store total items for progress calculation // Store total items for progress calculation
this.scanTotalItems = data?.total_items || 0; this.scanTotalItems = data?.total_items || 0;
// Store last scan data for reopening
this._lastScanData = data;
// Create overlay element // Create overlay element
const overlay = document.createElement('div'); const overlay = document.createElement('div');
overlay.id = 'scan-progress-overlay'; overlay.id = 'scan-progress-overlay';
@ -1136,6 +1150,14 @@ class AniWorldApp {
document.body.appendChild(overlay); document.body.appendChild(overlay);
// Add click-outside-to-close handler
overlay.addEventListener('click', (e) => {
// Only close if clicking the overlay background, not the container
if (e.target === overlay) {
this.removeScanProgressOverlay();
}
});
// Trigger animation by adding visible class after a brief delay // Trigger animation by adding visible class after a brief delay
requestAnimationFrame(() => { requestAnimationFrame(() => {
overlay.classList.add('visible'); overlay.classList.add('visible');
@ -1281,6 +1303,49 @@ class AniWorldApp {
} }
} }
/**
* Reopen the scan progress overlay if a scan is in progress
* Called when user clicks on the rescan status indicator
*/
async reopenScanOverlay() {
// Check if overlay already exists
const existingOverlay = document.getElementById('scan-progress-overlay');
if (existingOverlay) {
// Overlay is already open, do nothing
return;
}
// Check if scan is running via API
try {
const response = await this.makeAuthenticatedRequest('/api/anime/scan/status');
if (!response || !response.ok) {
console.log('Could not fetch scan status');
return;
}
const data = await response.json();
console.log('Scan status for reopen:', data);
if (data.is_scanning) {
// A scan is in progress, show the overlay
this.showScanProgressOverlay({
directory: data.directory,
total_items: data.total_items
});
// Update with current progress
this.updateScanProgressOverlay({
directories_scanned: data.directories_scanned,
files_found: data.directories_scanned,
current_directory: data.current_directory,
total_items: data.total_items
});
}
} catch (error) {
console.error('Error checking scan status for reopen:', error);
}
}
/** /**
* Check if a scan is currently in progress (useful after page reload) * Check if a scan is currently in progress (useful after page reload)
* and show the progress overlay if so * and show the progress overlay if so