diff --git a/eslint.config.mjs b/eslint.config.mjs index 59a1177e3..13b247a3f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -53,6 +53,7 @@ const jsConfig = defineConfig([ generateForever: 'readonly', showContributors: 'readonly', opts: 'writable', + monitorOption: 'readonly', sortUIElements: 'readonly', all_gallery_buttons: 'readonly', selected_gallery_button: 'readonly', @@ -98,6 +99,8 @@ const jsConfig = defineConfig([ idbAdd: 'readonly', idbCount: 'readonly', idbFolderCleanup: 'readonly', + idbClearAll: 'readonly', + idbIsReady: 'readonly', initChangelog: 'readonly', sendNotification: 'readonly', monitorConnection: 'readonly', diff --git a/javascript/gallery.js b/javascript/gallery.js index 54131386f..5791990c3 100644 --- a/javascript/gallery.js +++ b/javascript/gallery.js @@ -26,13 +26,6 @@ const el = { const SUPPORTED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'tiff', 'jp2', 'jxl', 'gif', 'mp4', 'mkv', 'avi', 'mjpeg', 'mpg', 'avr']; -function resetController(reason) { - maintenanceController.abort(reason); - const controller = new AbortController(); - maintenanceController = controller; - return controller; -} - function getVisibleGalleryFiles() { if (!el.files) return []; return Array.from(el.files.children).filter((node) => node.name && node.offsetParent); @@ -267,6 +260,14 @@ class SimpleFunctionQueue { this.#queue = []; } + static abortLogger(identifier, result) { + if (typeof result === 'string' || (result instanceof DOMException && result.name === 'AbortError')) { + log(identifier, result?.message || result); + } else { + error(identifier, result.message); + } + } + /** * @param {{ * signal: AbortSignal, @@ -928,9 +929,10 @@ async function gallerySort(btn) { /** * Generate and display the overlay to announce cleanup is in progress. * @param {number} count - Number of entries being cleaned up + * @param {boolean} all - Indicate that all thumbnails are being cleared * @returns {ClearMsgCallback} */ -function showCleaningMsg(count) { +function showCleaningMsg(count, all = false) { // Rendering performance isn't a priority since this doesn't run often const parent = el.folders.parentElement; const cleaningOverlay = document.createElement('div'); @@ -945,7 +947,7 @@ function showCleaningMsg(count) { msgText.style.cssText = 'font-size: 1.2em'; msgInfo.style.cssText = 'font-size: 0.9em; text-align: center;'; msgText.innerText = 'Thumbnail cleanup...'; - msgInfo.innerText = `Found ${count} old entries`; + msgInfo.innerText = all ? 'Clearing all entries' : `Found ${count} old entries`; anim.classList.add('idbBusyAnim'); msgDiv.append(msgText, msgInfo); @@ -954,7 +956,7 @@ function showCleaningMsg(count) { return () => { cleaningOverlay.remove(); }; } -const maintenanceQueue = new SimpleFunctionQueue('Maintenance'); +const maintenanceQueue = new SimpleFunctionQueue('Gallery Maintenance'); /** * Handles calling the cleanup function for the thumbnail cache @@ -998,31 +1000,65 @@ async function thumbCacheCleanup(folder, imgCount, controller, force = false) { return; } const cb_clearMsg = showCleaningMsg(cleanupCount); - const tRun = Date.now(); // Doesn't need high resolution await idbFolderCleanup(staticGalleryHashes, folder, controller.signal) .then((delcount) => { const t1 = performance.now(); log(`Thumbnail DB cleanup: folder=${folder} kept=${staticGalleryHashes.size} deleted=${delcount} time=${Math.floor(t1 - t0)}ms`); + currentGalleryFolder = null; + el.clearCacheFolder.innerText = ''; + updateStatusWithSort('Thumbnail cache cleared'); + }) + .catch((e) => { + SimpleFunctionQueue.abortLogger('Thumbnail DB cleanup:', e); + }) + .finally(async () => { + await new Promise((resolve) => { setTimeout(resolve, 1000); }); + cb_clearMsg(); + }); + }, + }); + } +} + +function folderCleanupRunner(evt) { evt.preventDefault(); evt.stopPropagation(); if (!currentGalleryFolder) return; @@ -1030,10 +1066,7 @@ function clearGalleryFolderCache(evt) { setTimeout(() => { el.clearCacheFolder.style.color = 'var(--color-blue)'; }, 1000); - const controller = resetController('Clearing thumbnails'); - galleryHashes.clear(); - galleryProgressBar.clear(); - resetGallerySelection(); + const controller = resetGalleryState('Clearing folder thumbnails cache'); el.files.innerHTML = ''; thumbCacheCleanup(currentGalleryFolder, 0, controller, true); } @@ -1045,9 +1078,9 @@ function addCacheClearLabel() { // Don't use async div.style.marginBlock = '0.75rem'; const span = document.createElement('span'); - span.style.cssText = 'font-weight:bold; text-decoration:underline; cursor:pointer; color:var(--color-blue); user-select: none;'; + span.style.cssText = 'font-weight: bold; text-decoration: underline; cursor: pointer; color: var(--color-blue); user-select: none;'; span.innerText = '