Helpers utilitaires de l’app
Référence d’un `helper.tsx` classique (ou `utils/helpers`) dans une app Expo/React Native, calqué sur un socle courant : Expo Router, AsyncStorage, i18next, dayjs, toasts et expo-notifications. Des noms comme `saveToSecureStorage` s’appuient ici sur AsyncStorage ; utilisez un stockage sécurisé pour les secrets. Des imports tels que `@/src/language-config/i18n` sont des exemples à adapter.
Paquets à installer
Installez ces paquets npm dans votre app Expo (expo install est préférable pour les modules natifs). Le helper.tsx téléchargé reprend la même liste et précise les dépendances par fonction.
@react-native-async-storage/async-storagedayjsexpo-deviceexpo-notificationsexpo-routerreact-native-toast-message
Recommandé
npx expo install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-messagenpm
npm install @react-native-async-storage/async-storage dayjs expo-device expo-notifications expo-router react-native-toast-messagereact et react-native sont fournis avec Expo.
À brancher dans votre projet
Adaptez les chemins i18n (`@/src/language-config/...`), `../constants/routes` et `../context/ThemeContext`. Optionnel : EXPO_PUBLIC_GOOGLE_API_KEY pour les cartes statiques.
Langue et thème
- `changeLanguage`
src/utils/helper.tsxAsync : enregistre la clé de langue (ex. `@language`) puis appelle `i18n.changeLanguage` pour rafraîchir les chaînes.
Utilise
- npm: @react-native-async-storage/async-storage
- Vos modules: @/src/language-config/i18n
export const changeLanguage = async (lang: string) => { await saveToSecureStorage("@language", lang); i18n.changeLanguage(lang); };- `getTheme`
src/utils/helper.tsxCalcule le thème effectif : si l’utilisateur a choisi `default`, suit le `ColorSchemeName` système ; sinon renvoie le `Theme` explicite du contexte.
Utilise
- React Native: ColorSchemeName
- Vos modules: ../context/ThemeContext (`Theme`)
export const getTheme = (theme: Theme, systemTheme: ColorSchemeName) => { if (theme === "default" && systemTheme === "dark") { return "dark"; } else if (theme === "default" && systemTheme === "light") { return "light"; } else { return theme; } };
Logs et toasts
- `devLog`
src/utils/helper.tsxEncapsule `console.log` pour ne journaliser que lorsque `__DEV__` est vrai.
Utilise
- Intégré: __DEV__ (Metro / React Native)
export const devLog = (...args: any[]) => { if (__DEV__) { console.log(...args); } };- `ShowToastOptions`
src/utils/helper.tsxChamps optionnels `text1`, `text2` et `type` (success | error) pour `showError` / `showSuccess` lorsque vous ne passez pas une simple chaîne.
Utilise
- Intégré: Type-only — pair with react-native-toast-message in showError / showSuccess
export interface ShowToastOptions { text1?: string; text2?: string; type?: "success" | "error"; }- `showError`
src/utils/helper.tsxAffiche un toast react-native-toast-message. Accepte une chaîne ou des options ; complète avec `getTextList().somethingWentWrong` si besoin.
Utilise
- npm: react-native-toast-message
- Vos modules: @/src/language-config/TextList (`getTextList`)
export const showError = (messageOrOptions: string | ShowToastOptions) => { let type = "error"; let text1 = ""; let text2 = getTextList().somethingWentWrong; if (typeof messageOrOptions === "string") { text2 = messageOrOptions || text2; } else { type = messageOrOptions.type || type; text1 = messageOrOptions.text1 || text1; text2 = messageOrOptions.text2 || text2; } Toast.show({ type, text1: `${text1}`, text2, }); };- `showSuccess`
src/utils/helper.tsxMême forme que `showError`, orientée succès.
Utilise
- npm: react-native-toast-message
- Vos modules: @/src/language-config/TextList (`getTextList`)
export const showSuccess = (messageOrOptions: string | ShowToastOptions) => { let type = "success"; let text1 = ""; let text2 = getTextList().somethingWentWrong; if (typeof messageOrOptions === "string") { text2 = messageOrOptions || text2; } else { type = messageOrOptions.type || type; text1 = messageOrOptions.text1 || text1; text2 = messageOrOptions.text2 || text2; } Toast.show({ type, text1: `${text1}`, text2, }); };
Persistance (AsyncStorage)
- `saveToSecureStorage`
src/utils/helper.tsx`AsyncStorage.setItem` avec try/catch et `devLog`. Le nom évoque le secure alors que l’exemple utilise AsyncStorage.
Utilise
- npm: @react-native-async-storage/async-storage
export async function saveToSecureStorage( key: string, value: string ): Promise<void> { try { await AsyncStorage.setItem(key, value); } catch (error) { devLog("[AsyncStorage] Error saving data:", error); } }- `getFromSecureStorage`
src/utils/helper.tsxLit une chaîne par clé ; renvoie `null` si absente ou en cas d’erreur.
Utilise
- npm: @react-native-async-storage/async-storage
export async function getFromSecureStorage( key: string ): Promise<string | null> { try { const value = await AsyncStorage.getItem(key); return value; } catch (error) { devLog("[AsyncStorage] Error retrieving data:", error); return null; } }- `removeKeysFromSecureStorage`
src/utils/helper.tsx`AsyncStorage.multiRemove` pour supprimer plusieurs clés (déconnexion, désactivation du « se souvenir »…).
Utilise
- npm: @react-native-async-storage/async-storage
export async function removeKeysFromSecureStorage(key: string[]) { try { await AsyncStorage.multiRemove(key); } catch (error) { devLog("[AsyncStorage] Error removing data:", error); return null; } }- `removeAllKeysFromSecureStorage`
src/utils/helper.tsxVide tout AsyncStorage—rarement, pour reset complet.
Utilise
- npm: @react-native-async-storage/async-storage
export async function removeAllKeysFromSecureStorage() { try { await AsyncStorage.clear(); } catch (error) { devLog("[AsyncStorage] Error removing all data:", error); return null; } }
Se souvenir de moi
- `RememberMeCredentials`
src/utils/helper.tsxForme : `email`, `password`, `rememberMe`. Stocker un mot de passe en AsyncStorage est risqué en production ; préférez jetons ou coffre sécurisé.
Utilise
- Intégré: Type-only — no runtime imports
export interface RememberMeCredentials { email: string; password: string; rememberMe: boolean; }- `saveRememberMeCredentials`
src/utils/helper.tsxSi `rememberMe` est vrai, enregistre email/mot de passe/`@remember_me` ; sinon supprime ces clés.
Utilise
- npm: @react-native-async-storage/async-storage
- Note: Uses saveToSecureStorage / removeKeysFromSecureStorage
export const saveRememberMeCredentials = async (credentials: RememberMeCredentials): Promise<void> => { try { if (credentials.rememberMe) { await saveToSecureStorage("@remembered_email", credentials.email); await saveToSecureStorage("@remembered_password", credentials.password); await saveToSecureStorage("@remember_me", "true"); } else { await removeKeysFromSecureStorage([ "@remembered_email", "@remembered_password", "@remember_me", ]); } } catch (error) { devLog("[Remember Me] Error saving credentials:", error); } };- `loadRememberMeCredentials`
src/utils/helper.tsxLit les identifiants si `@remember_me` vaut la chaîne true ; sinon valeurs vides et rememberMe faux.
Utilise
- npm: @react-native-async-storage/async-storage
export const loadRememberMeCredentials = async (): Promise<RememberMeCredentials> => { try { const savedEmail = await getFromSecureStorage("@remembered_email"); const savedPassword = await getFromSecureStorage("@remembered_password"); const rememberMe = await getFromSecureStorage("@remember_me"); if (savedEmail && savedPassword && rememberMe === "true") { return { email: savedEmail, password: savedPassword, rememberMe: true, }; } return { email: "", password: "", rememberMe: false, }; } catch (error) { devLog("[Remember Me] Error loading credentials:", error); return { email: "", password: "", rememberMe: false, }; } };- `clearRememberMeCredentials`
src/utils/helper.tsxSupprime uniquement les trois clés « se souvenir ».
Utilise
- npm: @react-native-async-storage/async-storage
export const clearRememberMeCredentials = async (): Promise<void> => { try { await removeKeysFromSecureStorage([ "@remembered_email", "@remembered_password", "@remember_me", ]); } catch (error) { devLog("[Remember Me] Error clearing credentials:", error); } };
Texte, dates et devise
- `capitalizeFirstLetter`
src/utils/helper.tsxRenvoie une chaîne vide si l’entrée est vide ; sinon première lettre en majuscule.
Utilise
- Intégré: No packages
export const capitalizeFirstLetter = (text: string): string => { if (!text) return ""; return text.charAt(0).toUpperCase() + text.slice(1); };- `getTimeOfDay`
src/utils/helper.tsxCréneau horaire (matin/après-midi/soir/nuit) avec libellés localisés via `getTextList()`.
Utilise
- Vos modules: @/src/language-config/TextList (`getTextList`)
export const getTimeOfDay = () => { const currentHour = new Date().getHours(); if (currentHour >= 5 && currentHour < 12) { return getTextList().morning; } else if (currentHour >= 12 && currentHour < 17) { return getTextList().afternoon; } else if (currentHour >= 17 && currentHour < 21) { return getTextList().evening; } else { return getTextList().night; } };- `formatDate`
src/utils/helper.tsxFormate une `Date` ou une chaîne avec dayjs selon le motif (ex. YYYY-MM-DD).
Utilise
- npm: dayjs
export const formatDate = (date: string | Date, format: string): string => { return dayjs(date).format(format); };- `formatCurrency`
src/utils/helper.tsx`Intl.NumberFormat` en mode devise, 2 décimales, GBP par défaut si manquant.
Utilise
- Intégré: Intl.NumberFormat
export const formatCurrency = (amount: number, currency: string) => { return new Intl.NumberFormat("en-US", { style: "currency", currency: currency || "GBP", minimumFractionDigits: 2, maximumFractionDigits: 2, }).format(amount); };- `getCurrencySymbol`
src/utils/helper.tsxAssocie codes ISO et symboles ; repli £ si absent ou inconnu.
Utilise
- Intégré: Intl (standard JS)
export const getCurrencySymbol = (currencyCode: string | null | undefined): string => { const normalizedCurrencyCode = currencyCode?.trim().toUpperCase(); if (!normalizedCurrencyCode) { return "£"; } const currencyMap: Record<string, string> = { USD: "$", GBP: "£", EUR: "€", INR: "₹", JPY: "¥", CNY: "¥", AUD: "A$", CAD: "C$", CHF: "CHF", SEK: "kr", NOK: "kr", DKK: "kr", PLN: "zł", RUB: "₽", BRL: "R$", MXN: "$", ZAR: "R", SGD: "S$", HKD: "HK$", NZD: "NZ$", KRW: "₩", TRY: "₺", AED: "د.إ", SAR: "﷼", PKR: "₨", BDT: "৳", THB: "฿", MYR: "RM", IDR: "Rp", PHP: "₱", VND: "₫", }; return currencyMap[normalizedCurrencyCode] || "£"; };
Linking et réglages
- `openAppSettings`
src/utils/helper.tsxiOS : `app-settings:` ; Android : `Linking.openSettings()` vers les réglages de l’app.
Utilise
- React Native: Linking, Platform
export const openAppSettings = () => { if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else { Linking.openSettings(); } };- `openWebUrl`
src/utils/helper.tsxFine couche sur `Linking.openURL` pour liens web ou deep links.
Utilise
- React Native: Linking
export const openWebUrl = (url: string) => { Linking.openURL(url); };
Navigation Expo Router
- `navigate`
src/utils/helper.tsxEnveloppe `router.push` : `pathname`, `params` optionnel et `search` pour Expo Router.
Utilise
- npm: expo-router
type RelativePathString = string; type NavigateOptions = { params?: Record<string, string>; search?: Record<string, string | number | boolean | undefined>; }; export const navigate = ( route: RelativePathString | any, options?: NavigateOptions ) => { router.push({ pathname: route, ...(options?.params && { params: options.params }), ...(options?.search && { search: options.search }), }); };- `navigateBack`
src/utils/helper.tsxAppelle `router.back()` pour revenir dans la pile.
Utilise
- npm: expo-router
export const navigateBack = () => { router.back(); };- `updateUser`
src/utils/helper.tsxEnregistre jetons d’accès/refresh, met à jour l’utilisateur via `setUser`, puis navigue vers une route ou la racine des onglets.
Utilise
- npm: @react-native-async-storage/async-storage, expo-router
- Vos modules: ../constants/routes · `navigate` in same file
- Note: Expects API shape with data.access_token, data.refresh_token, data.user
export const updateUser = async ( data: any, setUser: (user: any) => void, route?: any ) => { await saveToSecureStorage("@access_token", data?.data?.access_token); await saveToSecureStorage("@refresh_token", data?.data?.refresh_token); setUser(data?.data?.user); if (route) { navigate(route); } else { navigate(Routes.TABS.ROOT); } };
Données UI
- `getConsistentColor`
src/utils/helper.tsxCouleur pastel stable à partir d’un id (hash simple sur une palette)—avatars ou badges.
Utilise
- Intégré: No packages
export const getConsistentColor = (reviewId: string) => { const colors = [ "#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7", "#DDA0DD", "#98D8C8", "#F7DC6F", "#BB8FCE", "#85C1E9", "#F8C471", "#82E0AA", "#F1948A", "#85C1E9", "#D7BDE2", ]; let hash = 0; for (let i = 0; i < reviewId.length; i++) { hash = reviewId.charCodeAt(i) + ((hash << 5) - hash); } return colors[Math.abs(hash) % colors.length]; };
Google Static Maps
- `MapMarker`
src/utils/helper.tsxMarqueur : `lat`/`lng` requis ; `name`, `color`, `label` optionnels pour Static Maps.
Utilise
- Intégré: Type-only
export interface MapMarker { name?: string; lat: number; lng: number; color?: string; label?: string; }- `StaticMapOptions`
src/utils/helper.tsxOptions de carte statique : centre, zoom, taille, type, marqueurs, échelle, format.
Utilise
- Intégré: Type-only
export interface StaticMapOptions { center?: { lat: number; lng: number }; zoom?: number; size?: string; maptype?: "roadmap" | "satellite" | "terrain" | "hybrid"; markers?: MapMarker[]; scale?: 1 | 2; format?: "png" | "jpg" | "gif"; }- `generateStaticMapUrl`
src/utils/helper.tsxConstruit l’URL Google Static Maps ; lit `EXPO_PUBLIC_GOOGLE_API_KEY` ; chaîne vide + log si absente.
Utilise
- Intégré: EXPO_PUBLIC_GOOGLE_API_KEY (env), global URL / encodeURIComponent
- Note: Uses Google Maps Static API
export const generateStaticMapUrl = (options: StaticMapOptions): string => { const { center, zoom = 13, size = "600x200", maptype = "roadmap", markers = [], scale = 1, format = "png", } = options; const apiKey = process.env.EXPO_PUBLIC_GOOGLE_API_KEY; if (!apiKey) { devLog("Warning: Google Maps API key not found"); return ""; } const baseUrl = "https://maps.googleapis.com/maps/api/staticmap"; const params: string[] = []; if (center) { params.push(`center=${center.lat},${center.lng}`); } else if (markers.length > 0) { params.push(`center=${markers[0].lat},${markers[0].lng}`); } params.push(`zoom=${zoom}`); params.push(`size=${size}`); params.push(`maptype=${maptype}`); params.push(`scale=${scale}`); params.push(`format=${format}`); if (markers.length > 0) { markers.forEach((marker) => { const markerParams: string[] = []; if (marker.color) { markerParams.push(`color:${marker.color}`); } if (marker.label) { markerParams.push(`label:${marker.label}`); } markerParams.push(`${marker.lat},${marker.lng}`); const markerString = markerParams.join("|"); params.push(`markers=${encodeURIComponent(markerString)}`); }); } params.push(`key=${apiKey}`); const finalUrl = `${baseUrl}?${params.join("&")}`; devLog("Generated Static Map URL:", finalUrl); return finalUrl; };- `generateStaticMapUrlWithMarker`
src/utils/helper.tsxRaccourci avec un marqueur rouge et le reste des options.
Utilise
- Intégré: Builds on generateStaticMapUrl
export const generateStaticMapUrlWithMarker = ( name: string, lat: number, lng: number, options?: Omit<StaticMapOptions, "markers" | "center"> ): string => { return generateStaticMapUrl({ center: { lat, lng }, markers: [{ name, lat, lng, color: "red" }], ...options, }); };
Notifications locales
- `initializeNotifications`
src/utils/helper.tsxSur appareil réel, demande la permission ; sur Android, canal default en importance max (expo-notifications).
Utilise
- npm: expo-notifications, expo-device
- React Native: Platform
export const initializeNotifications = async () => { if (Device.isDevice) { const { status } = await Notifications.requestPermissionsAsync(); if (status !== "granted") { devLog("Permission for notifications not granted!"); return; } } if (Platform.OS === "android") { await Notifications.setNotificationChannelAsync("default", { name: "default", importance: Notifications.AndroidImportance.MAX, }); } };- `showNotification`
src/utils/helper.tsxPlanifie une notification locale immédiate (`trigger: null`) avec titre et corps.
Utilise
- npm: expo-notifications
export const showNotification = async (title: string, body: string) => { await Notifications.scheduleNotificationAsync({ content: { title, body }, trigger: null, }); };