Spaces:
Sleeping
Sleeping
| /* eslint-disable react-refresh/only-export-components */ | |
| /** | |
| * AppContext - Global application state. | |
| * | |
| * Manages: | |
| * - Language preference (English / Hindi / Gujarati) with persistence | |
| * - User preferences (notifications, etc.) | |
| * - Global loading state | |
| * - Global error state | |
| */ | |
| import { createContext, useReducer, useEffect, useCallback } from "react"; | |
| import { useTranslation } from "react-i18next"; | |
| import storage from "@utils/storage"; | |
| import { | |
| STORAGE_KEYS, | |
| LANGUAGES, | |
| DEFAULT_LANGUAGE, | |
| } from "@utils/constants"; | |
| // --------------------------------------------------------------------------- | |
| // Initial State | |
| // --------------------------------------------------------------------------- | |
| function loadPersistedPreferences() { | |
| return storage.get(STORAGE_KEYS.USER_PREFERENCES, { | |
| notifications: true, | |
| }); | |
| } | |
| function buildInitialState() { | |
| return { | |
| language: storage.get(STORAGE_KEYS.LANGUAGE, DEFAULT_LANGUAGE), | |
| preferences: loadPersistedPreferences(), | |
| loading: false, | |
| loadingMessage: "", | |
| error: null, | |
| }; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Action Types | |
| // --------------------------------------------------------------------------- | |
| const ACTION_TYPES = { | |
| SET_LANGUAGE: "SET_LANGUAGE", | |
| SET_PREFERENCES: "SET_PREFERENCES", | |
| SET_LOADING: "SET_LOADING", | |
| SET_ERROR: "SET_ERROR", | |
| CLEAR_ERROR: "CLEAR_ERROR", | |
| RESET: "RESET", | |
| }; | |
| // --------------------------------------------------------------------------- | |
| // Reducer | |
| // --------------------------------------------------------------------------- | |
| function appReducer(state, action) { | |
| switch (action.type) { | |
| case ACTION_TYPES.SET_LANGUAGE: | |
| return { ...state, language: action.payload }; | |
| case ACTION_TYPES.SET_PREFERENCES: | |
| return { | |
| ...state, | |
| preferences: { ...state.preferences, ...action.payload }, | |
| }; | |
| case ACTION_TYPES.SET_LOADING: | |
| return { | |
| ...state, | |
| loading: action.payload.loading, | |
| loadingMessage: action.payload.message || "", | |
| }; | |
| case ACTION_TYPES.SET_ERROR: | |
| return { ...state, error: action.payload, loading: false }; | |
| case ACTION_TYPES.CLEAR_ERROR: | |
| return { ...state, error: null }; | |
| case ACTION_TYPES.RESET: | |
| return buildInitialState(); | |
| default: | |
| return state; | |
| } | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Context | |
| // --------------------------------------------------------------------------- | |
| export const AppContext = createContext(null); | |
| // --------------------------------------------------------------------------- | |
| // Provider | |
| // --------------------------------------------------------------------------- | |
| export function AppProvider({ children }) { | |
| const [state, dispatch] = useReducer(appReducer, null, buildInitialState); | |
| const { i18n } = useTranslation(); | |
| // Persist language and sync with i18n whenever it changes | |
| useEffect(() => { | |
| storage.set(STORAGE_KEYS.LANGUAGE, state.language); | |
| document.documentElement.lang = state.language; | |
| if (i18n.language !== state.language) { | |
| i18n.changeLanguage(state.language); | |
| } | |
| }, [state.language, i18n]); | |
| // Persist preferences whenever they change | |
| useEffect(() => { | |
| storage.set(STORAGE_KEYS.USER_PREFERENCES, state.preferences); | |
| }, [state.preferences]); | |
| // --- Actions --------------------------------------------------------------- | |
| const setLanguage = useCallback((lang) => { | |
| const validLangs = Object.values(LANGUAGES); | |
| if (!validLangs.includes(lang)) { | |
| console.error( | |
| `[AppContext] Invalid language "${lang}". Expected one of: ${validLangs.join(", ")}`, | |
| ); | |
| return; | |
| } | |
| dispatch({ type: ACTION_TYPES.SET_LANGUAGE, payload: lang }); | |
| }, []); | |
| const actions = { | |
| setLanguage, | |
| toggleLanguage: () => { | |
| const langs = [LANGUAGES.EN, LANGUAGES.HI, LANGUAGES.GU]; | |
| const currentIndex = langs.indexOf(state.language); | |
| const nextIndex = (currentIndex + 1) % langs.length; | |
| dispatch({ type: ACTION_TYPES.SET_LANGUAGE, payload: langs[nextIndex] }); | |
| }, | |
| setPreferences: (prefs) => { | |
| dispatch({ type: ACTION_TYPES.SET_PREFERENCES, payload: prefs }); | |
| }, | |
| setLoading: (loading, message = "") => { | |
| dispatch({ | |
| type: ACTION_TYPES.SET_LOADING, | |
| payload: { loading, message }, | |
| }); | |
| }, | |
| setError: (error) => { | |
| dispatch({ type: ACTION_TYPES.SET_ERROR, payload: error }); | |
| }, | |
| clearError: () => { | |
| dispatch({ type: ACTION_TYPES.CLEAR_ERROR }); | |
| }, | |
| resetState: () => { | |
| storage.remove(STORAGE_KEYS.LANGUAGE); | |
| storage.remove(STORAGE_KEYS.USER_PREFERENCES); | |
| dispatch({ type: ACTION_TYPES.RESET }); | |
| }, | |
| }; | |
| const value = { ...state, ...actions }; | |
| return <AppContext.Provider value={value}>{children}</AppContext.Provider>; | |
| } | |