From bf6bdf366f4036270615068b770cc568ffc581e5 Mon Sep 17 00:00:00 2001 From: Madhura68 Date: Sun, 10 May 2026 12:50:50 +0200 Subject: [PATCH] feat(PBI-76): bridge runs one-shot localStorage migration After hydrate, scans legacy localStorage keys via buildMigrationPatch and, if any data is found, pushes one bulk patch to the server, applies it locally, then removes the legacy keys. Demo accounts skip the migration entirely. Cancellable on unmount to avoid setState on unmounted component. Co-Authored-By: Claude Opus 4.7 (1M context) --- components/shared/user-settings-bridge.tsx | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/components/shared/user-settings-bridge.tsx b/components/shared/user-settings-bridge.tsx index 6a8740e..71bd436 100644 --- a/components/shared/user-settings-bridge.tsx +++ b/components/shared/user-settings-bridge.tsx @@ -1,8 +1,13 @@ 'use client' import { useEffect } from 'react' +import { updateUserSettingsAction } from '@/actions/user-settings' import { useUserSettingsStore } from '@/stores/user-settings/store' import type { UserSettings } from '@/lib/user-settings' +import { + buildMigrationPatch, + clearLegacyLocalStorage, +} from '@/lib/user-settings-migration' interface Props { initial: UserSettings @@ -23,6 +28,29 @@ export function UserSettingsBridge({ initial, isDemo }: Props) { hydrate(initial, isDemo) }, [hydrate, initial, isDemo]) + // One-shot migration: read legacy localStorage prefs, push to server, clear. + // Idempotent via marker; demo accounts skip (no server-write). + useEffect(() => { + if (isDemo) return + const result = buildMigrationPatch() + if (!result.hasData) { + clearLegacyLocalStorage([]) + return + } + let cancelled = false + void (async () => { + const res = await updateUserSettingsAction(result.patch) + if (cancelled) return + if ('success' in res && res.success) { + applyServerPatch(result.patch) + clearLegacyLocalStorage(result.legacyKeys) + } + })() + return () => { + cancelled = true + } + }, [isDemo, applyServerPatch]) + useEffect(() => { if (isDemo) return const es = new EventSource('/api/realtime/user-settings')