Skip to content

Commit bc16fc7

Browse files
committed
Remove localStorage snapshot storage
Since cloud sync handles persistence, local snapshots are no longer needed. This fixes the localStorage quota exceeded issue. Changes: - Remove all snapshot storage from writeStorage() - Remove appendSnapshot, clearAllSnapshots functions - Remove STORAGE_SNAPSHOT_LIMIT constant - Simplify getLatestSnapshotValue to always return null - Remove 'Restore Latest Snapshot' button from UI - Remove restoreLocalLibraries function from Sidebar - Simplify getSnapshotCount to always return 0 Data now flows: cloud <-> local only (no snapshot backup)
1 parent 9c80f18 commit bc16fc7

4 files changed

Lines changed: 17 additions & 122 deletions

File tree

functions/_lib/buildInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const APP_VERSION = "0.9.14";
2-
export const APP_COMMIT = "c0d17193";
2+
export const APP_COMMIT = "9c80f183";
33
export const APP_BUILD_LABEL = `v${APP_VERSION}+${APP_COMMIT}`;
44
export type BuildChannel = "stable" | "beta" | "alpha";
55
export const buildLabelForChannel = (channel: BuildChannel): string => {

src/components/Sidebar.tsx

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,7 @@ const formatMqttSourceMeta = (value: unknown): string[] => {
265265
return lines;
266266
};
267267

268-
const getSnapshotCount = (key: string): number => {
269-
try {
270-
const raw = localStorage.getItem(`${key}-snapshots-v1`);
271-
if (!raw) return 0;
272-
const parsed = JSON.parse(raw) as unknown;
273-
return Array.isArray(parsed) ? parsed.length : 0;
274-
} catch {
275-
return 0;
276-
}
277-
};
268+
const getSnapshotCount = (_key: string): number => 0;
278269

279270
export function Sidebar() {
280271
const { theme, colorTheme, variant } = useThemeVariant();
@@ -343,7 +334,6 @@ export function Sidebar() {
343334
const createBlankSimulationPreset = useAppStore((state) => state.createBlankSimulationPreset);
344335
const loadSimulationPreset = useAppStore((state) => state.loadSimulationPreset);
345336
const updateSimulationPresetEntry = useAppStore((state) => state.updateSimulationPresetEntry);
346-
const restoreLibrariesFromSnapshots = useAppStore((state) => state.restoreLibrariesFromSnapshots);
347337
const recommendAndFetchTerrainForCurrentArea = useAppStore(
348338
(state) => state.recommendAndFetchTerrainForCurrentArea,
349339
);
@@ -986,29 +976,6 @@ export function Sidebar() {
986976
);
987977
};
988978

989-
const restoreLocalLibraries = () => {
990-
const confirmed = window.confirm(
991-
"Restore latest snapshot for local site/simulation libraries? This can overwrite recent unsaved local edits.",
992-
);
993-
if (!confirmed) {
994-
setStorageStatus("Restore cancelled.");
995-
return;
996-
}
997-
const result = restoreLibrariesFromSnapshots();
998-
refreshSnapshotInfo();
999-
if (!result.restored) {
1000-
setStorageStatus("No snapshots available to restore.");
1001-
return;
1002-
}
1003-
const now = new Date().toISOString();
1004-
const nextHealth = { ...storageHealth, lastRestoreIso: now };
1005-
setStorageHealth(nextHealth);
1006-
writeStorageHealth(nextHealth);
1007-
setStorageStatus(
1008-
`Restored from snapshots: ${result.siteCount} site(s), ${result.simulationCount} simulation(s).`,
1009-
);
1010-
};
1011-
1012979
const openAddLinkModal = () => {
1013980
const hasFromInSites = sites.some((site) => site.id === selectedLink.fromSiteId);
1014981
const hasToInSites = sites.some((site) => site.id === selectedLink.toSiteId);
@@ -2339,11 +2306,6 @@ export function Sidebar() {
23392306
<button className="inline-action" onClick={exportLocalLibraries} type="button">
23402307
Export Library Backup
23412308
</button>
2342-
{currentUser?.isAdmin && (
2343-
<button className="inline-action" onClick={restoreLocalLibraries} type="button">
2344-
Restore Latest Snapshot
2345-
</button>
2346-
)}
23472309
</div>
23482310
{storageStatus ? <p className="field-help">{storageStatus}</p> : null}
23492311
<label className="field-grid">

src/lib/buildInfo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const APP_VERSION = "0.9.14";
2-
export const APP_COMMIT = "c0d17193";
2+
export const APP_COMMIT = "9c80f183";
33
export const APP_BUILD_LABEL = `v${APP_VERSION}+${APP_COMMIT}`;
44
export type BuildChannel = "stable" | "beta" | "alpha";
55
export const buildLabelForChannel = (channel: BuildChannel): string => {

src/store/appStore.ts

Lines changed: 14 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -332,15 +332,6 @@ const UI_THEME_PREFERENCE_KEY = "linksim-ui-theme-v1";
332332
const UI_COLOR_THEME_KEY = "linksim-ui-color-theme-v1";
333333
const BASEMAP_PROVIDER_KEY = "linksim-basemap-provider-v1";
334334
const BASEMAP_STYLE_PRESET_KEY = "linksim-basemap-style-preset-v1";
335-
const STORAGE_SNAPSHOT_LIMIT = 5;
336-
337-
type StoredSnapshot<T> = {
338-
savedAtIso: string;
339-
value: T;
340-
};
341-
342-
const snapshotKeyFor = (key: string): string => `${key}-snapshots-v1`;
343-
const isSnapshotTrackedKey = (key: string): boolean => key === SITE_LIBRARY_KEY || key === SIM_PRESETS_KEY;
344335

345336
const readStorage = <T,>(key: string, fallback: T): T => {
346337
try {
@@ -362,77 +353,19 @@ const readStorageRawState = <T,>(key: string): { status: "ok" | "missing" | "inv
362353
}
363354
};
364355

365-
const readSnapshotHistory = <T,>(key: string): StoredSnapshot<T>[] => {
366-
const parsed = readStorage<StoredSnapshot<T>[]>(snapshotKeyFor(key), []);
367-
return Array.isArray(parsed) ? parsed : [];
368-
};
369-
370-
const getLatestSnapshotValue = <T,>(key: string): T | null => {
371-
const history = readSnapshotHistory<T>(key);
372-
for (let i = history.length - 1; i >= 0; i -= 1) {
373-
const item = history[i];
374-
if (item && typeof item === "object" && "value" in item) {
375-
return item.value;
376-
}
377-
}
378-
return null;
379-
};
380-
381-
const appendSnapshot = (key: string, value: unknown) => {
382-
if (!isSnapshotTrackedKey(key)) return;
383-
const history = readSnapshotHistory<unknown>(key);
384-
const next: StoredSnapshot<unknown>[] = [
385-
...history,
386-
{
387-
savedAtIso: new Date().toISOString(),
388-
value,
389-
},
390-
].slice(-STORAGE_SNAPSHOT_LIMIT);
391-
localStorage.setItem(snapshotKeyFor(key), JSON.stringify(next));
392-
};
393-
394-
const writeStorage = (key: string, value: unknown, options?: { snapshot?: boolean }): boolean => {
395-
const tryWriteWithSnapshot = (): boolean => {
396-
try {
397-
localStorage.setItem(key, JSON.stringify(value));
398-
if (options?.snapshot !== false) appendSnapshot(key, value);
399-
return true;
400-
} catch {
401-
return false;
402-
}
403-
};
404-
405-
const clearAllSnapshots = (): void => {
406-
const keys = [SITE_LIBRARY_KEY, SIM_PRESETS_KEY];
407-
for (const k of keys) {
408-
try {
409-
const snapshotKey = snapshotKeyFor(k);
410-
const existing = localStorage.getItem(snapshotKey);
411-
if (existing) {
412-
localStorage.setItem(snapshotKey, "[]");
413-
console.log(`[appStore] Cleared ALL snapshots for "${k}" due to quota`);
414-
}
415-
} catch {
416-
// Best effort
417-
}
418-
}
419-
};
420-
421-
if (tryWriteWithSnapshot()) {
356+
const writeStorage = (key: string, value: unknown): boolean => {
357+
try {
358+
localStorage.setItem(key, JSON.stringify(value));
422359
return true;
360+
} catch (error) {
361+
console.error(`[appStore] Failed to write to localStorage (${key}):`, error);
362+
return false;
423363
}
364+
};
424365

425-
console.error(`[appStore] localStorage QUOTA EXCEEDED for key "${key}". Clearing snapshots and retrying...`);
426-
clearAllSnapshots();
366+
const getLatestSnapshotValue = <T,>(_key: string): T | null => null;
427367

428-
if (tryWriteWithSnapshot()) {
429-
console.log(`[appStore] Retry after clearing snapshots succeeded`);
430-
return true;
431-
}
432368

433-
console.error(`[appStore] localStorage QUOTA EXCEEDED (retry failed) for key "${key}"`);
434-
return false;
435-
};
436369

437370
const makeId = (prefix: string): string =>
438371
`${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -1132,22 +1065,22 @@ export const useAppStore = create<AppState>((set, get) => ({
11321065
},
11331066
setUiThemePreference: (value) => {
11341067
const normalized = normalizeUiThemePreference(value);
1135-
writeStorage(UI_THEME_PREFERENCE_KEY, normalized, { snapshot: false });
1068+
writeStorage(UI_THEME_PREFERENCE_KEY, normalized);
11361069
set({ uiThemePreference: normalized });
11371070
},
11381071
setUiColorTheme: (value) => {
11391072
const normalized = normalizeUiColorTheme(value);
1140-
writeStorage(UI_COLOR_THEME_KEY, normalized, { snapshot: false });
1073+
writeStorage(UI_COLOR_THEME_KEY, normalized);
11411074
set({ uiColorTheme: normalized });
11421075
},
11431076
setBasemapProvider: (value) => {
11441077
const normalized = normalizeBasemapProvider(value);
1145-
writeStorage(BASEMAP_PROVIDER_KEY, normalized, { snapshot: false });
1078+
writeStorage(BASEMAP_PROVIDER_KEY, normalized);
11461079
set({ basemapProvider: normalized });
11471080
},
11481081
setBasemapStylePreset: (value) => {
11491082
const normalized = normalizeBasemapStylePreset(value);
1150-
writeStorage(BASEMAP_STYLE_PRESET_KEY, normalized, { snapshot: false });
1083+
writeStorage(BASEMAP_STYLE_PRESET_KEY, normalized);
11511084
set({ basemapStylePreset: normalized });
11521085
},
11531086
selectScenario: (id) => {
@@ -1184,7 +1117,7 @@ export const useAppStore = create<AppState>((set, get) => ({
11841117
mapViewport: scenario.viewport,
11851118
siteLibrary: libraryBacked.siteLibrary,
11861119
});
1187-
writeStorage(LAST_SESSION_KEY, { selectedScenarioId: scenario.id, savedAtIso: new Date().toISOString() }, { snapshot: false });
1120+
writeStorage(LAST_SESSION_KEY, { selectedScenarioId: scenario.id, savedAtIso: new Date().toISOString() });
11881121
get().recomputeCoverage();
11891122
},
11901123
setSelectedLinkId: (id) =>
@@ -1887,7 +1820,7 @@ export const useAppStore = create<AppState>((set, get) => ({
18871820
if (libraryBacked.addedCount > 0) {
18881821
writeStorage(SITE_LIBRARY_KEY, libraryBacked.siteLibrary);
18891822
}
1890-
writeStorage(LAST_SESSION_KEY, { selectedScenarioId: preset.id, savedAtIso: new Date().toISOString() }, { snapshot: false });
1823+
writeStorage(LAST_SESSION_KEY, { selectedScenarioId: preset.id, savedAtIso: new Date().toISOString() });
18911824
get().recomputeCoverage();
18921825
},
18931826
renameSimulationPreset: (presetId, name) => {

0 commit comments

Comments
 (0)