feat: notificaciones push, APK nativo, cleartext traffic habilitado
This commit is contained in:
@@ -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' },
|
||||
|
||||
@@ -20,10 +20,16 @@
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"edgeToEdgeEnabled": true
|
||||
"edgeToEdgeEnabled": true,
|
||||
"package": "com.hackonlinces.basuraapp"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
},
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "4aec7244-5fe6-49e9-90fd-e57f93d91073"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user