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
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
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` :

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");
});
test("permission failure prevents export and cleanup", async () => {
const directory = new FakeDirectory();
directory.permission = "prompt";
let generated = false;
const result = await saveBackup({ directoryHandle: directory, generateJson: async () => { generated = true; return "{}"; } });
assert.equal(result.reason, "permission");
assert.equal(generated, false);
test("missing permission never opens a prompt or starts an export", async () => {
for (const permission of ["prompt", "denied"]) {
const directory = new FakeDirectory();
directory.permission = permission;
directory.requestPermission = () => { throw new Error("Permission popup must require user interaction"); };
let 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 () => {

View file

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

View file

@ -1829,6 +1829,7 @@
"chooseFolder": "Choisir un dossier",
"changeFolder": "Changer de dossier",
"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",
"noFolder": "Aucun dossier",
"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="auto-backup-toggle-copy"><strong>{content.enabled}</strong><small>{content.toggleHelp}</small><small>{content.retention}</small></span>
</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>}
<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>}
<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>
{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 || backup.permission !== "granted"} onClick={() => void backup.saveNow()}>{content.saveNow}</button>
</footer>
</>
)}

View file

@ -72,7 +72,7 @@ export function useAutoBackup(store) {
const next = { ...configOverride, directoryHandle, lastBackup: new Date().toISOString() };
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);
return result;
} catch (runError) {