itsm_ysm/mobile/constants/ThemeContext.tsx
itms-merge-dev efc3e66635 feat(itms): 모바일 앱 신규 (Expo, 12화면, OAuth password grant)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 17:10:46 +09:00

69 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 */ } }
}