From 283b27447a45cc1a791e3c008d3afb3683163e9e Mon Sep 17 00:00:00 2001 From: tenacitas-paypa Date: Fri, 26 Jun 2026 11:03:56 +0200 Subject: [PATCH 1/2] fix: show error banner if localStorage data is corrupted --- src/main.jsx | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main.jsx b/src/main.jsx index 7a566cf..eb37b61 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -29,6 +29,9 @@ const translations = { unstar: 'Remove saved session', starredOnly: 'Show saved sessions', donations: 'Donors', + storageErrorNotice: 'Some saved sessions could not be read and were reset.', + storageErrorClear: 'Clear saved sessions', + storageErrorDismiss: 'Dismiss', donationsTitle: 'Donations', donationsRegisterPrefix: 'The conference is free to attend; you only need to register on the', donationsRegisterLink: 'website', @@ -60,6 +63,9 @@ const translations = { unstar: 'Quitar sesión guardada', starredOnly: 'Mostrar sesiones guardadas', donations: 'Donors', + storageErrorNotice: 'Algunos datos guardados no se pudieron leer y se han restablecido.', + storageErrorClear: 'Limpiar sesiones guardadas', + storageErrorDismiss: 'Ignorar', donationsTitle: 'Donaciones', donationsRegisterPrefix: 'La conferencia es de libre acceso, solo tienes que registrarte en la', donationsRegisterLink: 'web', @@ -182,13 +188,35 @@ function App() { const [showStarredOnly, setShowStarredOnly] = useState(false); const [starredIds, setStarredIds] = useState(() => { const stored = window.localStorage.getItem(starredStorageKey); - return new Set(stored ? JSON.parse(stored) : []); + if (!stored) return new Set(); + try { + const parsed = JSON.parse(stored); + return new Set(Array.isArray(parsed) ? parsed : []); + } catch { + return new Set(); + } + }); + const [storageError, setStorageError] = useState(() => { + const stored = window.localStorage.getItem(starredStorageKey); + if (!stored) return false; + try { + const parsed = JSON.parse(stored); + return !Array.isArray(parsed); + } catch { + return true; + } }); const [expandedEventId, setExpandedEventId] = useState(null); const [clickedExpandedEventId, setClickedExpandedEventId] = useState(null); const [selectedEvent, setSelectedEvent] = useState(null); const [showDonations, setShowDonations] = useState(false); const cardRefs = useRef(new Map()); + + function clearStorage() { + window.localStorage.removeItem(starredStorageKey); + setStarredIds(new Set()); + setStorageError(false); + } const labels = translations[locale]; const activeSchedule = schedule.find((day) => day.date === activeDay) ?? schedule[0]; const normalizedQuery = normalize(query.trim()); @@ -309,6 +337,19 @@ function App() { return ( <> + {storageError && ( +
+

{labels.storageErrorNotice}

+
+ + +
+
+ )}
From 0a47506aea0454d4649a85fb2c1b5ea1de4caaa4 Mon Sep 17 00:00:00 2001 From: tenacitas-paypa Date: Fri, 26 Jun 2026 11:04:16 +0200 Subject: [PATCH 2/2] feat: add storage error banner styles --- src/styles.css | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/styles.css b/src/styles.css index b8dc6a4..8311806 100644 --- a/src/styles.css +++ b/src/styles.css @@ -749,3 +749,69 @@ h1 { z-index: 0; border-radius: inherit; } + +/* Storage error banner */ +.storage-error-banner { + position: fixed; + top: 16px; + left: 50%; + z-index: 1000; + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + justify-content: space-between; + width: min(560px, calc(100% - 32px)); + padding: 12px 16px; + border: 1px solid #c05621; + border-radius: 12px; + background: #fff7ed; + box-shadow: 0 4px 16px rgb(0 0 0 / 12%); + color: #7c2d12; + transform: translateX(-50%); +} + +.storage-error-text { + margin: 0; + flex: 1; + min-width: 0; + font-size: 0.875rem; +} + +.storage-error-actions { + display: flex; + gap: 8px; + align-items: center; + flex-shrink: 0; +} + +.storage-error-clear { + padding: 6px 12px; + border: 1px solid #c05621; + border-radius: 999px; + background: #c05621; + color: #fff; + font-size: 0.8125rem; + font-weight: 700; + cursor: pointer; + white-space: nowrap; +} + +.storage-error-clear:hover { + background: #9a3412; + border-color: #9a3412; +} + +.storage-error-dismiss { + padding: 6px 10px; + border: none; + background: transparent; + color: #9a3412; + font-size: 0.8125rem; + cursor: pointer; + text-decoration: underline; +} + +.storage-error-dismiss:hover { + color: #7c2d12; +}