mirror of
https://github.com/chmouel/liseur-sync.git
synced 2026-10-19 03:17:09 +00:00
fix: Preserve offline error state
This commit is contained in:
parent
461deae9ff
commit
bcbf0b382e
3 changed files with 34 additions and 5 deletions
|
|
@ -58,10 +58,12 @@ if (buttons.length) {
|
|||
button.disabled = false;
|
||||
const controller = new AbortController();
|
||||
active = controller;
|
||||
let operationAccount = "";
|
||||
button.textContent = "Cancel download";
|
||||
say(button, "Preparing offline copy…");
|
||||
try {
|
||||
const identity = await auth.acquire();
|
||||
operationAccount = identity.account;
|
||||
const partition = storagePartition();
|
||||
const context = await accountContext(partition, identity.account);
|
||||
const authorize = async current => {
|
||||
|
|
@ -119,9 +121,21 @@ if (buttons.length) {
|
|||
}
|
||||
} catch (error) {
|
||||
if (error.name === "AbortError") say(button, "Download cancelled");
|
||||
else if (error.code === "quota") say(button, error.message, true);
|
||||
else say(button, error.message || "Offline download failed", true);
|
||||
button.textContent = "Save offline";
|
||||
else {
|
||||
const message = error.code === "quota"
|
||||
? error.message : error.message || "Offline download failed";
|
||||
let restored = false;
|
||||
if (operationAccount) {
|
||||
try {
|
||||
await updateReadyState(button, operationAccount);
|
||||
restored = true;
|
||||
} catch (stateError) {
|
||||
console.warn("offline download state could not be restored", stateError);
|
||||
}
|
||||
}
|
||||
if (!restored) button.textContent = "Save offline";
|
||||
say(button, message, true);
|
||||
}
|
||||
} finally {
|
||||
active = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,9 @@ export async function drainOfflineOutbox(context, request) {
|
|||
responseCurrent: () => true,
|
||||
accepted: done,
|
||||
refused: (_, code) => failed("failed", code || "session rejected"),
|
||||
deferred: () => {},
|
||||
deferred: (status, code) => failed(
|
||||
"pending", code || `HTTP ${status || "unknown"}`,
|
||||
),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
|
@ -49,11 +51,15 @@ export async function drainOfflineOutbox(context, request) {
|
|||
if (response.status === 409) await failed("conflict", "revision conflict", body?.server);
|
||||
else if ([400, 403, 404, 413, 422].includes(response.status))
|
||||
await failed("failed", body?.error || `HTTP ${response.status}`);
|
||||
else await failed("pending", body?.error || `HTTP ${response.status}`);
|
||||
continue;
|
||||
}
|
||||
const result = body?.results?.find(value => (annotation ? value.id : value.op_id) ===
|
||||
(annotation ? record.annotationID : record.id));
|
||||
if (!result) continue;
|
||||
if (!result) {
|
||||
await failed("pending", "missing acknowledgement");
|
||||
continue;
|
||||
}
|
||||
if (["applied", "duplicate"].includes(result.status)) {
|
||||
if (annotation) {
|
||||
const server = { ...record.payload.annotation, rev: result.rev, seq: result.seq };
|
||||
|
|
|
|||
|
|
@ -188,6 +188,15 @@ async function storageChecks() {
|
|||
"permanent annotation error does not stop sessions or other annotations");
|
||||
await rejected(() => s.finishOfflineSession({ ...context, bookID, payload: { ...payload, active_ms: 2000 } }),
|
||||
"delivered session retains immutable identity");
|
||||
await queue("retryable", "retryable", "retryable");
|
||||
await drainOfflineOutbox(context, async () => reply({ error: "temporary failure" }, "", 500));
|
||||
const retryable = (await pending()).find(row => row.id === "retryable");
|
||||
check(retryable?.state === "pending" && retryable.error === "temporary failure",
|
||||
"unexpected server errors stay retryable with an error");
|
||||
await drainOfflineOutbox(context, async () =>
|
||||
reply({ results: [{ id: "retryable", status: "applied", rev: 1 }] }));
|
||||
check(!(await pending()).some(row => row.id === "retryable"),
|
||||
"retryable outbox records recover after a later success");
|
||||
await s.saveOfflineSessionCheckpoint({ ...context, bookID, checkpoint: { id: "session" } });
|
||||
check(!await s.getOfflineSessionCheckpoint({ ...context, bookID }), "late checkpoint cannot recreate finalized session");
|
||||
await s.saveOfflineSessionCheckpoint({ ...context, bookID, checkpoint: { id: "next-session" } });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue