Utilidades helper de la app
Referencia de un `helper.tsx` típico (o `utils/helpers`) en una app React Native CLI, basado en React Navigation, AsyncStorage, i18next, dayjs, toasts y @notifee/react-native. Nombres como `saveToSecureStorage` aquí usan AsyncStorage; cámbialo por almacenamiento seguro si manejas secretos. Rutas como `@/src/language-config/i18n` son ejemplos: adáptalas a tu proyecto.
Paquetes a instalar
Instala estos paquetes npm en tu app React Native CLI. Usa `npm install` (o tu gestor) y asegúrate de tener la configuración nativa necesaria para navegación/notificaciones.
@react-native-async-storage/async-storagedayjs@notifee/react-nativereact-native-toast-message@react-navigation/native
Recomendado
npm install @react-native-async-storage/async-storage dayjs @notifee/react-native react-native-toast-message @react-navigation/nativenpm
npm install @react-native-async-storage/async-storage dayjs @notifee/react-native react-native-toast-message @react-navigation/nativereact y react-native vienen por la configuración de tu app.
Conecta en tu proyecto
Ajusta rutas de i18n (`@/src/language-config/...`), `../constants/routes`, `../context/ThemeContext` y tu `../navigation/NavigationService` (`navigationRef`). Opcional: GOOGLE_API_KEY para mapas estáticos.
Idioma y tema
- `changeLanguage`
src/utils/helper.tsxAsync: guarda la clave de idioma (p. ej. `@language`) y llama `i18n.changeLanguage` para refrescar strings.
Usa
- npm: @react-native-async-storage/async-storage
- Tus módulos: @/src/language-config/i18n
export const changeLanguage = async (lang: string) => { await saveToSecureStorage("@language", lang); i18n.changeLanguage(lang); };- `getTheme`
src/utils/helper.tsxResuelve el tema efectivo: si el usuario eligió `default`, sigue el `ColorSchemeName` del sistema; si no, devuelve el `Theme` explícito del contexto.
Usa
- React Native: ColorSchemeName
- Tus módulos: ../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 y toasts
- `devLog`
src/utils/helper.tsxEnvuelve `console.log` para que solo registre cuando `__DEV__` es verdadero.
Usa
- Integrado: __DEV__ (Metro / React Native)
export const devLog = (...args: any[]) => { if (__DEV__) { console.log(...args); } };- `ShowToastOptions`
src/utils/helper.tsxCampos opcionales `text1`, `text2` y `type` (success | error) para `showError` / `showSuccess` cuando no pasas solo un string.
Usa
- Integrado: 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.tsxMuestra un toast de react-native-toast-message. Acepta string u opciones; rellena con `getTextList().somethingWentWrong` si faltan textos.
Usa
- npm: react-native-toast-message
- Tus módulos: @/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.tsxIgual que `showError` pero orientado a éxito (confirmaciones).
Usa
- npm: react-native-toast-message
- Tus módulos: @/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, }); };
Persistencia (AsyncStorage)
- `saveToSecureStorage`
src/utils/helper.tsx`AsyncStorage.setItem` con try/catch y `devLog`. El nombre sugiere secure, pero el ejemplo usa AsyncStorage.
Usa
- 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.tsxLee un string por clave; devuelve `null` si no hay valor o hay error.
Usa
- 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` para borrar varias claves (logout, desactivar recordarme…).
Usa
- 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.tsxVacía todo AsyncStorage—solo para reset fuerte o cierre de sesión global.
Usa
- 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; } }
Recordarme
- `RememberMeCredentials`
src/utils/helper.tsxForma: `email`, `password`, `rememberMe`. Guardar contraseñas en AsyncStorage es arriesgado en producción; mejor tokens o almacén seguro.
Usa
- Integrado: Type-only — no runtime imports
export interface RememberMeCredentials { email: string; password: string; rememberMe: boolean; }- `saveRememberMeCredentials`
src/utils/helper.tsxSi `rememberMe` es true, guarda email/contraseña/`@remember_me`; si no, borra esas claves.
Usa
- npm: @react-native-async-storage/async-storage
- Nota: 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.tsxLee credenciales si `@remember_me` vale la cadena true; si no, devuelve vacío y rememberMe false.
Usa
- 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.tsxElimina solo las tres claves de recordarme.
Usa
- 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); } };
Texto, fechas y moneda
- `capitalizeFirstLetter`
src/utils/helper.tsxDevuelve cadena vacía si no hay texto; si no, mayúscula inicial y resto igual.
Usa
- Integrado: No packages
export const capitalizeFirstLetter = (text: string): string => { if (!text) return ""; return text.charAt(0).toUpperCase() + text.slice(1); };- `getTimeOfDay`
src/utils/helper.tsxSaludo por franja horaria (mañana/tarde/tarde-noche/noche) con strings localizados de `getTextList()`.
Usa
- Tus módulos: @/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.tsxFormatea `Date` o string con dayjs según el patrón (ej. YYYY-MM-DD).
Usa
- npm: dayjs
export const formatDate = (date: string | Date, format: string): string => { return dayjs(date).format(format); };- `formatCurrency`
src/utils/helper.tsx`Intl.NumberFormat` en modo moneda con 2 decimales; moneda por defecto GBP si falta.
Usa
- Integrado: 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.tsxMapea códigos ISO a símbolos; si falta o es desconocido, cae en £.
Usa
- Integrado: 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 y ajustes
- `openAppSettings`
src/utils/helper.tsxiOS: URL `app-settings:`; Android: `Linking.openSettings()` (pantalla de la app).
Usa
- React Native: Linking, Platform
export const openAppSettings = () => { if (Platform.OS === "ios") { Linking.openURL("app-settings:"); } else { Linking.openSettings(); } };- `openWebUrl`
src/utils/helper.tsxWrapper de `Linking.openURL` para URLs web o deep links.
Usa
- React Native: Linking
export const openWebUrl = (url: string) => { Linking.openURL(url); };
Navegación con React Navigation
- `navigate`
src/utils/helper.tsxEnvoltorio de `navigate` para React Navigation usando un `navigationRef` global. Pasa `params` (y mezcla `search` dentro del mismo objeto para comodidad).
Usa
- npm: @react-navigation/native
- Tus módulos: ../navigation/NavigationService (`navigationRef`)
type NavigateOptions = { params?: Record<string, string>; search?: Record<string, string | number | boolean | undefined>; }; export const navigate = ( route: string | any, options?: NavigateOptions ) => { const nav = navigationRef?.current; if (!nav) { devLog("Navigation not ready"); return; } nav.navigate(route as never, { ...(options?.params ?? {}), // React Navigation doesn't have an exact "search" concept like Expo Router. ...(options?.search ?? {}), } as never); };- `navigateBack`
src/utils/helper.tsxLlama a `navigationRef.current.goBack()` para hacer pop en el stack.
Usa
- npm: @react-navigation/native
- Tus módulos: ../navigation/NavigationService (`navigationRef`)
export const navigateBack = () => { navigationRef?.current?.goBack(); };- `updateUser`
src/utils/helper.tsxGuarda tokens de acceso/refresh, actualiza usuario con `setUser` y navega a una ruta o al root de tabs.
Usa
- npm: @react-native-async-storage/async-storage, @react-navigation/native
- Tus módulos: ../constants/routes · `navigate` in same file
- Nota: 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); } };
Datos para UI
- `getConsistentColor`
src/utils/helper.tsxColor pastel estable a partir de un id (hash simple sobre una paleta fija)—avatars o etiquetas.
Usa
- Integrado: 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.tsxMarcador: `lat`/`lng` obligatorios; `name`, `color`, `label` opcionales para Static Maps.
Usa
- Integrado: Type-only
export interface MapMarker { name?: string; lat: number; lng: number; color?: string; label?: string; }- `StaticMapOptions`
src/utils/helper.tsxOpciones de URL estática: centro, zoom, tamaño, tipo de mapa, marcadores, escala, formato.
Usa
- Integrado: 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.tsxConstruye URL de Google Static Maps; usa `process.env.GOOGLE_API_KEY`; si falta, devuelve cadena vacía y registra aviso.
Usa
- Integrado: GOOGLE_API_KEY (env), global URL / encodeURIComponent
- Nota: 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.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.tsxAtajo con un marcador rojo centrado y el resto de opciones.
Usa
- Integrado: 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, }); };
Notificaciones locales
- `initializeNotifications`
src/utils/helper.tsxEn dispositivo real pide permiso; en Android crea canal `default` usando **@notifee/react-native**.
Usa
- npm: @notifee/react-native
- React Native: Platform
export const initializeNotifications = async () => { const settings = await notifee.requestPermission(); if (settings.authorizationStatus < 1) { devLog("Permission for notifications not granted!"); return; } if (Platform.OS === "android") { await notifee.createChannel({ id: "default", name: "default", importance: AndroidImportance.HIGH, }); } };- `showNotification`
src/utils/helper.tsxPrograma notificación local inmediata (`trigger: null`) con título y cuerpo.
Usa
- npm: @notifee/react-native
export const showNotification = async (title: string, body: string) => { await notifee.displayNotification({ title, body, android: { channelId: "default" }, }); };