Fix localStorage race conditions and error handling in user settings#274
Fix localStorage race conditions and error handling in user settings#274
Conversation
…ove dead code Co-authored-by: devakesu <61821107+devakesu@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Improves robustness of user settings persistence and session validation by hardening storage interactions and removing stale/racy logic around Supabase session checks.
Changes:
- Wraps
localStoragewrites intry/catchduring optimistic updates to avoid failing mutations in restricted storage environments. - Updates
validateActiveSessionto avoid stale mount-state parameters and reduce race conditions during async session checks. - Switches
CourseCardfromgetUser()togetSession()to prefer cached session access and reduce unnecessary network calls.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/providers/user-settings.ts |
Adds guarded localStorage writes in onMutate, refactors session validation helper, and removes dead initial-fetch ref logic. |
src/components/attendance/course-card.tsx |
Uses getSession() instead of getUser() when deriving the scoped localStorage key for bunk calc UI state. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try { | ||
| if (newSettings.bunk_calculator_enabled !== undefined) { | ||
| localStorage.setItem(`showBunkCalc_${userId}`, newSettings.bunk_calculator_enabled.toString()); | ||
| window.dispatchEvent(new CustomEvent("bunkCalcToggle", { detail: newSettings.bunk_calculator_enabled })); | ||
| } | ||
| if (newSettings.target_percentage !== undefined) { | ||
| const normalizedTarget = normalizeTarget(newSettings.target_percentage); | ||
| localStorage.setItem(`targetPercentage_${userId}`, normalizedTarget.toString()); | ||
| } | ||
| } catch (error) { | ||
| // Storage access may throw in private mode or when disabled | ||
| // Log error but don't fail the mutation - DB update can still proceed | ||
| logger.dev("Failed to write to localStorage in onMutate", { error }); |
There was a problem hiding this comment.
The try block wraps both localStorage.setItem and the bunkCalcToggle event dispatch. In environments where setItem throws (private mode / storage disabled), the event will never dispatch, so listeners (e.g. CourseCard) won’t update even though the optimistic cache update succeeded. Consider limiting the try/catch to just the localStorage calls and dispatching the event regardless (or dispatching in a finally after computing the new value).
| try { | |
| if (newSettings.bunk_calculator_enabled !== undefined) { | |
| localStorage.setItem(`showBunkCalc_${userId}`, newSettings.bunk_calculator_enabled.toString()); | |
| window.dispatchEvent(new CustomEvent("bunkCalcToggle", { detail: newSettings.bunk_calculator_enabled })); | |
| } | |
| if (newSettings.target_percentage !== undefined) { | |
| const normalizedTarget = normalizeTarget(newSettings.target_percentage); | |
| localStorage.setItem(`targetPercentage_${userId}`, normalizedTarget.toString()); | |
| } | |
| } catch (error) { | |
| // Storage access may throw in private mode or when disabled | |
| // Log error but don't fail the mutation - DB update can still proceed | |
| logger.dev("Failed to write to localStorage in onMutate", { error }); | |
| if (newSettings.bunk_calculator_enabled !== undefined) { | |
| try { | |
| localStorage.setItem(`showBunkCalc_${userId}`, newSettings.bunk_calculator_enabled.toString()); | |
| } catch (error) { | |
| // Storage access may throw in private mode or when disabled | |
| // Log error but don't fail the mutation - DB update can still proceed | |
| logger.dev("Failed to write bunk_calculator_enabled to localStorage in onMutate", { error }); | |
| } | |
| // Always notify listeners about bunk calculator toggle, even if localStorage fails | |
| window.dispatchEvent(new CustomEvent("bunkCalcToggle", { detail: newSettings.bunk_calculator_enabled })); | |
| } | |
| if (newSettings.target_percentage !== undefined) { | |
| const normalizedTarget = normalizeTarget(newSettings.target_percentage); | |
| try { | |
| localStorage.setItem(`targetPercentage_${userId}`, normalizedTarget.toString()); | |
| } catch (error) { | |
| // Storage access may throw in private mode or when disabled | |
| // Log error but don't fail the mutation - DB update can still proceed | |
| logger.dev("Failed to write target_percentage to localStorage in onMutate", { error }); | |
| } |
* feat: update localStorage handling for user-specific settings - Upgrade glob package to version 13.0.1. - Modify CourseCard component to use user ID for scoped localStorage keys for the bunk calculator setting. - Update useUserSettings hook to sync user settings to localStorage using user-specific keys. - Implement cleanup of legacy localStorage keys after migrating to user-scoped keys. - Ensure session validation checks are performed correctly to prevent race conditions during user logout. * Fix session validation and null userId in localStorage operations (#273) * Initial plan * fix: address PR review comments for localStorage and session validation - Fix null userId in onMutate by getting session directly instead of using ref - Add userId validation in validateActiveSession to prevent cross-user leakage - Wrap getSession() calls in try/catch to handle transient errors gracefully - Add isMounted guard in CourseCard to prevent state updates after unmount - Replace dynamic import with static import for Supabase client - Use logger.dev instead of console.debug for consistency Co-authored-by: devakesu <61821107+devakesu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devakesu <61821107+devakesu@users.noreply.github.com> * Fix localStorage race conditions and error handling in user settings (#274) * Initial plan * Address review comments: add error handling, fix race conditions, remove dead code Co-authored-by: devakesu <61821107+devakesu@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devakesu <61821107+devakesu@users.noreply.github.com> --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Addresses code review feedback on user settings persistence: localStorage operations could fail in restricted environments, async session validation had race conditions, and dead code remained from previous refactoring.
Changes
onMutate): Storage access throws in private mode; catch and log without failing the mutationvalidateActiveSessionrace condition: Remove stale boolean parameter, readisMountedfrom closure aftergetSession()completes to prevent cross-user leakagehasCompletedInitialFetchRef: Dead state from earlier sync logic iterationgetSession()overgetUser()(CourseCard): Avoid network request on mount; read cached session✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.