Clarify local backup permission recovery
All checks were successful
Deploy Sokko G / deploy (push) Successful in 17s

This commit is contained in:
Shinuwa 2026-09-25 09:53:58 +02:00
parent 7b23a8601a
commit 34e542be28
6 changed files with 20 additions and 11 deletions

View file

@ -36,7 +36,9 @@ locale du navigateur et conservent les dix fichiers `sokkog-global-YYYY-MM-DD.js
les plus récents. La vérification au démarrage ne demande pas de permission et ne les plus récents. La vérification au démarrage ne demande pas de permission et ne
crée pas les jours manquants lorsque l'application était fermée. Une nouvelle crée pas les jours manquants lorsque l'application était fermée. Une nouvelle
sauvegarde automatique est ignorée si son contenu est identique à la version la sauvegarde automatique est ignorée si son contenu est identique à la version la
plus récente, en excluant uniquement l'horodatage d'export. plus récente, en excluant uniquement l'horodatage d'export. Si le navigateur ne
conserve pas l'autorisation du dossier entre deux sessions, l'interface propose
une réautorisation explicite ; le démarrage n'ouvre aucune demande de permission.
Entrée type dans `modules` : Entrée type dans `modules` :

View file

@ -94,13 +94,17 @@ test("manual backup replaces the day and retains ten non-consecutive versions",
assert.equal(directory.files.get("notes.json"), "old"); assert.equal(directory.files.get("notes.json"), "old");
}); });
test("permission failure prevents export and cleanup", async () => { test("missing permission never opens a prompt or starts an export", async () => {
const directory = new FakeDirectory(); for (const permission of ["prompt", "denied"]) {
directory.permission = "prompt"; const directory = new FakeDirectory();
let generated = false; directory.permission = permission;
const result = await saveBackup({ directoryHandle: directory, generateJson: async () => { generated = true; return "{}"; } }); directory.requestPermission = () => { throw new Error("Permission popup must require user interaction"); };
assert.equal(result.reason, "permission"); let generated = false;
assert.equal(generated, false); const result = await saveBackup({ directoryHandle: directory, generateJson: async () => { generated = true; return "{}"; } });
assert.equal(result.reason, "permission");
assert.equal(result.permission, permission);
assert.equal(generated, false);
}
}); });
test("cleanup failure does not invalidate a successfully written backup", async () => { test("cleanup failure does not invalidate a successfully written backup", async () => {

View file

@ -233,6 +233,7 @@ export function validateSiteContent(site) {
"toolboxes.autoBackup.chooseFolder", "toolboxes.autoBackup.chooseFolder",
"toolboxes.autoBackup.changeFolder", "toolboxes.autoBackup.changeFolder",
"toolboxes.autoBackup.reauthorize", "toolboxes.autoBackup.reauthorize",
"toolboxes.autoBackup.permissionRequired",
"toolboxes.autoBackup.saveNow", "toolboxes.autoBackup.saveNow",
"toolboxes.autoBackup.noFolder", "toolboxes.autoBackup.noFolder",
"toolboxes.autoBackup.never", "toolboxes.autoBackup.never",

View file

@ -1829,6 +1829,7 @@
"chooseFolder": "Choisir un dossier", "chooseFolder": "Choisir un dossier",
"changeFolder": "Changer de dossier", "changeFolder": "Changer de dossier",
"reauthorize": "Réautoriser l’accès", "reauthorize": "Réautoriser l’accès",
"permissionRequired": "L’accès au dossier doit être réautorisé pour reprendre les sauvegardes. Utilisez « Réautoriser l’accès » ci-dessous.",
"saveNow": "Sauvegarder maintenant", "saveNow": "Sauvegarder maintenant",
"noFolder": "Aucun dossier", "noFolder": "Aucun dossier",
"never": "Jamais", "never": "Jamais",

View file

@ -118,10 +118,11 @@ export function AutoBackupModal({ backup, content, onClose }) {
<span className="switch-track" aria-hidden="true"><span className="switch-thumb" /></span> <span className="switch-track" aria-hidden="true"><span className="switch-thumb" /></span>
<span className="auto-backup-toggle-copy"><strong>{content.enabled}</strong><small>{content.toggleHelp}</small><small>{content.retention}</small></span> <span className="auto-backup-toggle-copy"><strong>{content.enabled}</strong><small>{content.toggleHelp}</small><small>{content.retention}</small></span>
</label> </label>
{backup.config.directoryHandle && (backup.permission === "prompt" || backup.permission === "denied") && <p className="nds-inline-notice" data-variant="warning" role="status">{content.permissionRequired}</p>}
{backup.error && <p className="nds-inline-notice" data-variant="danger" role="alert">{backup.error}</p>} {backup.error && <p className="nds-inline-notice" data-variant="danger" role="alert">{backup.error}</p>}
<footer className="nds-modal-footer auto-backup-modal-footer"> <footer className="nds-modal-footer auto-backup-modal-footer">
{backup.config.directoryHandle && backup.permission !== "granted" && <button className="nds-button" data-size="sm" type="button" disabled={backup.busy} onClick={() => void backup.requestPermission()}>{content.reauthorize}</button>} {backup.config.directoryHandle && (backup.permission === "prompt" || backup.permission === "denied") && <button className="nds-button" data-size="sm" type="button" disabled={backup.busy} onClick={() => void backup.requestPermission()}>{content.reauthorize}</button>}
<button className="nds-button" data-size="sm" data-variant="primary" type="button" disabled={backup.busy || !backup.config.directoryHandle} onClick={() => void backup.saveNow()}>{content.saveNow}</button> <button className="nds-button" data-size="sm" data-variant="primary" type="button" disabled={backup.busy || !backup.config.directoryHandle || backup.permission !== "granted"} onClick={() => void backup.saveNow()}>{content.saveNow}</button>
</footer> </footer>
</> </>
)} )}

View file

@ -72,7 +72,7 @@ export function useAutoBackup(store) {
const next = { ...configOverride, directoryHandle, lastBackup: new Date().toISOString() }; const next = { ...configOverride, directoryHandle, lastBackup: new Date().toISOString() };
await persist(next); await persist(next);
} }
if (!result.ok) setError(result.error?.message || "La sauvegarde locale a échoué."); if (!result.ok) setError(result.reason === "permission" ? "" : result.error?.message || "La sauvegarde locale a échoué.");
if (result.ok && result.cleanupError) setError(result.cleanupError); if (result.ok && result.cleanupError) setError(result.cleanupError);
return result; return result;
} catch (runError) { } catch (runError) {