Skip to content

THU-504: Local settings store#857

Open
raivieiraadriano92 wants to merge 11 commits into
mainfrom
raivieiraadriano92/thu-504-local-settings-store
Open

THU-504: Local settings store#857
raivieiraadriano92 wants to merge 11 commits into
mainfrom
raivieiraadriano92/thu-504-local-settings-store

Conversation

@raivieiraadriano92
Copy link
Copy Markdown
Collaborator

@raivieiraadriano92 raivieiraadriano92 commented May 11, 2026

Summary

  • Create a Zustand + localStorage store (local-settings-store.ts) for device-specific settings that shouldn't sync across devices
  • Migrate cloud_url, debug_posthog, is_native_fetch_enabled, haptics_enabled, syncEnabled, and theme from the PowerSync DB / raw localStorage to the new store
  • Remove 5 dead default definitions from defaults/settings.ts and the unused ui-theme DB setting
  • Refactor ThemeProvider to read/write via the Zustand store instead of its own localStorage key
  • Reset local settings to defaults on data deletion to preserve pre-migration cleanup behavior

Why

  • cloud_url creates a bootstrap circular dependency — needed before DB init, but stored in DB
  • is_native_fetch_enabled was read via async DB query on every fetch request — now synchronous
  • Device-specific settings (haptics, native fetch, debug flags) shouldn't sync across devices
  • ui-theme DB setting was dead code — ThemeProvider used its own localStorage
  • powersync_sync_enabled was a scattered raw localStorage flag

Test plan

  • App starts correctly — cloud_url available at Step 0 without DB
  • Dev settings page: edit cloud URL, toggle native fetch, toggle debug PostHog — all persist across refresh
  • Preferences page: haptics toggle works and persists
  • Theme toggle (light/dark/system) works and persists across refresh
  • Sync toggle on/off connects/disconnects PowerSync
  • Chat components using cloudUrl (citations, tool icons, link previews) render correctly
  • SSO redirect and sign-in form work
  • Logout → "Delete data from device" resets local settings to defaults
  • Logout → "Leave data on device" preserves local settings
  • localStorage has thunderbolt-local-settings key with correct shape
  • TypeScript compiles with no errors, all tests pass

Note

Medium Risk
Touches initialization, networking base URL selection, SSO redirect, analytics init, and sync enablement by moving several settings off the DB/localStorage flags into a new persisted store; mis-migration or missing test updates could break startup/auth flows or point clients at the wrong backend.

Overview
Introduces a new persisted Zustand store (src/stores/local-settings-store.ts) for device-local settings and switches multiple call sites to read cloudUrl, theme, syncEnabled, hapticsEnabled, isNativeFetchEnabled, and debugPosthog from it instead of the PowerSync DB or ad-hoc localStorage keys.

This rewires startup/bootstrap paths to use the store (e.g. fetchConfig(...) now uses getLocalSetting('cloudUrl')), updates ThemeProvider and the pre-paint theme script in index.html to use the new thunderbolt-local-settings persisted shape, and updates UI surfaces (Dev Settings, Preferences, chat citation/link preview/tool icon components, SSO redirect, sign-in) to use store values.

Cleans up removed/dead DB defaults in defaults/settings.ts, moves PowerSync sync enablement to the store, and resets local settings back to initialLocalSettings when clearing local data.

Reviewed by Cursor Bugbot for commit 521a8aa. Bugbot is set up for automated code reviews on this repo. Configure here.

@raivieiraadriano92 raivieiraadriano92 self-assigned this May 11, 2026
@raivieiraadriano92 raivieiraadriano92 changed the title Raivieiraadriano92/thu 504 local settings store THU-504: Local settings store May 11, 2026
@github-actions
Copy link
Copy Markdown

Semgrep Security Scan

No security issues found.

@raivieiraadriano92 raivieiraadriano92 marked this pull request as ready for review May 11, 2026 21:55
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 11, 2026

PR Metrics

Metric Value
Lines changed (prod code) +174 / -176
JS bundle size (gzipped) 🟢 1.02 MB → 1.02 MB (-4.0 KB, -0.4%)
Test coverage 🟢 71.47% → 71.45% (+-0.0%)
Performance (preview) Preview not ready — Render deploy may have timed out
Accessibility
Best Practices
SEO

Updated Wed, 13 May 2026 21:10:50 GMT · run #1492

Comment thread src/components/chat/tool-icon.tsx
Copy link
Copy Markdown
Collaborator

@ital0 ital0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pointed out some details I noticed.

I completetly ignore files under eval since they should be used only internally.

Comment thread src/stores/local-settings-store.ts Outdated
Comment thread src/db/powersync/database.ts
Comment thread src/lib/theme-provider.tsx Outdated
Comment thread src/stores/local-settings-store.ts Outdated
Comment thread src/components/chat/citation-badge.tsx Outdated
@github-actions
Copy link
Copy Markdown

Preview environment deployed 🚀

Service URL
Marketing / blog / docs https://thunderbolt-pr-857.preview.thunderbolt.io
App https://app-pr-857.preview.thunderbolt.io
API https://api-pr-857.preview.thunderbolt.io
Keycloak https://auth-pr-857.preview.thunderbolt.io
PowerSync https://powersync-pr-857.preview.thunderbolt.io

Stack: preview-pr-857 · Commit: 521a8aa5ae3d83f5e02bbe22298d908e52a2ac9e

Auto-destroys on PR close/merge. Login via the bundled Keycloak realm — demo@thunderbolt.io / demo by default.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 521a8aa. Configure here.

const setLocalSetting = useLocalSettingsStore((s) => s.setLocalSetting)

const isModified = <K extends keyof typeof initialLocalSettings>(key: K) =>
useLocalSettingsStore.getState()[key] !== initialLocalSettings[key]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Store read via getState() during render risks tearing

Low Severity

The isModified helper calls useLocalSettingsStore.getState() directly during render to check whether a value differs from its default. Under React 18 concurrent rendering, getState() can return a newer snapshot than the one the component is rendering with (from useShallow), causing the modification indicator to be inconsistent with the displayed value. Deriving isModified from the already-subscribed values instead of a separate getState() call would keep everything consistent within the same render pass.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 521a8aa. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants