Reopen last selected archive with File System Access API #1218 (#1221)

This commit is contained in:
Jaifroid 2024-03-02 18:07:35 +00:00 committed by GitHub
parent 7c65c58292
commit 0e5db5ba8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 65 additions and 21 deletions

View File

@ -124,12 +124,18 @@ best to display static content, but much functionality is likely to be broken. H
You can switch between these content injection modes in Configuration, but if your browser supports ServiceWorker mode as an offline-first PWA, you are
strongly advised to remain in this mode.
### Limitations
### File access and other limitations
It is not yet technically possible automatically re-open a selected ZIM file between sessions. However, browsers that support the File System Access API
or the `webkitdirectory` property of the File API, allow you to re-open a folder or directory of ZIMs with a quick permission prompt. Another alternative
is to drag-and-drop a ZIM file into the app. There are [versions of this app](https://kiwix.github.io/kiwix-js-pwa/app) that have experimental support for
the Origin Private File System, or that use frameworks like Electron, which do have the capability of remembering the chosen archive between app launches.
You can only re-open an archive automatically if your browser supports the File System Access API and allows you to grant permanent access permission.
In practice, this currently means Chromium browsers (Chrome, Edge, etc.) with a version number of 122 or higher. If that is the case, you will see a
popup asking you whether you wish to grant access "on every visit" (this will appear only after the second or third time that you have picked an archive
or folder). If you grant this permanent permission, then the browser will (optionally) re-open the last-visited archive when you open the app.
In other cases, your browser may fall back to using the `webkitdirectory` property of the File API, which allows you to re-open a folder or directory of
ZIMs with a quick permission prompt. Another alternative is to drag-and-drop a ZIM file into the app.
There are [versions of this app](https://kiwix.github.io/kiwix-js-pwa/app) that have experimental support for the Origin Private File System, or that use
frameworks like Electron, which do have the capability of remembering the chosen archive between app launches.
The app has fast title search, and slower full-text search for ZIM archives that have a full-text index, thanks to the
[openzim/javascript-libzim](https://github.com/openzim/javascript-libzim) project. Currently, full-text searching only works in browsers

View File

@ -49,6 +49,8 @@ document.localeJson = {
"configure-display-homekeyfocus-tip": "Auto-focuses the search bar when you press the Home key, and also when you click or tap on the Home tab. Enables quick access to search, no matter where you are in an article.",
"configure-display-openexternallinks": "<strong>Open external links in new tabs</strong>. Disabling this might break kiwix-js UI in some specific cases",
"configure-display-openexternallinks-tip": "Opens the external links outside kiwix-js (avoids some side-effects affecting kiwix-js UI).",
"configure-display-reopenlastarchive": "<strong>Automatically re-open last selected archive</strong> (only works if you grant permanent permission when prompted)",
"configure-display-reopenlastarchive-tip": "If your browser supports the permanent permissions feature of the File System Access API, you can automatically re-open archives when you restart the app. To enable this functionality, you need to give permission to access files 'on every visit' when prompted by your browser.",
"configure-display-selectapptheme": "<b>Select app theme</b> (content inversion is experimental):",
"configure-display-selectapptheme-tip": "Allows selection of themes either for the app only, or for the app and the loaded content.",
"configure-display-themeoption-light": "Light",

View File

@ -49,6 +49,8 @@ document.localeJson = {
"configure-display-homekeyfocus-tip": "Enfoca automáticamente la barra de búsqueda al pulsar la tecla Inicio, y también al hacer clic o tocar en la pestaña Inicio. Habilita acceso rápido a la búsqueda con el teclado, sin importar en qué parte del artículo esté.",
"configure-display-openexternallinks": "<strong>Abrir enlaces externos en nuevas pestañas</strong>. Si desactiva esto, se puede romper la interfaz de kiwix-js en algunos casos específicos",
"configure-display-openexternallinks-tip": "Abre los enlaces externos fuera de kiwix-js (evita algunos efectos secundarios que afectan la interfaz de kiwix-js).",
"configure-display-reopenlastarchive": "<strong>Reabrir automáticamente el último archivo seleccionado</strong> (sólo funciona si concede permiso permanente cuando se le solicite)",
"configure-display-reopenlastarchive-tip": "Si su navegador soporta la característica de permisos permanentes de la API de Acceso al Sistema de Archivos, puede reabrir automáticamente los archivos cuando reinicie la aplicación. Para habilitar esta funcionalidad, debe dar permiso para acceder a los archivos 'en cada visita' cuando su navegador se lo solicite.",
"configure-display-selectapptheme": "<b>Seleccionar tema de la aplicación</b> (la inversión de contenido es experimental):",
"configure-display-selectapptheme-tip": "Permite la selección de temas para la aplicación o para la aplicación y el contenido cargado.",
"configure-display-themeoption-light": "Claro",

View File

@ -49,6 +49,8 @@ document.localeJson = {
"configure-display-homekeyfocus-tip": "La barre de recherche se focalise automatiquement lorsque vous appuyez sur la touche Accueil, ainsi que lorsque vous cliquez ou tapez sur l'onglet Accueil. Active l'accès rapide à la recherche par clavier, quel que soit l'endroit où vous vous trouvez dans l'article.",
"configure-display-openexternallinks": "<strong>Ouvrir les liens externes dans de nouveaux onglets</strong>. Si vous désactivez cette option, l'interface de kiwix-js peut être cassée dans certains cas spécifiques",
"configure-display-openexternallinks-tip": "Ouvre les liens externes en dehors de kiwix-js (évite certains effets secondaires affectant l'interface de kiwix-js).",
"configure-display-reopenlastarchive": "<strong>Rouvrir automatiquement la dernière archive sélectionnée</strong> (fonctionne uniquement si vous accordez la permission permanente lorsque vous y êtes invité(e))",
"configure-display-reopenlastarchive-tip": "Si votre navigateur prend en charge la fonctionnalité de permissions permanentes de l'API d'Accès au Système de Fichiers, vous pouvez rouvrir automatiquement les archives lorsque vous redémarrez l'application. Pour activer cette fonctionnalité, vous devez donner la permission d'accéder aux fichiers 'à chaque visite' lorsque votre navigateur vous le demande.",
"configure-display-selectapptheme": "<b>Sélectionner le thème de l'application</b> (le mode inversé est expérimentale)&nbsp;:",
"configure-display-selectapptheme-tip": "Permet de sélectionner un thème pour l'application ou pour l'application et le contenu chargé.",
"configure-display-themeoption-light": "Clair",

View File

@ -17,7 +17,7 @@
Peter-x - https://github.com/peter-x
Jaifroid - https://github.com/Jaifroid
Copyright 2013-2023 Mossroy, Peter-x, Jaifroid, sharun-s and contributors
Copyright 2013-2024 Mossroy, Peter-x, Jaifroid, sharun-s and contributors
Licence GPL v3:
This file is part of Kiwix.
@ -393,7 +393,7 @@
<h3 id="licence" data-i18n="about-licence">Licence information</h3>
<div data-i18n="about-licence-text">
<p>Copyright 2013-2023 Mossroy, Peter-x, Jaifroid and other contributors.</p>
<p>Copyright 2013-2024 Mossroy, Jaifroid, Peter-x and other contributors.</p>
<p>This application is licensed under the GPL v3 Licence:</p>
<em>
<p>Kiwix is free software: you can redistribute it and/or modify
@ -541,9 +541,14 @@
</div>
<div class="checkbox">
<label data-i18n-tip="configure-display-openexternallinks-tip" title="Opens the external links outside kiwix-js (avoids some side-effects affecting kiwix-js UI).">
<input type="checkbox" name="openExternalLinksInNewTabs"
id="openExternalLinksInNewTabsCheck" checked>
<span data-i18n="configure-display-openexternallinks"><strong>Open external links in new tabs</strong>. Disabling this might break kiwix-js UI in some specific cases
<input type="checkbox" name="openExternalLinksInNewTabs" id="openExternalLinksInNewTabsCheck" checked>
<span data-i18n="configure-display-openexternallinks"><strong>Open external links in new tabs</strong>. Disabling this might break kiwix-js UI in some specific cases
</label>
</div>
<div id="reopenLastArchiveDiv" class="checkbox" style="display: none;">
<label data-i18n-tip="configure-display-reopenlastarchive-tip" title="If your browser supports the permanent permissions feature of the File System Access API, you can automatically re-open archives when you restart the app. To enable this functionality, you need to give permission to access files 'on every visit' when prompted by your browser.">
<input type="checkbox" name="reopenLastArchive" id="reopenLastArchiveCheck" checked>
<span data-i18n="configure-display-reopenlastarchive"><strong>Automatically re-open last selected archive</strong> (only works if you grant permanent permission when prompted)</span>
</label>
</div>
<div class="form-group">

View File

@ -499,7 +499,7 @@ document.getElementById('disableDragAndDropCheck').addEventListener('change', fu
// Handle switching from jQuery to serviceWorker modes.
document.getElementById('serviceworkerModeRadio').addEventListener('click', async function () {
document.getElementById('enableSourceVerificationCheckBox').style.display = '';
if (selectedArchive.isReady() && !(settingsStore.getItem("trustedZimFiles").includes(selectedArchive.file.name)) && params.sourceVerification) {
if (selectedArchive.isReady() && !(settingsStore.getItem('trustedZimFiles').includes(selectedArchive.file.name)) && params.sourceVerification) {
await verifyLoadedArchive(selectedArchive);
}
});
@ -511,7 +511,7 @@ document.getElementById('jqueryModeRadio').addEventListener('click', function ()
// Handle switching to serviceWorkerLocal mode for chrome-extension
document.getElementById('serviceworkerLocalModeRadio').addEventListener('click', async function () {
document.getElementById('enableSourceVerificationCheckBox').style.display = '';
if (selectedArchive.isReady() && !(settingsStore.getItem("trustedZimFiles").includes(selectedArchive.file.name)) && params.sourceVerification) {
if (selectedArchive.isReady() && !(settingsStore.getItem('trustedZimFiles').includes(selectedArchive.file.name)) && params.sourceVerification) {
await verifyLoadedArchive(selectedArchive);
}
});
@ -561,6 +561,10 @@ document.querySelectorAll('input[type="checkbox"][name=openExternalLinksInNewTab
settingsStore.setItem('openExternalLinksInNewTabs', params.openExternalLinksInNewTabs, Infinity);
})
});
document.getElementById('reopenLastArchiveCheck').addEventListener('change', function (e) {
params.reopenLastArchive = e.target.checked;
settingsStore.setItem('reopenLastArchive', params.reopenLastArchive, Infinity);
});
document.getElementById('appThemeSelect').addEventListener('change', function (e) {
params.appTheme = e.target.value;
settingsStore.setItem('appTheme', params.appTheme, Infinity);
@ -633,7 +637,7 @@ function focusPrefixOnHomeKey (event) {
* @param {archive} the archive that needs verification
* */
async function verifyLoadedArchive (archive) {
const response = await uiUtil.systemAlert(translateUI.t('dialog-sourceverification-alert') || "Is this ZIM archive from a trusted source?\n If not, you can still read the ZIM file in Safe Mode (aka JQuery mode). Closing this window also opens the file in Safe Mode. This option can be disabled in Expert Settings", translateUI.t('dialog-sourceverification-title') || "Security alert!", true, translateUI.t('dialog-sourceverification-safe-mode-button') || 'Open in Safe Mode', translateUI.t('dialog-sourceverification-trust-button')|| 'Trust Source');
const response = await uiUtil.systemAlert(translateUI.t('dialog-sourceverification-alert') || 'Is this ZIM archive from a trusted source?\n If not, you can still read the ZIM file in Safe Mode (aka JQuery mode). Closing this window also opens the file in Safe Mode. This option can be disabled in Expert Settings', translateUI.t('dialog-sourceverification-title') || 'Security alert!', true, translateUI.t('dialog-sourceverification-safe-mode-button') || 'Open in Safe Mode', translateUI.t('dialog-sourceverification-trust-button') || 'Trust Source');
if (response) {
params.contentInjectionMode = 'serviceworker';
var trustedZimFiles = settingsStore.getItem('trustedZimFiles');
@ -1311,14 +1315,27 @@ if ($.isFunction(navigator.getDeviceStorages)) {
});
}
// @AUTOLOAD of archives starts here for frameworks or APIs that allow it
// If DeviceStorage is available (Firefox OS), we look for archives in it
if (storages !== null && storages.length > 0) {
// Make a fake first access to device storage, in order to ask the user for confirmation if necessary.
// This way, it is only done once at this moment, instead of being done several times in callbacks
// After that, we can start looking for archives
storages[0].get('fake-file-to-read').then(searchForArchivesInPreferencesOrStorage,
searchForArchivesInPreferencesOrStorage);
// If the File System Access API is available, we may be able to autoload the last selected archive in Chromium > 122
// which has persistent permissions
} else if (params.reopenLastArchive && window.showOpenFilePicker && params.previousZimFileName) {
displayFileSelect();
abstractFilesystemAccess.getSelectedZimFromCache(params.previousZimFileName).then(function (files) {
setLocalArchiveFromFileList(files);
}).catch(function (err) {
console.warn(err);
document.getElementById('btnConfigure').click();
});
// If no autoload API is available, we display the file select dialog
} else {
// If DeviceStorage is not available, we display the file select components
displayFileSelect();
if (archiveFiles.files && archiveFiles.files.length > 0) {
// Archive files are already selected,
@ -1714,9 +1731,9 @@ async function archiveReadyCallback (archive) {
if (params.sourceVerification && (params.contentInjectionMode === 'serviceworker' || params.contentInjectionMode === 'serviceworkerlocal')) {
// Check if source of the zim file can be trusted.
if (!(settingsStore.getItem('trustedZimFiles').includes(archive.file.name))) {
await verifyLoadedArchive(archive);
await verifyLoadedArchive(archive);
}
}
}
// When a new ZIM is loaded, we turn this flag to null, so that we don't get false positive attempts to use the Worker
// It will be defined as false or true when the first article is loaded
appstate.isReplayWorkerAvailable = null;

View File

@ -128,6 +128,8 @@ params['isWebkitDirApiSupported'] = 'webkitdirectory' in document.createElement(
params['sourceVerification'] = params.contentInjectionMode === 'serviceworker' ? (getSetting('sourceVerification') === null ? true : getSetting('sourceVerification')) : false; // Sets a boolean indicating weather a user trusts the source of zim files
params['libzimMode'] = getSetting('libzimMode') || 'wasm'; // Sets a value indicating which libzim mode is selected
params['useLibzim'] = !!getSetting('useLibzim'); // Sets a value indicating which libzim mode is selected
params['previousZimFileName'] = getSetting('previousZimFileName') || ''; // Sets the name of the last opened zim file
params['reopenLastArchive'] = getSetting('reopenLastArchive') !== false; // Sets a Boolean defaulting to true indicating whether to reopen the last opened zim file if possible
/**
* Apply any override parameters that might be in the querystring.
@ -194,6 +196,10 @@ document.getElementById('libzimModeSelect').value = params.libzimMode;
document.getElementById('useLibzim').checked = params.useLibzim;
document.getElementById('appVersion').textContent = 'Kiwix ' + params.appVersion;
document.getElementById('enableSourceVerification').checked = getSetting('sourceVerification') === null ? true : getSetting('sourceVerification');
document.getElementById('reopenLastArchiveCheck').checked = params.reopenLastArchive;
// If the File System Access API is supported, unhide the reopenLastArchiveDiv
if (params.isFileSystemApiSupported) document.getElementById('reopenLastArchiveDiv').style.display = '';
// This is a simplified version of code in settingsStore, because that module is not available in init.js
function getSetting (name) {
var result;

View File

@ -181,12 +181,16 @@ function getSelectedZimFromCache (selectedFilename) {
return new Promise((resolve, reject) => {
cache.idxDB('zimFiles', async function (fileOrDirHandle) {
if (!fileOrDirHandle) {
reject(new Error('No file or directory selected'));
return reject(new Error('No file or directory selected'));
}
// Request permission if not already granted
if ((await fileOrDirHandle.queryPermission()) !== 'granted') {
try {
await fileOrDirHandle.requestPermission();
} catch (error) {
return reject(new Error('Permission denied', error));
}
}
// Left it here for debugging purposes as its sometimes asking for permission even when its granted
// console.debug('FileHandle and Permission', fileOrDirHandle, await fileOrDirHandle.queryPermission())
if ((await fileOrDirHandle.queryPermission()) !== 'granted') await fileOrDirHandle.requestPermission();
if (fileOrDirHandle.kind === 'directory') {
const files = [];
for await (const entry of fileOrDirHandle.values()) {