FarmerHelp / frontend /src /context /AppContext.jsx
Gankit12's picture
Res2
fc91a5e
Raw
History Blame Contribute Delete
4.92 kB
/* 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>;
}