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-storage
  • dayjs
  • @notifee/react-native
  • react-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/native

npm

npm install @react-native-async-storage/async-storage dayjs @notifee/react-native react-native-toast-message @react-navigation/native

react 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.

Incluye cabecera de dependencias, imports y comentarios /** … */ por export (npm / React Native / tus módulos / integrado).

Idioma y tema

`changeLanguage`

src/utils/helper.tsx

Async: 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.tsx

Resuelve 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.tsx

Envuelve `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.tsx

Campos 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.tsx

Muestra 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.tsx

Igual 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.tsx

Lee 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.tsx

Vací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.tsx

Forma: `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.tsx

Si `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.tsx

Lee 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.tsx

Elimina 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.tsx

Devuelve 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.tsx

Saludo 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.tsx

Formatea `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.tsx

Mapea 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.tsx

iOS: 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.tsx

Wrapper 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.tsx

Envoltorio 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.tsx

Llama 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.tsx

Guarda 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.tsx

Color 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.tsx

Marcador: `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.tsx

Opciones 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.tsx

Construye 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.tsx

Atajo 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.tsx

En 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.tsx

Programa 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" },
  });
};

Patrocinado

Promoción breve