Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
Binary file added .DS_Store
Binary file not shown.
14 changes: 14 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Google Maps API Configuration
GOOGLE_API_KEY=AIzaSyBwv7wdSDYKdl4rZChr3lKEjrRgw7Ysj7Y

# Backend API Configuration
BACKEND_API_URL=http://localhost:5000

# Firebase Configuration
FIREBASE_API_KEY=your_firebase_api_key_here
FIREBASE_PROJECT_ID=your_firebase_project_id_here
FIREBASE_MESSAGING_SENDER_ID=your_sender_id_here
FIREBASE_APP_ID=your_app_id_here

# ElevenLabs Configuration
ELEVENLABS_API_KEY=sk_e9f9328b52d104a28d877639debf2c9cd23e0e3fa911a5c0
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
node_modules
node_modules
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"wca.enable": false
}
14 changes: 12 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,24 @@
"**/*"
],
"ios": {
"supportsTablet": true
"supportsTablet": true,
"infoPlist": {
"NSLocationAlwaysAndWhenInUseUsageDescription": "SafeCircle needs location access to track your safety and notify caregivers when you leave safe zones.",
"NSLocationWhenInUseUsageDescription": "SafeCircle needs location access to track your safety and notify caregivers when you leave safe zones.",
"NSLocationAlwaysUsageDescription": "SafeCircle needs continuous location access to provide safety monitoring even when the app is in the background."
}
},
"android": {
"adaptiveIcon": {

"backgroundColor": "#ffffff"
},
"package": "com.safecircle.app"
"package": "com.safecircle.app",
"permissions": [
"ACCESS_FINE_LOCATION",
"ACCESS_COARSE_LOCATION",
"ACCESS_BACKGROUND_LOCATION"
]
},
"extra": {
"eas": {
Expand Down
Binary file modified assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 99 additions & 19 deletions components/Alerts/NotificationCard.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,108 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const NotificationCard = ({ type, message, time }) => {
const NotificationCard = ({ type, message, time, distance }) => {
const isSOS = type === 'SOS';
const color = isSOS ? '#ff4d4f' : '#52c41a';
const bg = isSOS ? '#fff5f5' : '#f6ffed';
const isReturned = type === 'SafeZone';

const getCardStyle = () => {
if (isSOS) {
return [styles.card, styles.sosCard];
} else if (isReturned) {
return [styles.card, styles.safeCard];
} else {
return [styles.card, styles.defaultCard];
}
};

const getIconAndTitle = () => {
if (isSOS) {
return { icon: '🚨', title: 'Alert' };
} else if (isReturned) {
return { icon: '✅', title: 'Safe' };
} else {
return { icon: 'ℹ️', title: 'Info' };
}
};

const { icon, title } = getIconAndTitle();

return (
<div
className="w-72 p-4 rounded-xl shadow-lg transition transform hover:scale-105"
style={{
borderLeft: `6px solid ${color}`,
backgroundColor: bg,
}}
>
<div className="flex justify-between items-center">
<span className="font-semibold text-gray-800">
{isSOS ? '🚨 Alert' : '✅ Safe'}
</span>
<span className="text-xs text-gray-400">{time}</span>
</div>
<p className="text-sm text-gray-700 mt-1">{message}</p>
</div>
<View style={getCardStyle()}>
<View style={styles.header}>
<Text style={styles.titleText}>
{icon} {title}
</Text>
<Text style={styles.timeText}>{time}</Text>
</View>

<Text style={styles.messageText}>{message}</Text>

{distance && (
<Text style={styles.distanceText}>
Distance: {distance}m from safe zone
</Text>
)}
</View>
);
};

export default NotificationCard;
const styles = StyleSheet.create({
card: {
width: 280,
padding: 16,
borderRadius: 12,
marginVertical: 8,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
sosCard: {
backgroundColor: '#fff5f5',
borderLeftWidth: 6,
borderLeftColor: '#ff4d4f',
},
safeCard: {
backgroundColor: '#f6ffed',
borderLeftWidth: 6,
borderLeftColor: '#52c41a',
},
defaultCard: {
backgroundColor: '#f0f8ff',
borderLeftWidth: 6,
borderLeftColor: '#1890ff',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
titleText: {
fontSize: 16,
fontWeight: '600',
color: '#333',
},
timeText: {
fontSize: 12,
color: '#999',
},
messageText: {
fontSize: 14,
color: '#666',
lineHeight: 20,
},
distanceText: {
fontSize: 12,
color: '#ff4d4f',
fontWeight: '500',
marginTop: 4,
},
});

export default NotificationCard;
181 changes: 157 additions & 24 deletions components/Map/SafeZoneMap.jsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,189 @@
import React, { useState, useEffect } from 'react';
import { View, StyleSheet, Alert } from 'react-native';
import MapView, { Marker, Circle } from 'react-native-maps';
import NotificationCard from '../Alerts/NotificationCard';
import locationService from '../../services/locationService';

const SafeZoneMap = ({ userLocation, safeZoneCenter, safeZoneRadius }) => {
const SafeZoneMap = ({
userLocation,
safeZoneCenter,
safeZoneRadius = 100,
onLocationUpdate,
showUserLocation = true
}) => {
const [alert, setAlert] = useState(null);
const [currentLocation, setCurrentLocation] = useState(userLocation);

// Calculate distance between user and safe zone
// Calculate distance between user and safe zone using locationService
const getDistance = (loc1, loc2) => {
const R = 6371e3; // meters
const lat1 = loc1.lat * Math.PI / 180;
const lat2 = loc2.lat * Math.PI / 180;
const deltaLat = (loc2.lat - loc1.lat) * Math.PI / 180;
const deltaLng = (loc2.lng - loc1.lng) * Math.PI / 180;
const a =
Math.sin(deltaLat / 2) ** 2 +
Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLng / 2) ** 2;
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // in meters
if (!loc1 || !loc2) return 0;
return locationService.calculateDistance(
loc1.latitude,
loc1.longitude,
loc2.latitude,
loc2.longitude
);
};

// Handle location updates
useEffect(() => {
if (!userLocation || !safeZoneCenter) return;
const distance = getDistance(userLocation, safeZoneCenter);
const unsubscribe = locationService.addLocationListener((location) => {
setCurrentLocation(location);
if (onLocationUpdate) {
onLocationUpdate(location);
}
});

return unsubscribe;
}, [onLocationUpdate]);

// Monitor safe zone status
useEffect(() => {
if (!currentLocation || !safeZoneCenter) return;

const distance = getDistance(currentLocation, safeZoneCenter);

if (distance > safeZoneRadius) {
setAlert({
const newAlert = {
type: 'SOS',
message: 'User has exited the safe zone!',
time: 'Just now',
});
distance: Math.round(distance),
};

setAlert(newAlert);

// Show native alert
Alert.alert(
'Safe Zone Alert',
`You have left your safe zone! You are ${Math.round(distance)}m away.`,
[{ text: 'OK' }]
);
} else if (alert?.type === 'SOS' && distance <= safeZoneRadius) {
setAlert({
const returnAlert = {
type: 'SafeZone',
message: 'User returned to the safe zone.',
time: 'Just now',
});
};

setAlert(returnAlert);

// Show return alert
Alert.alert(
'Safe Zone Alert',
'Welcome back! You have returned to your safe zone.',
[{ text: 'OK' }]
);
}
}, [userLocation]);
}, [currentLocation, safeZoneCenter, safeZoneRadius, alert]);

// Default region for map
const getMapRegion = () => {
if (currentLocation) {
return {
latitude: currentLocation.latitude,
longitude: currentLocation.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
} else if (safeZoneCenter) {
return {
latitude: safeZoneCenter.latitude,
longitude: safeZoneCenter.longitude,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
} else {
// Default to a general location
return {
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
};
}
};

return (
<div className="relative">
{/* Google Map here */}
<View style={styles.container}>
<MapView
style={styles.map}
region={getMapRegion()}
showsUserLocation={showUserLocation}
showsMyLocationButton={true}
toolbarEnabled={false}
>
{/* Safe Zone Circle */}
{safeZoneCenter && (
<>
<Circle
center={{
latitude: safeZoneCenter.latitude,
longitude: safeZoneCenter.longitude,
}}
radius={safeZoneRadius}
strokeColor="rgba(0, 150, 255, 0.8)"
fillColor="rgba(0, 150, 255, 0.2)"
strokeWidth={2}
/>

{/* Safe Zone Center Marker */}
<Marker
coordinate={{
latitude: safeZoneCenter.latitude,
longitude: safeZoneCenter.longitude,
}}
title="Safe Zone Center"
description={`Radius: ${safeZoneRadius}m`}
pinColor="blue"
/>
</>
)}

{/* Current Location Marker */}
{currentLocation && (
<Marker
coordinate={{
latitude: currentLocation.latitude,
longitude: currentLocation.longitude,
}}
title="Your Location"
description="Current position"
pinColor="red"
/>
)}
</MapView>

{/* Alert Notification */}
{alert && (
<div className="absolute top-4 right-4 z-50">
<View style={styles.alertContainer}>
<NotificationCard
type={alert.type}
message={alert.message}
time={alert.time}
distance={alert.distance}
/>
</div>
</View>
)}
</div>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
position: 'relative',
},
map: {
flex: 1,
},
alertContainer: {
position: 'absolute',
top: 20,
right: 20,
zIndex: 1000,
elevation: 1000, // For Android
},
});

export default SafeZoneMap;
Loading