Skip to content

Fix : Properly clear App Cache and IndexedDB on Resetfix reset btn to clear cache #1136#1413

Open
infinityanant wants to merge 3 commits intokiwix:mainfrom
infinityanant:fix-reset-btn
Open

Fix : Properly clear App Cache and IndexedDB on Resetfix reset btn to clear cache #1136#1413
infinityanant wants to merge 3 commits intokiwix:mainfrom
infinityanant:fix-reset-btn

Conversation

@infinityanant
Copy link
Copy Markdown
Contributor

Problem

The reset function was not properly clearing the app cache and IndexedDB because:

  1. Cache deletion failed silently: The code relied on getCacheNames() which queries the Service Worker. When no Service Worker controller was available, it returned null and skipped cache deletion entirely.

  2. Race condition: The reset function triggered app reload before async cleanup operations (IndexedDB and Cache API deletions) completed, leaving data behind.

Solution

  • Changed cache deletion to use caches.keys() directly instead of querying the Service Worker, ensuring caches are always accessible
  • Wrapped IndexedDB and Cache API deletions in Promises
  • Used Promise.all() to wait for all async cleanup operations to complete before reloading the app
  • This ensures both kiwixjs-appCache and kiwixjs-assetsCache are properly deleted, along with IndexedDB databases

Testing

Verified that after clicking "Reset App":

  • Cache storage drops from ~17MB to 0MB
  • IndexedDB databases (collDB) are deleted
  • App reloads only after all cleanup completes

Fixes #1136

@infinityanant
Copy link
Copy Markdown
Contributor Author

infinityanant commented Mar 12, 2026

Hi @Jaifroid, could you please review this PR whenever you have a moment? I'd be happy to implement any changes or improvements you suggest. Thanks!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the “Reset App” behavior so that CacheStorage and IndexedDB are actually cleared before the app reloads, even when no Service Worker controller is available.

Changes:

  • Switch cache deletion to use caches.keys() and delete caches directly via the Cache API.
  • Convert IndexedDB and Cache API deletions into Promises and wait for completion with Promise.all() before reloading.
  • Minor formatting adjustments in settingsStore.js (function spacing/indentation).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +230 to +236
cachePromise = caches.keys().then(function (cacheNames) {
if (cacheNames && cacheNames.length > 0) {
var deletePromises = cacheNames.map(function (cacheName) {
console.debug('Deleting cache: ' + cacheName);
return caches.delete(cacheName);
});
return Promise.all(deletePromises);
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

caches.keys() returns all CacheStorage entries for the origin, which can include caches unrelated to this app (if multiple apps share the same domain). Consider filtering cacheNames to only delete Kiwix caches (e.g. by known names from the SW or a kiwix prefix) to avoid wiping other apps' caches on the same origin.

Copilot uses AI. Check for mistakes.
Comment on lines +190 to +195
indexedDBPromise = indexedDB.databases().then(function (dbs) {
var deletePromises = dbs.map(function (db) {
console.debug('Deleting ' + db.name + '...');
return new Promise(function (resolve) {
var req = indexedDB.deleteDatabase(db.name);
req.onsuccess = resolve;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the indexedDB.databases() branch, db.name can be null/undefined (per IDBDatabaseInfo). Calling indexedDB.deleteDatabase(db.name) will coerce it to a string like 'null'/'undefined'. Please guard/skip entries without a valid name before attempting deletion.

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +197
return new Promise(function (resolve) {
var req = indexedDB.deleteDatabase(db.name);
req.onsuccess = resolve;
req.onerror = resolve; // Resolve even on error
});
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IndexedDB delete promise resolves on onsuccess/onerror but does not handle onblocked. If any DB connection is still open, deleteDatabase may fire onblocked and never reach success/error, causing the reset to hang and never reload. Please handle onblocked (and consider logging req.error on onerror) so the overall reset can complete deterministically.

Copilot uses AI. Check for mistakes.
deleteRequest.onerror = function (ev) {
console.error('Error deleting ' + dbName + '...', ev);
resolve(); // Resolve even on error
};
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above for the Firefox branch: the deleteDatabase request can become blocked by open connections and never fire onsuccess/onerror, which would prevent Promise.all(...) from resolving and stop the reload. Please attach an onblocked handler (and optionally surface/log blocked state) to ensure reset completes.

Suggested change
};
};
deleteRequest.onblocked = function (ev) {
console.warn('Deletion of ' + dbName + ' is blocked, proceeding with reset...', ev);
resolve(); // Resolve even when blocked to avoid hanging the reset
};

Copilot uses AI. Check for mistakes.
Comment on lines +261 to +262


Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple extra blank lines before getCacheNames that were introduced in this change. Please remove the redundant empty lines to keep formatting consistent.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +95
// function reset(object) {
// // 1. Clear any cookie entries
// if (!object || object === 'cookie') {
// var regexpCookieKeys = /(?:^|;)\s*([^=]+)=([^;]*)/ig;
// var currentCookie = document.cookie;
// var foundCrumb = false;
// var cookieCrumb = regexpCookieKeys.exec(currentCookie);
// while (cookieCrumb !== null) {
// // DEV: Note that we don't use the keyPrefix in legacy cookie support
// foundCrumb = true;
// // This expiry date will cause the browser to delete the cookie crumb on next page refresh
// document.cookie = cookieCrumb[1] + '=;expires=Thu, 21 Sep 1979 00:00:01 UTC;';
// cookieCrumb = regexpCookieKeys.exec(currentCookie);
// }
// if (foundCrumb) console.debug('All cookie keys were expired...');
// }

// // 2. Clear any localStorage settings
// if (!object || object === 'localStorage') {
// if (params.storeType === 'local_storage') {
// localStorage.clear();
// console.debug('All Local Storage settings were deleted...');
// }
// }

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old reset implementation is left in the file as a large commented-out block. This adds noise and makes future maintenance harder; git history already preserves the previous version. Please remove the commented-out function block rather than keeping it inline.

Suggested change
// function reset(object) {
// // 1. Clear any cookie entries
// if (!object || object === 'cookie') {
// var regexpCookieKeys = /(?:^|;)\s*([^=]+)=([^;]*)/ig;
// var currentCookie = document.cookie;
// var foundCrumb = false;
// var cookieCrumb = regexpCookieKeys.exec(currentCookie);
// while (cookieCrumb !== null) {
// // DEV: Note that we don't use the keyPrefix in legacy cookie support
// foundCrumb = true;
// // This expiry date will cause the browser to delete the cookie crumb on next page refresh
// document.cookie = cookieCrumb[1] + '=;expires=Thu, 21 Sep 1979 00:00:01 UTC;';
// cookieCrumb = regexpCookieKeys.exec(currentCookie);
// }
// if (foundCrumb) console.debug('All cookie keys were expired...');
// }
// // 2. Clear any localStorage settings
// if (!object || object === 'localStorage') {
// if (params.storeType === 'local_storage') {
// localStorage.clear();
// console.debug('All Local Storage settings were deleted...');
// }
// }

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resetting the app no longer empties the app cache

3 participants