fix: 顶栏颜色跟随小程序主题并保留跟随系统
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createContext, useContext, useState, useCallback, useEffect } from 'react'
|
||||
import Taro, { useDidShow } from '@tarojs/taro'
|
||||
import { getTheme, setTheme as saveTheme, resolveTheme, type ThemeMode } from '../utils/store'
|
||||
import { View } from '@tarojs/components'
|
||||
import { getTheme, setTheme as saveTheme, resolveTheme, setDetectedSystemTheme, type ThemeMode } from '../utils/store'
|
||||
import { applyPageBackground } from '../utils/themeBackground'
|
||||
|
||||
interface ThemeContextValue {
|
||||
@@ -19,16 +20,14 @@ const ThemeContext = createContext<ThemeContextValue>({
|
||||
|
||||
export { ThemeContext }
|
||||
|
||||
type ThemeChangeListener = (result: { theme?: string }) => void
|
||||
|
||||
type ThemeChangeApi = {
|
||||
onThemeChange?: (listener: ThemeChangeListener) => void
|
||||
offThemeChange?: (listener: ThemeChangeListener) => void
|
||||
}
|
||||
|
||||
/** 主题广播事件名:自定义 tabBar 等无法继承 React Context 的独立组件通过它同步主题 */
|
||||
export const THEME_CHANGE_EVENT = 'themeChange'
|
||||
|
||||
type ThemeChangeApi = {
|
||||
onThemeChange?: (listener: (res: { theme?: string }) => void) => void
|
||||
offThemeChange?: (listener: (res: { theme?: string }) => void) => void
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [theme, set] = useState<ThemeMode>(getTheme())
|
||||
const [resolvedTheme, setResolved] = useState<'light' | 'dark'>(resolveTheme(getTheme()))
|
||||
@@ -48,6 +47,57 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
setTheme(next)
|
||||
}, [resolvedTheme, setTheme])
|
||||
|
||||
/**
|
||||
* darkmode 只作为系统主题探测通道;选中“跟随系统”时才跟着系统切换,
|
||||
* 固定浅色/深色时由 setTheme 直接决定,不被动绑定系统。
|
||||
*/
|
||||
const updateFromSystem = useCallback((t: 'light' | 'dark') => {
|
||||
setDetectedSystemTheme(t)
|
||||
if (getTheme() === 'auto') {
|
||||
setResolved(t)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const detectSystemTheme = useCallback(() => {
|
||||
try {
|
||||
const info = Taro.getAppBaseInfo()
|
||||
updateFromSystem(info.theme === 'dark' ? 'dark' : 'light')
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
// CSS 探测器作为兜底:个别机型 getAppBaseInfo().theme 延迟时再读一次
|
||||
try {
|
||||
Taro.createSelectorQuery()
|
||||
.select('.system-theme-detector')
|
||||
.fields({ computedStyle: ['opacity'] })
|
||||
.exec((res: any) => {
|
||||
const opacity = res && res[0] && res[0].computedStyle && res[0].computedStyle.opacity
|
||||
updateFromSystem(opacity === '1' ? 'dark' : 'light')
|
||||
})
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}, [updateFromSystem])
|
||||
|
||||
useEffect(() => {
|
||||
detectSystemTheme()
|
||||
}, [detectSystemTheme])
|
||||
|
||||
// 系统实时切换深浅色时更新“跟随系统”模式
|
||||
useEffect(() => {
|
||||
const listener = (res: { theme?: string }) => {
|
||||
if (res && (res.theme === 'dark' || res.theme === 'light')) {
|
||||
updateFromSystem(res.theme)
|
||||
}
|
||||
}
|
||||
const themeApi = Taro as typeof Taro & ThemeChangeApi
|
||||
themeApi.onThemeChange?.(listener)
|
||||
return () => {
|
||||
themeApi.offThemeChange?.(listener)
|
||||
}
|
||||
}, [updateFromSystem])
|
||||
|
||||
// 广播实际生效主题,供 custom-tab-bar 等独立组件订阅
|
||||
useEffect(() => {
|
||||
Taro.eventCenter.trigger(THEME_CHANGE_EVENT, resolvedTheme)
|
||||
@@ -59,35 +109,20 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
applyPageBackground(resolvedTheme)
|
||||
}, [resolvedTheme])
|
||||
|
||||
// 页面每次显示时(从小程序后台切回前台)重新检测系统主题
|
||||
// 页面每次显示时(从小程序后台切回前台)重新探测系统主题
|
||||
useDidShow(() => {
|
||||
if (theme === 'auto') {
|
||||
setResolved(resolveTheme('auto'))
|
||||
detectSystemTheme()
|
||||
}
|
||||
})
|
||||
|
||||
// auto模式下监听系统主题变化
|
||||
useEffect(() => {
|
||||
if (theme === 'auto') {
|
||||
// 先同步一次当前系统主题
|
||||
setResolved(resolveTheme('auto'))
|
||||
const listener: ThemeChangeListener = (res) => {
|
||||
setResolved(res.theme === 'dark' ? 'dark' : 'light')
|
||||
}
|
||||
const themeApi = Taro as typeof Taro & ThemeChangeApi
|
||||
themeApi.onThemeChange?.(listener)
|
||||
return () => {
|
||||
themeApi.offThemeChange?.(listener)
|
||||
}
|
||||
} else {
|
||||
updateResolved(theme)
|
||||
}
|
||||
}, [theme, updateResolved])
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ theme, resolvedTheme, toggleTheme, setTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
<>
|
||||
<View className='system-theme-detector' />
|
||||
<ThemeContext.Provider value={{ theme, resolvedTheme, toggleTheme, setTheme }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user