69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react'
|
||
import { Appearance } from 'react-native'
|
||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||
import { makeTheme, type ThemeMode, type ThemeTokens } from './ItmsTheme'
|
||
|
||
/*
|
||
* ITMS 테마 컨텍스트 — 단일 브랜드(WISE 블루) × 모드(LIGHT/DARK/SYSTEM).
|
||
* 로컬 AsyncStorage 영속(오프라인). ITMS 백엔드에는 별도 preferences API 가 없어 서버 동기 없음.
|
||
*/
|
||
export type PrefMode = ThemeMode | 'system'
|
||
const MODE_KEY = 'itms_theme_mode'
|
||
|
||
interface ThemeContextValue {
|
||
t: ThemeTokens
|
||
resolved: ThemeMode
|
||
mode: PrefMode
|
||
setMode: (m: PrefMode) => void
|
||
}
|
||
|
||
function systemMode(): ThemeMode {
|
||
return Appearance.getColorScheme() === 'light' ? 'light' : 'dark'
|
||
}
|
||
|
||
const ThemeContext = createContext<ThemeContextValue | null>(null)
|
||
|
||
export function ThemeProvider({ children }: { children: ReactNode }) {
|
||
const [mode, setModeState] = useState<PrefMode>('light')
|
||
const [sysScheme, setSysScheme] = useState<ThemeMode>(systemMode())
|
||
|
||
useEffect(() => {
|
||
let alive = true
|
||
AsyncStorage.getItem(MODE_KEY)
|
||
.then((v) => {
|
||
if (!alive) return
|
||
if (v === 'dark' || v === 'light' || v === 'system') setModeState(v)
|
||
})
|
||
.catch(() => { /* 접근 실패 무시 */ })
|
||
return () => { alive = false }
|
||
}, [])
|
||
|
||
useEffect(() => {
|
||
const sub = Appearance.addChangeListener(({ colorScheme }) => {
|
||
setSysScheme(colorScheme === 'light' ? 'light' : 'dark')
|
||
})
|
||
return () => sub.remove()
|
||
}, [])
|
||
|
||
const setMode = useCallback((m: PrefMode) => {
|
||
setModeState(m)
|
||
AsyncStorage.setItem(MODE_KEY, m).catch(() => { /* noop */ })
|
||
}, [])
|
||
|
||
const resolved: ThemeMode = mode === 'system' ? sysScheme : mode
|
||
|
||
const value = useMemo<ThemeContextValue>(
|
||
() => ({ t: makeTheme(resolved), resolved, mode, setMode }),
|
||
[resolved, mode, setMode]
|
||
)
|
||
|
||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||
}
|
||
|
||
/** 테마 훅. Provider 외부 호출 시 라이트 폴백(방어적). */
|
||
export function useTheme(): ThemeContextValue {
|
||
const ctx = useContext(ThemeContext)
|
||
if (ctx) return ctx
|
||
return { t: makeTheme('light'), resolved: 'light', mode: 'light', setMode: () => { /* noop */ } }
|
||
}
|