diff --git a/www/js/app.js b/www/js/app.js
index 0a6e3ce3..c69ad1ee 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -39,6 +39,7 @@ import transformStyles from './lib/transformStyles.js';
import transformZimit from './lib/transformZimit.js';
import kiwixServe from './lib/kiwixServe.js';
import updater from './lib/updater.js';
+import resetApp from './lib/resetApp.js';
// Import stylesheets programmatically
// document.adoptedStyleSheets = [styles, bootstrap];
@@ -1859,7 +1860,7 @@ document.getElementById('manipulateImagesCheck').addEventListener('click', funct
});
['btnReset', 'btnReset2'].forEach(function (id) {
document.getElementById(id).addEventListener('click', function () {
- settingsStore.reset();
+ resetApp.reset();
});
});
document.getElementById('btnRefreshApp').addEventListener('click', function () {
@@ -1872,7 +1873,7 @@ document.getElementById('bypassAppCacheCheck').addEventListener('change', functi
} else {
params.appCache = !this.checked;
settingsStore.setItem('appCache', params.appCache, Infinity);
- settingsStore.reset('cacheAPI');
+ resetApp.reset('cacheAPI');
}
// This will also send any new values to Service Worker
refreshCacheStatus();
diff --git a/www/js/lib/resetApp.js b/www/js/lib/resetApp.js
new file mode 100644
index 00000000..63ae1dcf
--- /dev/null
+++ b/www/js/lib/resetApp.js
@@ -0,0 +1,184 @@
+/**
+ * reset.js : Provide utilities for resetting the app to a fresh state
+ * Copyright 2024 Jaifroid and contributors
+ * License GPL v3:
+ *
+ * This file is part of Kiwix.
+ *
+ * Kiwix is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Kiwix is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Kiwix (file LICENSE-GPLv3.txt). If not, see
WARNING: This will reset the app to a freshly installed state, deleting all app caches,' + + // ' Archives stored in the Private File System,' + + ' and settings! (Archives stored in the OPFS will be preserved.)
Make sure you have an Internet connection' + + ' if this is an offline PWA, because it will be erased and reloaded.
', 'Warning!', true).then(function (confirm) { + if (confirm) performReset(); + else console.debug('User cancelled'); + }); + } +} + +// Gets cache names from Service Worker, as we cannot rely on having them in params.cacheNames +function getCacheNames (callback) { + if (navigator.serviceWorker && navigator.serviceWorker.controller) { + var channel = new MessageChannel(); + channel.port1.onmessage = function (event) { + var names = event.data; + callback(names); + }; + navigator.serviceWorker.controller.postMessage({ + action: 'getCacheNames' + }, [channel.port2]); + } else { + callback(null); + } +} + +// Deregisters all Service Workers and reboots the app +function reloadApp () { + var reboot = function () { + // Temporarily disable the beforeunload event listener + params.interceptBeforeUnload = false; + console.debug('Performing app reload...'); + setTimeout(function () { + window.location.href = location.origin + location.pathname + uriParams + }, 600); + }; + // Blank the querystring, so that parameters are not set on reload + var uriParams = ''; + if (~window.location.href.indexOf(params.PWAServer) && params.referrerExtensionURL) { + // However, if we're in a PWA that was called from local code, then by definition we must remain in SW mode and we need to + // ensure the user still has access to the referrerExtensionURL (so they can get back to local code from the UI) + uriParams = '?allowInternetAccess=truee&contentInjectionMode=serviceworker'; + uriParams += '&referrerExtensionURL=' + encodeURIComponent(params.referrerExtensionURL); + } + if (navigator && navigator.serviceWorker) { + console.debug('Deregistering Service Workers...'); + var cnt = 0; + navigator.serviceWorker.getRegistrations().then(function (registrations) { + if (!registrations.length) { + reboot(); + return; + } + cnt++; + registrations.forEach(function (registration) { + registration.unregister().then(function () { + cnt--; + if (!cnt) { + console.debug('All Service Workers unregistered...'); + reboot(); + } + }); + }); + }).catch(function (err) { + console.error(err); + reboot(); + }); + } else { + console.debug('Performing app reload...'); + reboot(); + } +} + +export default { + reset: reset, + reloadApp: reloadApp, + getCacheNames: getCacheNames +} diff --git a/www/js/lib/settingsStore.js b/www/js/lib/settingsStore.js index 4c88d101..fbfea1aa 100644 --- a/www/js/lib/settingsStore.js +++ b/www/js/lib/settingsStore.js @@ -1,10 +1,8 @@ 'use strict'; -/* global params, assetsCache */ +/* global params */ /* eslint-disable indent */ -import uiUtil from './uiUtil.js'; - var regexpCookieKeysToMigrate = new RegExp([ 'contentInjectionMode', 'lastSelectedArchive', 'lastSelectedArchivePath', 'imageDisplay', 'useMathJax', 'appVersion', @@ -68,157 +66,6 @@ function getBestAvailableStorageAPI () { return type; } -/** - * Performs a full app reset, deleting all caches and settings - * Or, if a parameter is supplied, deletes or disables the object - * @param {String} object Optional name of the object to disable or delete ('cookie', 'localStorage', 'cacheAPI') - */ -function reset (object) { - var performReset = function () { - // 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 (/localStorage/.test(assetsCache.capability)) { - localStorage.clear(); - console.debug('All Local Storage settings were deleted...'); - } - } - - // 3. Clear any IndexedDB entries - if (!object || object === 'indexedDB') { - if (/indexedDB/.test(assetsCache.capability)) { - window.indexedDB.deleteDatabase(params.indexedDB); - console.debug('All IndexedDB entries were deleted...'); - } - } - - // 4. Clear any (remaining) Cache API caches - if (!object || object === 'cacheAPI') { - getCacheNames(function (cacheNames) { - if (cacheNames && !cacheNames.error) { - var cnt = 0; - for (var cacheName in cacheNames) { - cnt++; - caches.delete(cacheNames[cacheName]).then(function () { - cnt--; - if (!cnt) { - // All caches deleted - console.debug('All Cache API caches were deleted...'); - // Reload if user performed full reset or if appCache is needed - if (!object || params.appCache) _reloadApp(); - } - }); - } - } else { - console.debug('No Cache API caches were in use (or we do not have access to the names).'); - // All operations complete, reload if user performed full reset or if appCache is needed - if (!object || params.appCache) _reloadApp(); - } - }); - } - - // 5. Clear any Origin Private File System Archives - // DEV: Method is currently behind a flag, so wait till fully implemented - // if (!object || object === 'OPFS') { - // if (navigator && navigator.storage && 'getDirectory' in navigator.storage) { - // navigator.storage.getDirectory().then(function (handle) { - // handle.remove({ recursive: true }).then(function () { - // console.debug('All OPFS archives were deleted...'); - // }); - // }); - // } - // } - }; - // If no specific object was specified, we are doing a general reset, so ask user for confirmation - if (object) performReset(); - else { - uiUtil.systemAlert('WARNING: This will reset the app to a freshly installed state, deleting all app caches,' + - // ' Archives stored in the Private File System,' + - ' and settings! (Archives stored in the OPFS will be preserved.)
Make sure you have an Internet connection' + - ' if this is an offline PWA, because it will be erased and reloaded.
', 'Warning!', true).then(function (confirm) { - if (confirm) performReset(); - else console.debug('User cancelled'); - }); - } -} - -// Gets cache names from Service Worker, as we cannot rely on having them in params.cacheNames -function getCacheNames (callback) { - if (navigator.serviceWorker && navigator.serviceWorker.controller) { - var channel = new MessageChannel(); - channel.port1.onmessage = function (event) { - var names = event.data; - callback(names); - }; - navigator.serviceWorker.controller.postMessage({ - action: 'getCacheNames' - }, [channel.port2]); - } else { - callback(null); - } -} - -// Deregisters all Service Workers and reboots the app -function _reloadApp () { - var reboot = function () { - // Temporarily disable the beforeunload event listener - params.interceptBeforeUnload = false; - console.debug('Performing app reload...'); - setTimeout(function () { - window.location.href = location.origin + location.pathname + uriParams - }, 600); - }; - // Blank the querystring, so that parameters are not set on reload - var uriParams = ''; - if (~window.location.href.indexOf(params.PWAServer) && params.referrerExtensionURL) { - // However, if we're in a PWA that was called from local code, then by definition we must remain in SW mode and we need to - // ensure the user still has access to the referrerExtensionURL (so they can get back to local code from the UI) - uriParams = '?allowInternetAccess=truee&contentInjectionMode=serviceworker'; - uriParams += '&referrerExtensionURL=' + encodeURIComponent(params.referrerExtensionURL); - } - if (navigator && navigator.serviceWorker) { - console.debug('Deregistering Service Workers...'); - var cnt = 0; - navigator.serviceWorker.getRegistrations().then(function (registrations) { - if (!registrations.length) { - reboot(); - return; - } - cnt++; - registrations.forEach(function (registration) { - registration.unregister().then(function () { - cnt--; - if (!cnt) { - console.debug('All Service Workers unregistered...'); - reboot(); - } - }); - }); - }).catch(function (err) { - console.error(err); - reboot(); - }); - } else { - console.debug('Performing app reload...'); - reboot(); - } -} - var settingsStore = { getItem: function (sKey) { if (!sKey) { @@ -309,7 +156,5 @@ export default { setItem: settingsStore.setItem, removeItem: settingsStore.removeItem, hasItem: settingsStore.hasItem, - getCacheNames: getCacheNames, - reset: reset, getBestAvailableStorageAPI: getBestAvailableStorageAPI };