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 { applyPageBackground } from '../utils/themeBackground' interface ThemeContextValue { theme: ThemeMode resolvedTheme: 'light' | 'dark' toggleTheme: () => void setTheme: (t: ThemeMode) => void } const ThemeContext = createContext({ theme: 'auto', resolvedTheme: 'light', toggleTheme: () => {}, setTheme: () => {} }) 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' export function ThemeProvider({ children }: { children: React.ReactNode }) { const [theme, set] = useState(getTheme()) const [resolvedTheme, setResolved] = useState<'light' | 'dark'>(resolveTheme(getTheme())) const updateResolved = useCallback((mode: ThemeMode) => { setResolved(resolveTheme(mode)) }, []) const setTheme = useCallback((t: ThemeMode) => { saveTheme(t) set(t) updateResolved(t) }, [updateResolved]) const toggleTheme = useCallback(() => { const next = resolvedTheme === 'light' ? 'dark' : 'light' setTheme(next) }, [resolvedTheme, setTheme]) // 广播实际生效主题,供 custom-tab-bar 等独立组件订阅 useEffect(() => { Taro.eventCenter.trigger(THEME_CHANGE_EVENT, resolvedTheme) }, [resolvedTheme]) // App 启动时立即同步页面背景(自定义导航区在 iOS 上由 backgroundColorTop 控制), // 避免首屏/切换 tab 时顶部导航区先显示白色再变主题色 useEffect(() => { applyPageBackground(resolvedTheme) }, [resolvedTheme]) // 页面每次显示时(从小程序后台切回前台)重新检测系统主题 useDidShow(() => { if (theme === 'auto') { setResolved(resolveTheme('auto')) } }) // 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 ( {children} ) } export function useThemeContext(): ThemeContextValue { return useContext(ThemeContext) }