Skip to content

Conversation

@pd-redis
Copy link
Contributor

@pd-redis pd-redis commented Nov 28, 2025

What

Add automatic system theme change detection. When user selects "System" theme, the app now automatically updates when the OS theme changes from dark to light (or vice versa).

  • Created useSystemThemeListener hook that listens to prefers-color-scheme media query changes
  • Integrated hook in App.tsx to monitor system theme changes
  • Hook only activates when user has System theme selected

Testing

  • Manual: Switch OS theme while app is open with System theme selected - app theme updates automatically
  • Unit tests: Basic tests verify hook doesn't crash in different scenarios

Closes #RI-7754


Note

Automatically updates app theme when the OS switches light/dark by adding a system theme listener hook and wiring it into App.

  • Theme:
    • Add useSystemThemeListener hook to listen to prefers-color-scheme changes and call changeTheme(Theme.System).
    • Integrate the hook in ui/src/App.tsx to react to system theme changes when using System theme.

Written by Cursor Bugbot for commit 0c9a64c. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 28, 2025

Code Coverage - Frontend unit tests

St.
Category Percentage Covered / Total
🟢 Statements 82.9% 21003/25336
🟡 Branches 68.08% 8847/12995
🟡 Functions 78% 5744/7364
🟢 Lines 83.3% 20568/24690

Test suite run success

5474 tests passing in 705 suites.

Report generated by 🧪jest coverage report action from 0c9a64c

valkirilov
valkirilov previously approved these changes Dec 1, 2025
Comment on lines 11 to 36
export const useSystemThemeListener = () => {
const { usingSystemTheme, changeTheme } = useThemeContext()

const handleSystemThemeChange = useCallback(() => {
usingSystemTheme && changeTheme(Theme.System)
}, [changeTheme, usingSystemTheme])

useEffect(() => {
if (!mediaQuery) {
// Initialize mediaQuery if not done already because window might not be defined when module is loaded
mediaQuery = window.matchMedia?.(THEME_MATCH_MEDIA_DARK)
}
// Only listen if using system theme
if (usingSystemTheme) {
if (!mediaQuery) {
return undefined
}
mediaQuery.addEventListener('change', handleSystemThemeChange)
}
return () => {
if (mediaQuery) {
mediaQuery.removeEventListener('change', handleSystemThemeChange)
}
}
}, [usingSystemTheme])
}
Copy link
Contributor

@KrumTy KrumTy Dec 1, 2025

Choose a reason for hiding this comment

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

I'm noticing some issues with this implementation.
The goal is to attach an event listener in case the theme is set to SYSTEM that would monitor the respective media match and change theme acordingly.

  1. handleSystemThemeChange is memoized callback, however its two dependencies are changeTheme (stable, does not change), usingSystemTheme (same as the useEffect one's). Also it is not listed as dependency of the useEffect - not clear that it's supposed to be redefined first or the effect is first, plus it's used as cleanup - hard to follow lifecycle. Finally, since It is not used outside the useEffect and has the same dependencies, it is preferable that it's defined inside the useEffect as well.
  2. The useEffect has the same check as the one in handleSystemThemeChange (usingSystemTheme) - it only adds the event listener if usingSystemTheme is true, meaning the check inside handleSystemThemeChange is redundant
  3. The useEffect returns a cleanup function regardless of weather or not an event was attached

I'm suggesting a much simplified solution that should perform the same task and addresses the point mentioned above:

Suggested change
export const useSystemThemeListener = () => {
const { usingSystemTheme, changeTheme } = useThemeContext()
const handleSystemThemeChange = useCallback(() => {
usingSystemTheme && changeTheme(Theme.System)
}, [changeTheme, usingSystemTheme])
useEffect(() => {
if (!mediaQuery) {
// Initialize mediaQuery if not done already because window might not be defined when module is loaded
mediaQuery = window.matchMedia?.(THEME_MATCH_MEDIA_DARK)
}
// Only listen if using system theme
if (usingSystemTheme) {
if (!mediaQuery) {
return undefined
}
mediaQuery.addEventListener('change', handleSystemThemeChange)
}
return () => {
if (mediaQuery) {
mediaQuery.removeEventListener('change', handleSystemThemeChange)
}
}
}, [usingSystemTheme])
}
export const useSystemThemeListener = () => {
const { usingSystemTheme, changeTheme } = useThemeContext()
useEffect(() => {
const mediaQuery = window.matchMedia?.(THEME_MATCH_MEDIA_DARK)
// Only listen if using system theme
if (usingSystemTheme && mediaQuery) {
const handleSystemThemeChange = () => changeTheme(Theme.System)
mediaQuery.addEventListener('change', handleSystemThemeChange)
return () => {
mediaQuery.removeEventListener('change', handleSystemThemeChange)
}
}
return undefined
}, [usingSystemTheme, changeTheme])
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will trigger non-consistent return points warning in tslint, that is why I prefer the more verbose and defensive style I used.

Better safe than sorry

Copy link
Contributor

@KrumTy KrumTy Dec 1, 2025

Choose a reason for hiding this comment

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

ok, add return undefined in the end like you did, eslint is the not related to my concerns above

(edit: updated suggestion above)
(edit2: in js every void function returns undefined)

Remove module-level variable and useCallback wrapper.
Simplify event listener setup and cleanup logic.

References: #RI-7754
Remove module-level variable and useCallback wrapper.
Simplify event listener setup and cleanup logic.

References: #RI-7754
@pd-redis pd-redis merged commit cb1b485 into main Dec 1, 2025
19 checks passed
@pd-redis pd-redis deleted the fe/bugfix/RI-7754-detect-system-color-change branch December 1, 2025 11:02
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.

4 participants