A05: Add sessionStorage error handling for unavailable storage
Bug: sessionStorage operations (getItem, setItem, removeItem) could fail if storage is unavailable (private mode, quota exceeded, etc.), causing form draft recovery to break. Fix: Wrap all sessionStorage operations in try/catch via safeStorage helper. - safeStorage.getItem() - returns null on error - safeStorage.setItem() - silently ignores errors - safeStorage.removeItem() - silently ignores errors This ensures: - Draft recovery works even if storage is partially unavailable - Form submission doesn't crash if storage is full - Cleanup on success doesn't crash - File input values are not saved (already handled by FormData filter) Verification: - Astro build: 0 errors - All existing A05 behavior preserved - Error handling added for read, write, and cleanup
This commit is contained in:
@@ -22,11 +22,17 @@ const reportId = Astro.url.searchParams.get("report_id");
|
|||||||
</form>}
|
</form>}
|
||||||
<script is:inline define:vars={{ state }}>
|
<script is:inline define:vars={{ state }}>
|
||||||
const form = document.querySelector(".report-form"); const key = "rf4-report-draft";
|
const form = document.querySelector(".report-form"); const key = "rf4-report-draft";
|
||||||
|
// A05: Safe sessionStorage access with error handling
|
||||||
|
const safeStorage = {
|
||||||
|
getItem: (k) => { try { return sessionStorage.getItem(k); } catch { return null; } },
|
||||||
|
setItem: (k, v) => { try { sessionStorage.setItem(k, v); } catch {} },
|
||||||
|
removeItem: (k) => { try { sessionStorage.removeItem(k); } catch {} },
|
||||||
|
};
|
||||||
// A05: Restore draft on all error states that don't destroy the submission
|
// A05: Restore draft on all error states that don't destroy the submission
|
||||||
const recoverableStates = ["create_error", "rate_limited", "server_error", "timeout"];
|
const recoverableStates = ["create_error", "rate_limited", "server_error", "timeout"];
|
||||||
if (form && recoverableStates.includes(state)) {
|
if (form && recoverableStates.includes(state)) {
|
||||||
try {
|
try {
|
||||||
const draft = JSON.parse(sessionStorage.getItem(key) || "{}");
|
const draft = JSON.parse(safeStorage.getItem(key) || "{}");
|
||||||
for (const [name, value] of Object.entries(draft)) {
|
for (const [name, value] of Object.entries(draft)) {
|
||||||
const field = form.elements.namedItem(name);
|
const field = form.elements.namedItem(name);
|
||||||
if (field && "value" in field) field.value = value;
|
if (field && "value" in field) field.value = value;
|
||||||
@@ -34,12 +40,18 @@ const reportId = Astro.url.searchParams.get("report_id");
|
|||||||
} catch {}
|
} catch {}
|
||||||
document.querySelector("#form-error")?.focus();
|
document.querySelector("#form-error")?.focus();
|
||||||
}
|
}
|
||||||
if (state === "sent" || state === "screenshot_sent") sessionStorage.removeItem(key);
|
if (state === "sent" || state === "screenshot_sent") safeStorage.removeItem(key);
|
||||||
form?.addEventListener("submit", () => {
|
form?.addEventListener("submit", () => {
|
||||||
const btn = form.querySelector("button[type=submit]");
|
const btn = form.querySelector("button[type=submit]");
|
||||||
if (btn) { btn.disabled = true; btn.textContent = "Отправка..."; }
|
if (btn) { btn.disabled = true; btn.textContent = "Отправка..."; }
|
||||||
const draft = {}; for (const [name, value] of new FormData(form)) if (typeof value === "string" && name !== "website") draft[name] = value;
|
// A05: Save draft before submit, handle sessionStorage unavailable
|
||||||
sessionStorage.setItem(key, JSON.stringify(draft));
|
const draft = {};
|
||||||
|
try {
|
||||||
|
for (const [name, value] of new FormData(form)) {
|
||||||
|
if (typeof value === "string" && name !== "website") draft[name] = value;
|
||||||
|
}
|
||||||
|
safeStorage.setItem(key, JSON.stringify(draft));
|
||||||
|
} catch {}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user