From 752a5b255694df550f95a05e59635168308b669a Mon Sep 17 00:00:00 2001 From: xRangerDE Date: Mon, 11 May 2026 07:33:23 +0200 Subject: [PATCH] =?UTF-8?q?cleanup:=20centralise=20localStorage=20try/catc?= =?UTF-8?q?h=20in=20renderer-shared=20=E2=80=94=20safeLocalStorageGet/Set/?= =?UTF-8?q?Remove?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every renderer module that persists state was wrapping its localStorage.getItem/setItem/removeItem call in the same try/catch idiom — handling private-browsing quirks and other sandbox contexts where storage isn't writable. Three identical patterns repeated nine times across renderer-streamers (filter / sort / hide-downloaded state), renderer-updates (skipped-update version), and renderer.ts (active-tab persistence). Introduced three helpers in renderer-shared.ts: - safeLocalStorageGet(key, fallback = '') — wraps getItem with the try/catch + fallback - safeLocalStorageSet(key, value) — wraps setItem - safeLocalStorageRemove(key) — wraps removeItem (needed for clearSkippedUpdateVersion which actually deletes the entry rather than blanking it) Refactored 9 callsites. Reduces the noise:before: try { return localStorage.getItem(KEY) ?? ''; } catch { return ''; } try { localStorage.setItem(KEY, value); } catch { /* localStorage may be unavailable */ } after: return safeLocalStorageGet(KEY); safeLocalStorageSet(KEY, value); Left the VOD scroll-positions persistence in renderer-streamers untouched — its surrounding try/catch wraps JSON.parse/stringify logic that doesn't fit the simple helper signature. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/renderer-shared.ts | 16 ++++++++++++++++ src/renderer-streamers.ts | 24 +++++++++--------------- src/renderer-updates.ts | 6 +++--- src/renderer.ts | 8 +++----- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/renderer-shared.ts b/src/renderer-shared.ts index 4549b5a..5d29f10 100644 --- a/src/renderer-shared.ts +++ b/src/renderer-shared.ts @@ -19,6 +19,22 @@ function escapeHtml(value: string): string { .replace(/'/g, '''); } +/* localStorage helpers — every renderer module that persists state was + wrapping its get/set calls in the same try/catch idiom to handle + environments where localStorage isn't writable (private-browsing + quirks, certain sandboxed contexts). Centralising the pattern. */ +function safeLocalStorageGet(key: string, fallback = ''): string { + try { return localStorage.getItem(key) ?? fallback; } catch { return fallback; } +} + +function safeLocalStorageSet(key: string, value: string): void { + try { localStorage.setItem(key, value); } catch { /* localStorage may be unavailable */ } +} + +function safeLocalStorageRemove(key: string): void { + try { localStorage.removeItem(key); } catch { /* localStorage may be unavailable */ } +} + let config: AppConfig = {}; let currentStreamer: string | null = null; let isConnected = false; diff --git a/src/renderer-streamers.ts b/src/renderer-streamers.ts index 9833a41..3112736 100644 --- a/src/renderer-streamers.ts +++ b/src/renderer-streamers.ts @@ -53,11 +53,11 @@ const VOD_HIDE_DOWNLOADED_STORAGE_KEY = 'twitch-vod-manager:vod-hide-downloaded' let vodHideDownloaded = false; function loadPersistedHideDownloaded(): boolean { - try { return localStorage.getItem(VOD_HIDE_DOWNLOADED_STORAGE_KEY) === '1'; } catch { return false; } + return safeLocalStorageGet(VOD_HIDE_DOWNLOADED_STORAGE_KEY) === '1'; } function persistHideDownloaded(value: boolean): void { - try { localStorage.setItem(VOD_HIDE_DOWNLOADED_STORAGE_KEY, value ? '1' : '0'); } catch { /* ignore */ } + safeLocalStorageSet(VOD_HIDE_DOWNLOADED_STORAGE_KEY, value ? '1' : '0'); } function onVodHideDownloadedChange(): void { @@ -78,17 +78,15 @@ const VOD_SORT_STORAGE_KEY = 'twitch-vod-manager:vod-sort'; let vodSortKey: VodSortKey = 'date_desc'; function loadPersistedVodSort(): VodSortKey { - try { - const stored = localStorage.getItem(VOD_SORT_STORAGE_KEY); - if (stored && (VALID_VOD_SORTS as readonly string[]).includes(stored)) { - return stored as VodSortKey; - } - } catch { /* localStorage may be unavailable */ } + const stored = safeLocalStorageGet(VOD_SORT_STORAGE_KEY); + if (stored && (VALID_VOD_SORTS as readonly string[]).includes(stored)) { + return stored as VodSortKey; + } return 'date_desc'; } function persistVodSort(key: VodSortKey): void { - try { localStorage.setItem(VOD_SORT_STORAGE_KEY, key); } catch { /* localStorage may be unavailable */ } + safeLocalStorageSet(VOD_SORT_STORAGE_KEY, key); } function vodDurationToSeconds(durationStr: string): number { @@ -162,15 +160,11 @@ function refreshVodSortSelectLabels(): void { } function loadPersistedVodFilter(): string { - try { - return localStorage.getItem(VOD_FILTER_STORAGE_KEY) ?? ''; - } catch { - return ''; - } + return safeLocalStorageGet(VOD_FILTER_STORAGE_KEY); } function persistVodFilter(query: string): void { - try { localStorage.setItem(VOD_FILTER_STORAGE_KEY, query); } catch { /* localStorage may be unavailable */ } + safeLocalStorageSet(VOD_FILTER_STORAGE_KEY, query); } function filterVodsByQuery(vods: VOD[], query: string): VOD[] { diff --git a/src/renderer-updates.ts b/src/renderer-updates.ts index 935a1a0..83d0c91 100644 --- a/src/renderer-updates.ts +++ b/src/renderer-updates.ts @@ -12,15 +12,15 @@ let shouldOpenUpdateModalOnAvailable = false; const SKIPPED_UPDATE_VERSION_KEY = 'twitch-vod-manager:skipped-update-version'; function getSkippedUpdateVersion(): string { - try { return localStorage.getItem(SKIPPED_UPDATE_VERSION_KEY) || ''; } catch { return ''; } + return safeLocalStorageGet(SKIPPED_UPDATE_VERSION_KEY); } function persistSkippedUpdateVersion(version: string): void { - try { localStorage.setItem(SKIPPED_UPDATE_VERSION_KEY, version); } catch { /* localStorage may be unavailable */ } + safeLocalStorageSet(SKIPPED_UPDATE_VERSION_KEY, version); } function clearSkippedUpdateVersion(): void { - try { localStorage.removeItem(SKIPPED_UPDATE_VERSION_KEY); } catch { /* localStorage may be unavailable */ } + safeLocalStorageRemove(SKIPPED_UPDATE_VERSION_KEY); } function notifyUpdate(message: string, type: 'info' | 'warn' = 'info'): void { diff --git a/src/renderer.ts b/src/renderer.ts index 72203d1..f68bdd7 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -836,16 +836,14 @@ function isKnownTab(value: string): value is typeof TAB_IDS[number] { } function loadPersistedActiveTab(): string { - try { - const stored = localStorage.getItem(ACTIVE_TAB_STORAGE_KEY); - if (stored && isKnownTab(stored)) return stored; - } catch { /* localStorage may be unavailable in privacy modes */ } + const stored = safeLocalStorageGet(ACTIVE_TAB_STORAGE_KEY); + if (stored && isKnownTab(stored)) return stored; return 'vods'; } function persistActiveTab(tab: string): void { if (!isKnownTab(tab)) return; - try { localStorage.setItem(ACTIVE_TAB_STORAGE_KEY, tab); } catch { } + safeLocalStorageSet(ACTIVE_TAB_STORAGE_KEY, tab); } function showTab(tab: string): void {