mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-08-03 19:38:36 -04:00
Separate resetApp functions
This commit is contained in:
parent
b40bec536f
commit
462d7fce78
@ -39,6 +39,7 @@ import transformStyles from './lib/transformStyles.js';
|
|||||||
import transformZimit from './lib/transformZimit.js';
|
import transformZimit from './lib/transformZimit.js';
|
||||||
import kiwixServe from './lib/kiwixServe.js';
|
import kiwixServe from './lib/kiwixServe.js';
|
||||||
import updater from './lib/updater.js';
|
import updater from './lib/updater.js';
|
||||||
|
import resetApp from './lib/resetApp.js';
|
||||||
|
|
||||||
// Import stylesheets programmatically
|
// Import stylesheets programmatically
|
||||||
// document.adoptedStyleSheets = [styles, bootstrap];
|
// document.adoptedStyleSheets = [styles, bootstrap];
|
||||||
@ -1859,7 +1860,7 @@ document.getElementById('manipulateImagesCheck').addEventListener('click', funct
|
|||||||
});
|
});
|
||||||
['btnReset', 'btnReset2'].forEach(function (id) {
|
['btnReset', 'btnReset2'].forEach(function (id) {
|
||||||
document.getElementById(id).addEventListener('click', function () {
|
document.getElementById(id).addEventListener('click', function () {
|
||||||
settingsStore.reset();
|
resetApp.reset();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.getElementById('btnRefreshApp').addEventListener('click', function () {
|
document.getElementById('btnRefreshApp').addEventListener('click', function () {
|
||||||
@ -1872,7 +1873,7 @@ document.getElementById('bypassAppCacheCheck').addEventListener('change', functi
|
|||||||
} else {
|
} else {
|
||||||
params.appCache = !this.checked;
|
params.appCache = !this.checked;
|
||||||
settingsStore.setItem('appCache', params.appCache, Infinity);
|
settingsStore.setItem('appCache', params.appCache, Infinity);
|
||||||
settingsStore.reset('cacheAPI');
|
resetApp.reset('cacheAPI');
|
||||||
}
|
}
|
||||||
// This will also send any new values to Service Worker
|
// This will also send any new values to Service Worker
|
||||||
refreshCacheStatus();
|
refreshCacheStatus();
|
||||||
|
184
www/js/lib/resetApp.js
Normal file
184
www/js/lib/resetApp.js
Normal file
@ -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 <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* global params, assetsCache */
|
||||||
|
/* eslint-disable indent */
|
||||||
|
|
||||||
|
import uiUtil from './uiUtil.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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('<p><b>WARNING:</b> This will reset the app to a freshly installed state, deleting all app caches,' +
|
||||||
|
// ' <b>Archives stored in the Private File System</b>,' +
|
||||||
|
' and settings! (Archives stored in the OPFS will be preserved.)<b></p><p>Make sure you have an Internet connection</b>' +
|
||||||
|
' if this is an offline PWA, because it will be erased and reloaded.</p>', '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
|
||||||
|
}
|
@ -1,10 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* global params, assetsCache */
|
/* global params */
|
||||||
/* eslint-disable indent */
|
/* eslint-disable indent */
|
||||||
|
|
||||||
import uiUtil from './uiUtil.js';
|
|
||||||
|
|
||||||
var regexpCookieKeysToMigrate = new RegExp([
|
var regexpCookieKeysToMigrate = new RegExp([
|
||||||
'contentInjectionMode', 'lastSelectedArchive', 'lastSelectedArchivePath',
|
'contentInjectionMode', 'lastSelectedArchive', 'lastSelectedArchivePath',
|
||||||
'imageDisplay', 'useMathJax', 'appVersion',
|
'imageDisplay', 'useMathJax', 'appVersion',
|
||||||
@ -68,157 +66,6 @@ function getBestAvailableStorageAPI () {
|
|||||||
return type;
|
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('<p><b>WARNING:</b> This will reset the app to a freshly installed state, deleting all app caches,' +
|
|
||||||
// ' <b>Archives stored in the Private File System</b>,' +
|
|
||||||
' and settings! (Archives stored in the OPFS will be preserved.)<b></p><p>Make sure you have an Internet connection</b>' +
|
|
||||||
' if this is an offline PWA, because it will be erased and reloaded.</p>', '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 = {
|
var settingsStore = {
|
||||||
getItem: function (sKey) {
|
getItem: function (sKey) {
|
||||||
if (!sKey) {
|
if (!sKey) {
|
||||||
@ -309,7 +156,5 @@ export default {
|
|||||||
setItem: settingsStore.setItem,
|
setItem: settingsStore.setItem,
|
||||||
removeItem: settingsStore.removeItem,
|
removeItem: settingsStore.removeItem,
|
||||||
hasItem: settingsStore.hasItem,
|
hasItem: settingsStore.hasItem,
|
||||||
getCacheNames: getCacheNames,
|
|
||||||
reset: reset,
|
|
||||||
getBestAvailableStorageAPI: getBestAvailableStorageAPI
|
getBestAvailableStorageAPI: getBestAvailableStorageAPI
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user