Skip to content

fix: consolidate theme state on next-themes useTheme() - #1415

Merged
K1NGD4VID merged 2 commits into
LabsCrypt:mainfrom
Nife-tanny:fix/1262-consolidate-theme-state
Sep 3, 2026
Merged

fix: consolidate theme state on next-themes useTheme()#1415
K1NGD4VID merged 2 commits into
LabsCrypt:mainfrom
Nife-tanny:fix/1262-consolidate-theme-state

Conversation

@Nife-tanny

Copy link
Copy Markdown
Contributor

Overview

Resolves #1262 by consolidating theme state management on next-themes' useTheme() as the single source of truth, eliminating two independent implementations that read/wrote the same flowfi-theme localStorage key without coordinating with each other or with the ThemeProvider.

What Each System Did Before

  1. layout.tsx — Used ThemeProvider (wrapping next-themes) with storageKey="flowfi-theme". This was the correct, canonical implementation.

  2. useSettings.ts — Had its own setTheme() callback that independently:

    • Updated module-level sharedSettings.theme state
    • Wrote to localStorage with key "flowfi-theme"
    • Manually toggled the dark class on document.documentElement
    • Exposed getStoredTheme() and applyStoredTheme() helpers
  3. settings-content.tsx — Had its own useState for theme initialized from localStorage.getItem("flowfi-theme"), and a toggleTheme() function that independently:

    • Set local React state
    • Wrote to localStorage with key "flowfi-theme"
    • Manually toggled the dark class on document.documentElement

The bug: None of the three called each other's setters, so toggling theme in one surface didn't reflect in another within the same tab — only a page reload or cross-tab storage event synced them.

What Changed

useSettings.ts

  • Removed Theme type, theme from Settings interface and DEFAULT_SETTINGS
  • Removed theme from STORAGE_KEYS
  • Removed setTheme() callback from the hook return
  • Removed getStoredTheme() and applyStoredTheme() helpers
  • Removed theme-related logic from loadSettingsFromStorage()
  • Preserved untouched: displayCurrency, amountFormat, decimalPlaces settings and all their setters, formatAmountWithPreference(), getDecimalPlaces(), getAmountFormat(), getDisplayCurrency()

settings-content.tsx

  • Replaced local useState for theme + manual toggleTheme() with useTheme() from next-themes
  • All theme buttons now call setTheme() from useTheme(), ensuring they share state with the provider
  • Preserved untouched: displayCurrency, amountFormat, decimalPlaces local state and their UI

useSettings.test.ts

  • Removed theme-related test cases (the hook no longer returns theme or setTheme)
  • Updated the cross-tab sync test to use displayCurrency instead of theme
  • Preserved untouched: All formatAmountWithPreference tests, all non-theme settings tests

No changes to:

  • layout.tsx — already correctly configured as the ThemeProvider
  • ModeToggle.tsx — already correctly using useTheme() from next-themes
  • theme-provider.tsx — already correctly wrapping NextThemesProvider

Same-Tab Sync Verification

After this change, all three surfaces share the same reactive state via useTheme():

  • ModeToggle (navbar): calls setTheme() from useTheme() → next-themes updates its React context → all subscribers re-render
  • settings-content.tsx: calls setTheme() from useTheme() → same path as above
  • Any useSettings consumer: no longer has theme state at all → no conflict possible

Since useTheme() returns values from the ThemeProvider's React context, any call to setTheme() immediately updates all other components reading from useTheme() in the same render tree — no page reload needed.

Pre-Existing Failures (Intentionally Untouched)

  • create-stream-recipient-prefill.test.tsx: 1 test fails with a 5-second timeout. Confirmed pre-existing (fails identically on the base branch before any changes).
  • TypeScript errors: All tsc --noEmit errors are in pre-existing test files (batch-stream-wizard.test.tsx, streams.test.ts, useStreamingAmount.test.tsx, etc.) — none in the files modified by this PR.
  • Next.js build: Turbopack panics with Invalid distDirRoot: ".next" — confirmed pre-existing (same error on base branch).

Local Verification

Check Status
npm run test (vitest) ✅ 322/323 pass (1 pre-existing timeout)
ESLint on changed files ✅ Clean
tsc --noEmit ✅ No new errors (all errors pre-existing in other files)
next build ⚠️ Pre-existing Turbopack error on base branch

Closes #1262

Remove duplicate theme management from useSettings.ts and
settings-content.tsx, both of which independently read/wrote the
"flowfi-theme" localStorage key and manually toggled the dark class
on <html> without coordinating with each other or with next-themes'
ThemeProvider. This caused toggling theme in one surface (e.g. the
settings page) to not reflect in another (e.g. ModeToggle in the
navbar) until a page reload.

All three theme surfaces now use next-themes' useTheme() as the single
source of truth:
- layout.tsx's ThemeProvider (unchanged — already the provider)
- settings-content.tsx now calls useTheme() from next-themes instead of
  managing its own localStorage read/write and class toggling
- useSettings.ts no longer manages theme at all; consumers needing
  theme use useTheme() from next-themes instead

Non-theme settings (displayCurrency, amountFormat, decimalPlaces) and
their utilities (formatAmountWithPreference, getDecimalPlaces, etc.)
are preserved untouched.

Closes LabsCrypt#1262

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@K1NGD4VID

Copy link
Copy Markdown
Collaborator

resolve conflict on this issue @Nife-tanny

@K1NGD4VID
K1NGD4VID merged commit 6ee4dc7 into LabsCrypt:main Sep 3, 2026
13 checks passed
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.

[Audit] Theme state is managed by three independent, uncoordinated systems

2 participants