feat: notificaciones push, APK nativo, cleartext traffic habilitado

This commit is contained in:
2026-05-22 22:57:48 -06:00
parent 6407efad74
commit f72f274104
2 changed files with 60 additions and 8 deletions

View File

@@ -2,6 +2,8 @@ import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, ActivityIndicator, ScrollView, Alert, RefreshControl } from 'react-native';
import { useState, useEffect, useCallback } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
const API_URL = 'http://10.137.112.65:8000';
@@ -9,6 +11,13 @@ const COLONIAS = [
'Zona Centro', 'Las Arboledas', 'Trojes', 'San Juanico',
'Los Olivos', 'Rancho Seco', 'Las Insurgentes'
];
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
export default function App() {
const [screen, setScreen] = useState('splash');
@@ -27,7 +36,10 @@ export default function App() {
const [direccion, setDireccion] = useState('');
const [codigoPostal, setCodigoPostal] = useState('');
useEffect(() => { cargarSesion(); }, []);
useEffect(() => {
cargarSesion();
registrarNotificaciones();
}, []);
useEffect(() => {
if (screen === 'eta' && domicilioActivo && token) {
@@ -167,9 +179,29 @@ export default function App() {
headers: { 'Authorization': `Bearer ${t || token}` }
});
const data = await res.json();
if (data.mensaje) setEta(data);
if (data.mensaje) {
if (data.evento === 'TRUCK_PROXIMITY' && eta?.evento !== 'TRUCK_PROXIMITY') {
enviarNotificacionLocal(
'🚨 ¡Camión cercano!',
`El camión está a menos de 15 minutos de ${data.colonia}. Saca tus bolsas a la acera.`
);
}
if (data.evento === 'ROUTE_START' && eta?.evento !== 'ROUTE_START') {
enviarNotificacionLocal(
'🟢 Ruta iniciada',
`El camión de ${data.colonia} ha salido. Prepara tus residuos.`
);
}
if (data.evento === 'ROUTE_COMPLETED' && eta?.evento !== 'ROUTE_COMPLETED') {
enviarNotificacionLocal(
'✅ Servicio finalizado',
`El camión de ${data.colonia} ha concluido su jornada.`
);
}
setEta(data);
}
} catch { if (!silencioso) Alert.alert('Error', 'No se pudo obtener el ETA'); }
if (!silencioso) setLoading(false);
setLoading(false);
};
const onRefresh = useCallback(async () => {
@@ -178,10 +210,23 @@ export default function App() {
setRefreshing(false);
}, [domicilioActivo, token]);
const registrarNotificaciones = async () => {
if (!Device.isDevice) return;
const { status } = await Notifications.requestPermissionsAsync();
if (status !== 'granted') return;
};
const enviarNotificacionLocal = async (titulo, cuerpo) => {
await Notifications.scheduleNotificationAsync({
content: { title: titulo, body: cuerpo, sound: true },
trigger: null,
});
};
const seleccionarDomicilio = async (dom) => {
setDomicilioActivo(dom);
await AsyncStorage.setItem('domicilioId', String(dom.id));
consultarETA(dom.id, token);
consultarETA(dom.id, token, true);
};
if (screen === 'splash') return (
@@ -302,7 +347,8 @@ export default function App() {
{domicilios.length > 1 && (
<ScrollView horizontal showsHorizontalScrollIndicator={false}
style={{ width: '100%', marginBottom: 12 }}>
style={{ width: '100%', marginBottom: 12 }}
contentContainerStyle={{ alignItems: 'center', paddingVertical: 4 }}>
{domicilios.map(d => (
<TouchableOpacity key={d.id}
style={[styles.domicilioTab, domicilioActivo?.id === d.id && styles.domicilioTabActivo]}
@@ -426,8 +472,8 @@ const styles = StyleSheet.create({
domicilioChip: { padding: 12, borderRadius: 8, borderWidth: 1, borderColor: '#ddd', backgroundColor: '#fff', marginBottom: 6 },
domicilioChipActivo: { borderColor: '#1a7a4a', backgroundColor: '#e8f5ee' },
domicilioChipText: { fontSize: 13, color: '#333' },
domicilioTab: { paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20, borderWidth: 1, borderColor: '#ddd', backgroundColor: '#fff', marginRight: 8 },
domicilioTabActivo: { backgroundColor: '#1a7a4a', borderColor: '#1a7a4a' },
domicilioTab: { paddingHorizontal: 14, paddingVertical: 8, borderRadius: 20, borderWidth: 1, borderColor: '#ddd', backgroundColor: '#fff', marginRight: 8, height: 36, justifyContent: 'center' },
domicilioTabActivo: { backgroundColor: '#1a7a4a', borderColor: '#1a7a4a', height: 36 },
domicilioTabText: { fontSize: 13, color: '#555' },
domicilioTabTextActivo: { color: '#fff', fontWeight: '500' },
etaCard: { width: '100%', backgroundColor: '#fff', borderRadius: 16, padding: 20, marginBottom: 16, borderWidth: 1, borderColor: '#d0e8d8' },