navidrome/ui/src/themes/useCurrentTheme.js
Deluan Quintão caa2f8a0c0
fix(ui): make playlist toggle switches visible in all themes (#6277)
* fix(ui): make playlist toggle switches visible in all themes

The Public and Auto-import switches in the playlist list did not set a
color, so Material-UI used the theme's secondary color. Many themes use
secondary as a surface color close to the table background, which made
checked switches nearly invisible (Catppuccin, Rosé Pine, Monokai,
Moonbase and others).

Set color="primary" on the playlist switch, like every other switch in
the app, and make primary the default MuiSwitch color in useCurrentTheme
so future switches cannot regress. Fixes #6272.

* refactor(ui): drop secondary switch overrides from themes

Dracula, Gruvbox Dark, Tokyo Night and Tokyo Night Light styled checked
MuiSwitch colorSecondary to work around the same invisible-switch problem
(Gruvbox in #5064). With primary as the default switch color and every
switch in the app using it, no switch renders with colorSecondary anymore,
so these overrides are dead code.
2026-10-06 09:50:11 -04:00

74 lines
2.4 KiB
JavaScript

import { useSelector } from 'react-redux'
import useMediaQuery from '@material-ui/core/useMediaQuery'
import themes from './index'
import { AUTO_THEME_ID } from '../consts'
import config from '../config'
import { useEffect, useMemo } from 'react'
const useCurrentTheme = () => {
// Runs above the ThemeProvider carrying the prop below, so it needs its own noSsr or the
// auto theme renders dark first and flips.
const prefersLightMode = useMediaQuery('(prefers-color-scheme: light)', {
noSsr: true,
})
const theme = useSelector((state) => {
if (state.theme === AUTO_THEME_ID) {
return prefersLightMode ? themes.LightTheme : themes.DarkTheme
}
const themeName =
Object.keys(themes).find((t) => t === state.theme) ||
Object.keys(themes).find(
(t) => themes[t].themeName === config.defaultTheme,
) ||
'DarkTheme'
return themes[themeName]
})
useEffect(() => {
const styles = document.getElementsByTagName('style')
let style
for (let i = 0; i < styles.length; i++) {
if (styles[i].id === 'nd-player-style-override') {
style = styles[i]
}
}
if (theme.player.stylesheet) {
if (style === undefined) {
style = document.createElement('style')
style.id = 'nd-player-style-override'
style.innerHTML = theme.player.stylesheet
document.head.appendChild(style)
} else {
style.innerHTML = theme.player.stylesheet
}
} else {
if (style !== undefined) {
document.head.removeChild(style)
}
}
// Set body background color to match theme (fixes white background on pull-to-refresh)
const isDark = theme.palette?.type === 'dark'
const bgColor =
theme.palette?.background?.default || (isDark ? '#303030' : '#fafafa')
document.body.style.backgroundColor = bgColor
}, [theme])
// We never server-render, so let media queries resolve on the first render: the default
// defers them to an effect, which makes every mount paint the wrong breakpoint and reflow.
return useMemo(
() => ({
...theme,
props: {
...theme.props,
MuiUseMediaQuery: { noSsr: true },
MuiPopover: { disableScrollLock: true },
// MUI defaults to secondary, which many themes use as a surface color
MuiSwitch: { color: 'primary' },
},
}),
[theme],
)
}
export default useCurrentTheme