From 458ca1d7766414052a54cf5ef43b3e9ad0d0025c Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 24 Dec 2025 21:27:32 +0100 Subject: [PATCH] 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 --- data/aniworld.db-shm | Bin 32768 -> 32768 bytes data/aniworld.db-wal | Bin 94792 -> 103032 bytes data/config.json | 2 +- .../config_backup_20251224_210928.json | 24 ----- ...son => config_backup_20251224_212619.json} | 2 +- src/server/web/static/css/styles.css | 8 ++ src/server/web/static/js/app.js | 89 +++++++++++++++--- 7 files changed, 87 insertions(+), 38 deletions(-) delete mode 100644 data/config_backups/config_backup_20251224_210928.json rename data/config_backups/{config_backup_20251224_210530.json => config_backup_20251224_212619.json} (77%) diff --git a/data/aniworld.db-shm b/data/aniworld.db-shm index 6ad606e34045bbeb66d739d278f408718982d1f6..22a2e9505e0b015331d9c66607d4e2d6c881f7d1 100644 GIT binary patch delta 170 zcmZo@U}|V!s+V}A%K!pQK+MR%ARq~(<$zd{*GK2NQ)!LNR_=dyZFR!@qzoh1n><=g zs(PSNU;r}rKN5fnPpnU6jqu=$Fkj3lGP=8sW-nHVKEKK#nP`AMdX7yt=% BGOhps delta 164 zcmZo@U}|V!s+V}A%K!q55G)`Lq~(D4{i0dbW}b#d3zgDTzrBxoeSGJWD;Y}X50a`L sXcQd$M*>j6iS?-)zdUf5#3-=&ildC=#!r8k7}+*{{La1kNv4b#00kI2Pyhe` diff --git a/data/aniworld.db-wal b/data/aniworld.db-wal index 1999346fc28cd68b041d2bef9eb6004a1b1b66ee..8da83782cc5e3071469f9db8f6030e34976453f5 100644 GIT binary patch delta 9988 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 delta 9862 zcma)>c~n$K7Ki&HOWQ7hib^AkwrErk?55Q?2NW0tTuPJkpq(NZjst#{Y2b z9G~>VQ>9K$mdK`sPFuL2G}r-tTKRG?s*+?9dh2xizQ3JH0Hevgr)1)Nb#&^U#vqr=(~O6-qHU2L zYqPkr`)Y^r+>()GStFJ@&NjIc^8Lx0F>j~9lGhI$DwTwVE5cMNE-7UM_il;?s{1fy zCFFjoTOq%enhbf&(g?`gmhOc-Zy5*q$mNNUH!M$ud{`Q`uOMv}k~e;nW)Ii1He))H z=Mpl$g*-76`@c_C7?e-RDuMiU)@;aAv$21=viCvxiWRMh8{=~3kqYc*b7(n=!|PM> zXzMJA{Dr%fdjl!vzFLVLul!RdlpmCr4tagvb;zq$ZGwDaKHm5b@|QyS%=b@19$qVPe;q`@ikUJKwfV`n-1LTv}orc_bJ)(g+p2aw?)fZPm`Jj?=D`EBP zHOX@E*;(bk?)2y(iQJ2lot9e*<9Ojd(H9{cTsjY0b)*z~RlA`D$~%@7LB6jHpLR|g zwHs_?}^N|9susjoTs@MYgpDVBfhgWt%`InU$kQZ-`g}i6;cQ6W_r%5H@ zk&1A06#93&bMmb^sBY8NW01#J1wnqKssi%Z>Wz>;t^Nq|<25)7glucF5=GA9!)bhd zpf)u}lVNn)o=hlCw$^G9epVX;SHUw`D$(f_I+cn)G?zwk+qnd!&ThwYgg5QTLwIoA z=Lo;3`wPOS>Z1{!z=t8cnQuWjqM;pOubt%p8#{Imfs54Bm;hIy-#qh)6!z>SM5bOs`2@tv6n-|UP+ zc;;s)B)D%sJB6fAcR}f-bB##a25l;TR4B*;}9|Ovry%>kE z`Vw@x;nFuqy8d!1!b7gWtWtO-9ZA2w5{mGGFYZ_gJ{NKD=|;`$URzY&Zp{6=D9lt@ zxvhJ>yEhsuZi`w=Yo~KB%iDNk^J6&M?V!FV$9%n zd{zzRk3AC}LQMHF&xI=)iyJ?AJ{UXb>lb)}cYpdA8o2*u0px+Ngr^A0K<@G@;U&oA zk-fsDo5_v63*h+Ueikl~O!>N>g*z*g+r939`ftD94SCZqLJ5YYzs^9yOPR&_V4^=- z!id$u975)*^#3Z-23eM@(X;m^e~joMvdnbKhMeHu;J@!fi;)UG)t08f4sEuS3p)hc z(IVL4gdNR<9o+0G33lM^NqAPVJjlx&s1|B4Ign82Vrqz%lJGKPcKAw4Fbw$gzE*~? zl~E)#GEGM5u){+cg~ATA`dL}-v;C}ia!P-K>6Sm*pM=T@(@~)V=qfVOq#Q`Lc-QJ2 zsT8T;e{iHM*dgDEU{>b`I#Z36pmY@n^Gh31Q=B6hCtW3B^37)qfj7bbe| z6gpY_o`TjQ`L&}d1@UL2DHidi-n0<$>)td6@sKfe6mjRV)PeZJu~dn8%{#OZZYYOw zv<7x)9ET57(}eNliSy*r30Ah;g$X45o?@(`n)mggRnW?lKG;fruCJ9L4*3$izw#p{ z(mG^=sdXaxVn_7)k?^;RInN*c>1!*2;wBE1*^`HylJ~!VyiG}>N87SEzv4fb7F;zA z4-q$ae;N|BzH8ro-andp(WaRhLt7Ak5QBpz?LE4VdiC zjsIu?8IXKSA_;{smi*L(*uPB+X&2J3TSO-i_g+l7h#y=`?*VRdUqW(tYUq+E0Vv=* zlSpeN3_Z+Y=wbQoR@k_=M>#2SvMC~&YNSZ>;|$6|e03%XHC~qMzt5x$B)=kygbEo; zzJE5}I{UM!4e4LA0;dM|91;rPtnn}8&?=;VelGUU&0G@dhb;Z!E3y2gl?1CG{{bs9 BJGlS= diff --git a/data/config.json b/data/config.json index b2e684e..c18efb5 100644 --- a/data/config.json +++ b/data/config.json @@ -17,7 +17,7 @@ "keep_days": 30 }, "other": { - "master_password_hash": "$pbkdf2-sha256$29000$1TrHeM8Z45yTsjZG6B1DKA$w4AoTXhgcGh90quXesvoRqVkH720fYXEu8LI2L4nUFM", + "master_password_hash": "$pbkdf2-sha256$29000$vrf2npNSKqVU6r137t27tw$XkJLRylHf84bQFQtXUKVSY7QsaMV1Z1Wgh41H3girGc", "anime_directory": "/mnt/server/serien/Serien/" }, "version": "1.0.0" diff --git a/data/config_backups/config_backup_20251224_210928.json b/data/config_backups/config_backup_20251224_210928.json deleted file mode 100644 index 5f8ab65..0000000 --- a/data/config_backups/config_backup_20251224_210928.json +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/data/config_backups/config_backup_20251224_210530.json b/data/config_backups/config_backup_20251224_212619.json similarity index 77% rename from data/config_backups/config_backup_20251224_210530.json rename to data/config_backups/config_backup_20251224_212619.json index f3ca4aa..584ba1e 100644 --- a/data/config_backups/config_backup_20251224_210530.json +++ b/data/config_backups/config_backup_20251224_212619.json @@ -17,7 +17,7 @@ "keep_days": 30 }, "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/" }, "version": "1.0.0" diff --git a/src/server/web/static/css/styles.css b/src/server/web/static/css/styles.css index b0b32d5..a6e6812 100644 --- a/src/server/web/static/css/styles.css +++ b/src/server/web/static/css/styles.css @@ -1500,6 +1500,10 @@ body { } /* Rescan icon specific styling */ +#rescan-status { + cursor: pointer; +} + #rescan-status i { color: var(--color-text-disabled); /* Gray when idle */ @@ -1511,6 +1515,10 @@ body { animation: iconPulse 2s infinite; } +#rescan-status.running { + cursor: pointer; +} + /* Status text removed - using tooltips only */ .status-dot { diff --git a/src/server/web/static/js/app.js b/src/server/web/static/js/app.js index 1486870..f3748c5 100644 --- a/src/server/web/static/js/app.js +++ b/src/server/web/static/js/app.js @@ -417,6 +417,11 @@ class AniWorldApp { this.rescanSeries(); }); + // Click on rescan status indicator to reopen scan overlay + document.getElementById('rescan-status').addEventListener('click', () => { + this.reopenScanOverlay(); + }); + // Configuration modal document.getElementById('config-btn').addEventListener('click', () => { this.showConfigModal(); @@ -1016,33 +1021,39 @@ class AniWorldApp { async rescanSeries() { 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', { method: 'POST' }); - if (!response) return; + if (!response) { + this.removeScanProgressOverlay(); + this.updateProcessStatus('rescan', false); + return; + } const data = await response.json(); // Debug logging console.log('Rescan response:', data); console.log('Success value:', data.success, 'Type:', typeof data.success); - if (data.success === true) { - const seriesCount = data.series_count || 0; - this.showToast( - `Rescan complete! Found ${seriesCount} series with missing episodes.`, - 'success' - ); - - // Reload the series list to show the updated data - await this.loadSeries(); - } else { + // Note: The scan progress will be updated via WebSocket events + // The overlay will be closed when scan_completed is received + if (data.success !== true) { + this.removeScanProgressOverlay(); + this.updateProcessStatus('rescan', false); this.showToast(`Rescan error: ${data.message}`, 'error'); } } catch (error) { console.error('Rescan error:', error); + this.removeScanProgressOverlay(); + this.updateProcessStatus('rescan', false); this.showToast('Failed to start rescan', 'error'); } } @@ -1090,6 +1101,9 @@ class AniWorldApp { // Store total items for progress calculation this.scanTotalItems = data?.total_items || 0; + + // Store last scan data for reopening + this._lastScanData = data; // Create overlay element const overlay = document.createElement('div'); @@ -1136,6 +1150,14 @@ class AniWorldApp { 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 requestAnimationFrame(() => { 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) * and show the progress overlay if so