refactor(ui): modulariza App.jsx y unifica branding y caché PWA
- Extrae App.jsx en módulos reutilizables: constants/ (categorias, provincias), utils/geo, styles/shared, hooks/usePwaInstall y components/ (StarRating, LocalCard, CampoUbicacion, BannerMovil, CategoriaBadge) - AdminPanel reutiliza constantes y componentes compartidos y corrige la limpieza de suscripciones de Firebase al desmontar - Añade logos de marca (logo-localesp.png + @2x) y regenera los iconos PWA con tamaños menores - sw.js: sube la caché del shell a v2 e incluye los nuevos iconos y logos
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
// Detecta si la web se está ejecutando ya como app instalada (modo standalone),
|
||||
// tanto en Chrome/Android (display-mode: standalone) como en Safari/iOS
|
||||
// (navigator.standalone) o dentro de una TWA de Android (referrer android-app://).
|
||||
function calcularSiEsStandalone() {
|
||||
if (typeof window === "undefined") return false;
|
||||
const enModoStandalone = window.matchMedia?.("(display-mode: standalone)")?.matches ?? false;
|
||||
const enIosStandalone = window.navigator?.standalone === true;
|
||||
const enTwaAndroid = document.referrer?.startsWith("android-app://") ?? false;
|
||||
return enModoStandalone || enIosStandalone || enTwaAndroid;
|
||||
}
|
||||
|
||||
function detectarIos() {
|
||||
if (typeof window === "undefined") return false;
|
||||
return /iphone|ipad|ipod/i.test(window.navigator.userAgent) && !window.MSStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook que centraliza el estado de instalación de la PWA:
|
||||
* - isInstalled: la app ya se está ejecutando en modo standalone (o el
|
||||
* navegador confirma que está instalada mediante getInstalledRelatedApps).
|
||||
* - canPromptInstall: el navegador ha ofrecido el evento nativo `beforeinstallprompt`,
|
||||
* así que se puede lanzar el diálogo de instalación con promptInstall().
|
||||
* - isIos: Safari/iOS no soporta beforeinstallprompt, así que ahí solo cabe
|
||||
* mostrar instrucciones manuales ("Compartir → Añadir a pantalla de inicio").
|
||||
*/
|
||||
export default function usePwaInstall() {
|
||||
const [installPromptEvent, setInstallPromptEvent] = useState(null);
|
||||
const [isInstalled, setIsInstalled] = useState(calcularSiEsStandalone);
|
||||
const [isIos] = useState(detectarIos);
|
||||
|
||||
useEffect(() => {
|
||||
const alCapturarPrompt = (evento) => {
|
||||
evento.preventDefault();
|
||||
setInstallPromptEvent(evento);
|
||||
};
|
||||
const alInstalar = () => {
|
||||
setInstallPromptEvent(null);
|
||||
setIsInstalled(true);
|
||||
};
|
||||
|
||||
window.addEventListener("beforeinstallprompt", alCapturarPrompt);
|
||||
window.addEventListener("appinstalled", alInstalar);
|
||||
|
||||
const consultaDisplayMode = window.matchMedia?.("(display-mode: standalone)");
|
||||
const alCambiarDisplayMode = () => setIsInstalled(calcularSiEsStandalone());
|
||||
consultaDisplayMode?.addEventListener?.("change", alCambiarDisplayMode);
|
||||
|
||||
// Progressive enhancement: en navegadores compatibles (Chrome/Android),
|
||||
// confirma si la PWA ya está instalada aunque se esté viendo en una pestaña normal.
|
||||
navigator.getInstalledRelatedApps?.()
|
||||
.then((apps) => { if (apps.length > 0) setIsInstalled(true); })
|
||||
.catch(() => {});
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("beforeinstallprompt", alCapturarPrompt);
|
||||
window.removeEventListener("appinstalled", alInstalar);
|
||||
consultaDisplayMode?.removeEventListener?.("change", alCambiarDisplayMode);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const promptInstall = useCallback(async () => {
|
||||
if (!installPromptEvent) return "unavailable";
|
||||
installPromptEvent.prompt();
|
||||
const { outcome } = await installPromptEvent.userChoice;
|
||||
setInstallPromptEvent(null);
|
||||
return outcome; // "accepted" | "dismissed"
|
||||
}, [installPromptEvent]);
|
||||
|
||||
return { isInstalled, canPromptInstall: Boolean(installPromptEvent), promptInstall, isIos };
|
||||
}
|
||||
Reference in New Issue
Block a user