diff --git a/app/_layout.jsx b/app/_layout.jsx
new file mode 100644
index 0000000..02305f1
--- /dev/null
+++ b/app/_layout.jsx
@@ -0,0 +1,59 @@
+import React, { useState, useEffect } from 'react';
+import { router, Stack } from 'expo-router';
+import { GlobalVariablesProvider, WebSocketProvider } from '../contexts';
+import { useNotifications, useWebSocketContext } from '@/hooks';
+
+export const unstable_settings = {
+ initialRouteName: 'login',
+};
+
+function AppTest() {
+ const [ oldMessage, setOldMessage ] = useState(1);
+ const { lastMessage } = useWebSocketContext();
+ const { schedulePushNotification } = useNotifications();
+
+ useEffect(() => {
+ router.replace('/login');
+ }, []);
+
+ const parseAddress = (data) => {
+ const { Address } = data;
+ const { StreetAddress, AddressApartment, Town, State } = Address;
+ const response = `${StreetAddress}${AddressApartment ? ` - ${AddressApartment}` : ''} ${Town}, ${State}`
+ return response;
+ }
+
+ useEffect(() => {
+ if (lastMessage) {
+ const parsedMessage = JSON?.parse(lastMessage);
+ if (parsedMessage?.data && new Date(oldMessage)?.getTime() < new Date(parsedMessage?.timestamp)?.getTime()) {
+ setOldMessage(new Date(parsedMessage?.timestamp)?.getTime());
+ schedulePushNotification(
+ `${parsedMessage?.data?.Response?.ServiceName} - ${parsedMessage?.data?.Incident?.IncNatureCode}`,
+ `${parsedMessage?.data?.Incident?.IncNature}\n${parseAddress(parsedMessage?.data)}`,
+ parsedMessage?.data
+ );
+ }
+ }
+ }, [lastMessage]);
+
+ return (
+
+
+
+ )
+}
+
+export default function App() {
+ return (
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/_layout.tsx b/app/_layout.tsx
deleted file mode 100644
index c723c1f..0000000
--- a/app/_layout.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React, { useEffect } from 'react';
-import { router, Stack } from 'expo-router';
-import { WebSocketProvider } from '../contexts/WebSocketContext';
-
-export const unstable_settings = {
- initialRouteName: 'login',
-};
-
-export default function App() {
-
- useEffect(() => {
- router.replace('/login');
- }, []);
-
- return (
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/app/incidents.jsx b/app/incidents.jsx
index c0cc707..936852b 100644
--- a/app/incidents.jsx
+++ b/app/incidents.jsx
@@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
-import { useCallFeed } from '../hooks/useCallFeed';
+import { useCallFeed } from '@/hooks';
import { router } from 'expo-router';
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
import { StatusBar } from 'expo-status-bar';
@@ -16,343 +16,343 @@ import ActionSheet from 'react-native-actions-sheet';
const DepartmentActionSheet = styled(ActionSheet)``;
export default function Incidents() {
- const actionSheetRef = useRef(null);
- const callFeed = useCallFeed();
+ const actionSheetRef = useRef(null);
+ const callFeed = useCallFeed();
- const {
- departments,
- callIconMap,
- callDetails,
- callColorSelector,
- formatCallTimePast,
- formatCallDateTime
- } = callFeed;
+ const {
+ departments,
+ callIconMap,
+ callDetails,
+ callColorSelector,
+ formatCallTimePast,
+ formatCallDateTime
+ } = callFeed;
- const sortedAndFilteredCalls = callDetails
- .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
- .filter((item, index, self) => {
- return index === self.findIndex(i => {
- return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}`
- });
- });
+ const sortedAndFilteredCalls = callDetails
+ .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
+ .filter((item, index, self) => {
+ return index === self.findIndex(i => {
+ return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}`
+ });
+ });
- const {
- departmentTypeMap,
- accountDetails,
- deptList,
- setDeptList,
- selectedDepartment,
- setSelectedDepartment,
- updateSelectedDepartment,
- selectedDepartmentColorPicker,
- } = departments;
+ const {
+ departmentTypeMap,
+ accountDetails,
+ deptList,
+ setDeptList,
+ selectedDepartment,
+ setSelectedDepartment,
+ updateSelectedDepartment,
+ selectedDepartmentColorPicker,
+ } = departments;
- return (
-
-
-
+
+
+ {
+ actionSheetRef.current?.show();
+ }}
+ >
+
+ {selectedDepartment?.deptAbv}
+
+
+
+
+
+
+
+
+ {sortedAndFilteredCalls?.length ? (
+ sortedAndFilteredCalls?.map((callItem, index) => {
+ const { data: call, timestamp } = callItem;
+ const { Incident, Address, Response } = call;
+ const {
+ ServiceNumber,
+ IncDate,
+ IncNature,
+ IncNatureCode,
+ IncNatureCodeDesc,
+ Status,
+ } = Incident;
+ const {
+ StreetAddress,
+ AddressApartment,
+ Town,
+ State,
+ LocationName,
+ } = Address;
+ const {
+ ServiceName
+ } = Response;
+ const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency;
+ return (
+ {
+ router.push({
+ pathname: '/call',
+ params: {
+ callDetails: btoa(JSON.stringify(call))
+ }
+ })
+ }}
>
-
+
+ {
- actionSheetRef.current?.show();
- }}
- >
-
+ {formatCallDateTime(IncDate)}
+
+ {formatCallTimePast(IncDate)}
+ {Status === 'CLOSED' ? (
+
+ ) : null}
+
+
+
+
+
+ {LocationName ? (
+
+ {`${LocationName}`}
+
+ ) : (
+
+
+ {`${StreetAddress}`}
+
+ {AddressApartment ? (
+
+ {` - ${AddressApartment}`}
+
+ ) : null }
+
+ {` ${Town}, ${State}`}
+
+
+ )}
+
+ {`${IncNature}`}
+
+
+
+ {`${IncNatureCodeDesc}`}
+
+
+
+
+
+
+
+
+
+
+
+ Service: {ServiceName}
+
+
+ {`Incident #: ${ServiceNumber}`}
+
+
+
+
+
+ )
+ })) : (
+ There are no Calls
+ ) }
+
+
+
+
+
+
+
+ {deptList?.map((item) => {
+ return (
+
+ {
+ actionSheetRef.current?.hide();
+ return updateSelectedDepartment(
+ selectedDepartment?.deptId,
+ item?.deptId
+ )
}}
>
- {selectedDepartment?.deptAbv}
-
-
-
-
-
-
-
-
- {sortedAndFilteredCalls?.length ? (
- sortedAndFilteredCalls?.map((callItem, index) => {
- const { data: call, timestamp } = callItem;
- const { Incident, Address, Response } = call;
- const {
- ServiceNumber,
- IncDate,
- IncNature,
- IncNatureCode,
- IncNatureCodeDesc,
- Status,
- } = Incident;
- const {
- StreetAddress,
- AddressApartment,
- Town,
- State,
- LocationName,
- } = Address;
- const {
- ServiceName
- } = Response;
- const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency;
- return (
- {
- router.push({
- pathname: '/call',
- params: {
- callDetails: btoa(JSON.stringify(call))
- }
- })
- }}
- >
-
+
-
-
- {formatCallDateTime(IncDate)}
-
- {formatCallTimePast(IncDate)}
- {Status === 'CLOSED' ? (
-
- ) : null}
-
-
-
-
-
- {LocationName ? (
-
- {`${LocationName}`}
-
- ) : (
-
-
- {`${StreetAddress}`}
-
- {AddressApartment ? (
-
- {` - ${AddressApartment}`}
-
- ) : null }
-
- {` ${Town}, ${State}`}
-
-
- )}
-
- {`${IncNature}`}
-
-
-
- {`${IncNatureCodeDesc}`}
-
-
-
-
-
-
-
-
-
-
-
- Service: {ServiceName}
-
-
- {`Incident #: ${ServiceNumber}`}
-
-
-
-
-
- )
- })) : (
- There are no Calls
- ) }
-
-
-
-
-
-
-
- {deptList?.map((item) => {
- return (
-
- {
- actionSheetRef.current?.hide();
- return updateSelectedDepartment(
- selectedDepartment?.deptId,
- item?.deptId
- )
- }}
- >
-
-
- {item?.dept}
-
- {item?.primary ? : null}
-
- {`${item?.deptAbv} - ${departmentTypeMap[item?.type]}`}
-
+ {item?.dept}
+
+ {item?.primary ? : null}
- );
- })}
-
-
-
- )
+ {`${item?.deptAbv} - ${departmentTypeMap[item?.type]}`}
+
+
+ );
+ })}
+
+
+
+ )
}
\ No newline at end of file
diff --git a/contexts/GlobalVariablesContext.js b/contexts/GlobalVariablesContext.js
new file mode 100644
index 0000000..0660093
--- /dev/null
+++ b/contexts/GlobalVariablesContext.js
@@ -0,0 +1,35 @@
+import React, { createContext, useEffect, useState } from 'react';
+import { AppState } from 'react-native';
+
+export const GlobalVariablesContext = createContext(null);
+
+export const GlobalVariablesProvider = ({ children }) => {
+ const [appState, setAppState] = useState(AppState.currentState);
+
+ useEffect(() => {
+ const handleAppStateChange = (nextAppState) => {
+ if (appState.match(/active/) && nextAppState.match(/inactive|background/)) {
+ console.log('App is in background or inactive');
+ } else if (appState.match(/inactive|background/) && nextAppState === 'active') {
+ console.log('App is in the foreground');
+ }
+ setAppState(nextAppState);
+ };
+ const subscription = AppState.addEventListener('change', handleAppStateChange);
+
+ return () => {
+ subscription.remove();
+ };
+ }, [appState]);
+
+ const exportedVariables = {
+ appState,
+ setAppState
+ }
+
+ return (
+
+ {children}
+
+ )
+};
\ No newline at end of file
diff --git a/contexts/index.js b/contexts/index.js
new file mode 100644
index 0000000..3cffedd
--- /dev/null
+++ b/contexts/index.js
@@ -0,0 +1,2 @@
+export { GlobalVariablesProvider, GlobalVariablesContext } from './GlobalVariablesContext';
+export { WebSocketProvider, WebSocketContext } from './WebSocketContext';
\ No newline at end of file
diff --git a/hooks/index.js b/hooks/index.js
index efebedc..9f85db3 100644
--- a/hooks/index.js
+++ b/hooks/index.js
@@ -1,2 +1,5 @@
export { useCallFeed } from './useCallFeed';
-export { useDepartments } from './useDepartments';
\ No newline at end of file
+export { useDepartments } from './useDepartments';
+export { useGlobalVariablesContext } from './useGlobalVariablesContext';
+export { useNotifications } from './useNotifications';
+export { useWebSocketContext } from './useWebSocketContext';
\ No newline at end of file
diff --git a/hooks/useCallFeed/useCallFeed.jsx b/hooks/useCallFeed/useCallFeed.jsx
index 3d50f1d..ec96fa6 100644
--- a/hooks/useCallFeed/useCallFeed.jsx
+++ b/hooks/useCallFeed/useCallFeed.jsx
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { useDepartments } from '../useDepartments';
import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline";
-import { useWebSocketContext } from '../useWebSocketContext';
+import { useNotifications, useWebSocketContext } from '@/hooks';
const callIconMap = {
"CHEST PAIN|HEART PROBLEMS": Cardiology,
@@ -37,119 +37,6 @@ const callIconMap = {
// Bacteria - Sick
// RuralClinic - Medical Facility Response
-const callDetails = {
- "Incident": {
- "IncID": 75,
- "IncNumber": 6873,
- "JurisdictionNumber": 3,
- "ServiceNumber": 42,
- "ServiceID": 45,
- "IncDate": "2024-09-25T01:01:01.55",
- // "IncNature": "CHEST PAIN|HEART PROBLEMS",
- "IncNature": "MOTOR VEHICLE COLLISION (MVC)",
- // "IncNature": "CARDIAC ARREST|DEATH",
- "IncNatureCode": "ALS",
- "IncNatureCodeDesc": "ALS PRIORITY (ALS)",
- "Notes": "570, 16:30> 311 Responding\n580, 16:25> Call Dispatched",
- "Status": "OPEN",
- "Origin": "911"
- },
- "Address": {
- "StreetAddress": "275 E Main St",
- "AddressApartment": "IFO",
- "Town": "Bridgeport",
- "State": "CT",
- "ZipCode": "06608",
- "Latitude": 41.178435683035445,
- "Longitude": -73.18194442701176,
- "County": "Fairfield",
- "Intersection1": "E MAIN ST",
- "Intersection2": "STRATFORD AVE",
- "LocationName": "Chipotle Mexican Grill",
- "WeatherCondition": "Foggy"
- },
- "Person": {
- "Name": "John Doe",
- "Age": 19,
- "Gender": "Female",
- "Statement": "BLOOD PRESSURE 56/41 - IN AND OUT OF CON",
- "Conscious": "No",
- "Breathing": "Yes",
- "CallBackNumber": "(223) 456-7890"
- },
- "Response": {
- "IncID": 75,
- "ResponseID": 75,
- "ServiceID": 45,
- "ServiceName": "DARIEN EMS",
- "Units": [
- {
- "Unit": 311,
- "Department": 'Darien EMS',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:02:02.55",
- "OnScene": "2024-09-25T01:10:10.55",
- "Transporting": "2024-09-25T01:25:01.55",
- "InService": "2024-09-25T02:00:01.55",
- },
- {
- "Unit": 315,
- "Department": 'Darien EMS Supv',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:03:03.15",
- "OnScene": "2024-09-25T01:11:11.55",
- "Transporting": null,
- "InService": "2024-09-25T02:10:01.55",
- },
- {
- "Unit": 310,
- "Department": 'Darien EMS',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:01:01.55",
- "OnScene": "2024-09-25T01:06:06.55",
- "Transporting": "2024-09-25T01:25:01.55",
- "InService": "2024-09-25T02:15:01.55",
- },
- {
- "Unit": 'NHT20',
- "Department": 'Noroton Heights Fire Department',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:08:08.55",
- "OnScene": "2024-09-25T01:12:12.55",
- "Transporting": null,
- "InService": "2024-09-25T01:01:01.55",
- },
- {
- "Unit": 'NFDE31',
- "Department": 'Noroton Fire Department',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:07:07.55",
- "OnScene": "2024-09-25T01:14:14.55",
- "Transporting": null,
- "InService": "2024-09-25T01:01:01.55",
- },
- {
- "Unit": 'DFDT43',
- "Department": 'Darien Fire Department',
- "Dispatched": "2024-09-25T01:01:01.55",
- "Responding": "2024-09-25T01:10:10.55",
- "OnScene": "2024-09-25T01:18:18.55",
- "Transporting": null,
- "InService": "2024-09-25T01:01:01.55",
- },
- {
- "Unit": 1514,
- "Department": 'Greenwich EMS',
- "Dispatched": "2024-09-25T01:15:15.55",
- "Responding": "2024-09-25T01:30:30.55",
- "OnScene": "2024-09-25T01:45:45.55",
- "Transporting": "2024-09-25T02:05:15.55",
- "InService": "2024-09-25T02:25:01.55",
- },
- ]
- }
-}
-
const formatCallTimePast = (callValue) => {
const initDate = new Date(callValue);
const currentTime = new Date();
@@ -173,20 +60,20 @@ const formatCallTimePast = (callValue) => {
}
const formatCallDateTime = (callValue) => {
- const initDate = new Date(callValue);
- if (initDate) {
- const formattedDate = `${initDate.toLocaleDateString('en-US', {
- month: 'short',
- day: 'numeric',
- year: 'numeric',
- })}`;
- const hours = initDate.getHours().toString().padStart(2, '0');
- const minutes = initDate.getMinutes().toString().padStart(2, '0');
- const formattedTime = `${hours}:${minutes}`;
+ const initDate = new Date(callValue);
+ if (initDate) {
+ const formattedDate = `${initDate.toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ year: 'numeric',
+ })}`;
+ const hours = initDate.getHours().toString().padStart(2, '0');
+ const minutes = initDate.getMinutes().toString().padStart(2, '0');
+ const formattedTime = `${hours}:${minutes}`;
- return `${formattedDate} - ${formattedTime}`;
- }
- return 'Date Unavailable';
+ return `${formattedDate} - ${formattedTime}`;
+ }
+ return 'Date Unavailable';
}
export const useCallFeed = () => {
@@ -195,34 +82,34 @@ export const useCallFeed = () => {
const [allCalls, setAllCalls] = useState([]);
const { CallThemes } = departments?.accountDetails;
const {
- CriticalCallList,
- HighCallList,
- MediumCallList,
- LowCallList,
+ CriticalCallList,
+ HighCallList,
+ MediumCallList,
+ LowCallList,
} = CallThemes;
useEffect(() => {
- if (lastMessage) {
- const parsedMessage = JSON?.parse(lastMessage);
- if (parsedMessage?.data) {
- setAllCalls([...allCalls, parsedMessage]);
- }
+ if (lastMessage) {
+ const parsedMessage = JSON?.parse(lastMessage);
+ if (parsedMessage?.data) {
+ setAllCalls([...allCalls, parsedMessage]);
}
+ }
}, [lastMessage]);
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
- if (status === 'CLOSED') {
- return '#0000CD';
- } else if (CriticalCallList.includes(cardiacArrestCall)) {
- return '#8B0000';
- } else if (HighCallList.includes(callAcuity)) {
- return "#FF0000";
- } else if (MediumCallList.includes(callAcuity)) {
- return "#FF8C00";
- } else if (LowCallList.includes(callAcuity)) {
- return "#228B22";
- }
- return 'grey';
+ if (status === 'CLOSED') {
+ return '#0000CD';
+ } else if (CriticalCallList.includes(cardiacArrestCall)) {
+ return '#8B0000';
+ } else if (HighCallList.includes(callAcuity)) {
+ return "#FF0000";
+ } else if (MediumCallList.includes(callAcuity)) {
+ return "#FF8C00";
+ } else if (LowCallList.includes(callAcuity)) {
+ return "#228B22";
+ }
+ return 'grey';
};
return {
diff --git a/hooks/useGlobalVariablesContext/index.js b/hooks/useGlobalVariablesContext/index.js
new file mode 100644
index 0000000..01e5aab
--- /dev/null
+++ b/hooks/useGlobalVariablesContext/index.js
@@ -0,0 +1 @@
+export { useGlobalVariablesContext } from './useGlobalVariablesContext';
\ No newline at end of file
diff --git a/hooks/useGlobalVariablesContext/useGlobalVariablesContext.js b/hooks/useGlobalVariablesContext/useGlobalVariablesContext.js
new file mode 100644
index 0000000..57389a7
--- /dev/null
+++ b/hooks/useGlobalVariablesContext/useGlobalVariablesContext.js
@@ -0,0 +1,4 @@
+import { useContext } from 'react';
+import { GlobalVariablesContext } from '../../contexts';
+
+export const useGlobalVariablesContext = () => useContext(GlobalVariablesContext);
\ No newline at end of file
diff --git a/hooks/useNotifications/index.js b/hooks/useNotifications/index.js
new file mode 100644
index 0000000..719f729
--- /dev/null
+++ b/hooks/useNotifications/index.js
@@ -0,0 +1 @@
+export { useNotifications } from './useNotifications';
\ No newline at end of file
diff --git a/hooks/useNotifications/useNotifications.jsx b/hooks/useNotifications/useNotifications.jsx
new file mode 100644
index 0000000..80521fe
--- /dev/null
+++ b/hooks/useNotifications/useNotifications.jsx
@@ -0,0 +1,115 @@
+
+import { useState, useEffect, useRef } from 'react';
+import { Platform } from 'react-native';
+import * as Device from 'expo-device';
+import * as Notifications from 'expo-notifications';
+import Constants from 'expo-constants';
+
+Notifications.setNotificationHandler({
+ handleNotification: async () => ({
+ shouldShowAlert: true,
+ shouldPlaySound: false,
+ shouldSetBadge: false,
+ }),
+});
+
+const schedulePushNotification = async (title, body, dataObj) => {
+ await Notifications.scheduleNotificationAsync({
+ content: {
+ title,
+ body,
+ data: { data: dataObj },
+ },
+ trigger: {
+ type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL,
+ seconds: 2,
+ },
+ });
+}
+
+const registerForPushNotificationsAsync = async () => {
+ let token;
+
+ if (Platform.OS === 'android') {
+ await Notifications.setNotificationChannelAsync('myNotificationChannel', {
+ name: 'A channel is needed for the permissions prompt to appear',
+ importance: Notifications.AndroidImportance.MAX,
+ vibrationPattern: [0, 250, 250, 250],
+ lightColor: '#FF231F7C',
+ });
+ }
+
+ if (Device.isDevice) {
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
+ let finalStatus = existingStatus;
+ if (existingStatus !== 'granted') {
+ const { status } = await Notifications.requestPermissionsAsync();
+ finalStatus = status;
+ }
+ if (finalStatus !== 'granted') {
+ alert('Failed to get push token for push notification!');
+ return;
+ }
+ // Learn more about projectId:
+ // https://docs.expo.dev/push-notifications/push-notifications-setup/#configure-projectid
+ // EAS projectId is used here.
+ try {
+ const projectId =
+ Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId;
+ if (!projectId) {
+ throw new Error('Project ID not found');
+ }
+ token = (
+ await Notifications.getExpoPushTokenAsync({
+ projectId,
+ })
+ ).data;
+ console.log(token);
+ } catch (e) {
+ token = `${e}`;
+ }
+ } else {
+ alert('Must use physical device for Push Notifications');
+ }
+
+ return token;
+}
+
+export const useNotifications = () => {
+ const [expoPushToken, setExpoPushToken] = useState('');
+ const [channels, setChannels] = useState([]);
+ const [notification, setNotification] = useState(undefined);
+ const notificationListener = useRef();
+ const responseListener = useRef();
+
+ useEffect(() => {
+ registerForPushNotificationsAsync().then(token => token && setExpoPushToken(token));
+
+ if (Platform.OS === 'android') {
+ Notifications.getNotificationChannelsAsync().then(value => setChannels(value ?? []));
+ }
+ notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
+ setNotification(notification);
+ });
+
+ responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
+ console.log(response);
+ });
+
+ return () => {
+ notificationListener.current &&
+ Notifications.removeNotificationSubscription(notificationListener.current);
+ responseListener.current &&
+ Notifications.removeNotificationSubscription(responseListener.current);
+ };
+ }, []);
+
+ return ({
+ schedulePushNotification: async (title, body, dataObj) => {
+ await schedulePushNotification(title, body, dataObj);
+ },
+ expoPushToken,
+ expoNotificationChannels: channels,
+ expoNotification: notification,
+ });
+}
\ No newline at end of file
diff --git a/hooks/useWebSocketContext/useWebSocketContext.js b/hooks/useWebSocketContext/useWebSocketContext.js
index 375861d..c3b7bb1 100644
--- a/hooks/useWebSocketContext/useWebSocketContext.js
+++ b/hooks/useWebSocketContext/useWebSocketContext.js
@@ -1,4 +1,4 @@
import { useContext } from 'react';
-import { WebSocketContext } from '../../contexts/WebSocketContext';
+import { WebSocketContext } from '../../contexts';
export const useWebSocketContext = () => useContext(WebSocketContext);
diff --git a/package-lock.json b/package-lock.json
index 03a3962..b441616 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,8 +12,10 @@
"@react-navigation/native": "^6.0.2",
"expo": "^52.0.46",
"expo-constants": "~17.0.8",
+ "expo-device": "^7.0.3",
"expo-font": "~13.0.4",
"expo-linking": "~7.0.5",
+ "expo-notifications": "^0.29.14",
"expo-router": "~4.0.20",
"expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1",
@@ -2908,6 +2910,12 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/@ide/backoff": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz",
+ "integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==",
+ "license": "MIT"
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -4750,6 +4758,19 @@
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"license": "MIT"
},
+ "node_modules/assert": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
+ "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "is-nan": "^1.3.2",
+ "object-is": "^1.1.5",
+ "object.assign": "^4.1.4",
+ "util": "^0.12.5"
+ }
+ },
"node_modules/ast-types": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz",
@@ -4783,6 +4804,21 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/babel-core": {
"version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
@@ -5009,6 +5045,12 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/badgin": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz",
+ "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==",
+ "license": "MIT"
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5253,6 +5295,24 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -5266,6 +5326,22 @@
"node": ">= 0.4"
}
},
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
@@ -6080,6 +6156,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@@ -6089,6 +6182,23 @@
"node": ">=8"
}
},
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/del": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz",
@@ -6777,6 +6887,15 @@
}
}
},
+ "node_modules/expo-application": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-6.0.2.tgz",
+ "integrity": "sha512-qcj6kGq3mc7x5yIb5KxESurFTJCoEKwNEL34RdPEvTB/xhl7SeVZlu05sZBqxB1V4Ryzq/LsCb7NHNfBbb3L7A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-asset": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.5.tgz",
@@ -6808,6 +6927,44 @@
"react-native": "*"
}
},
+ "node_modules/expo-device": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/expo-device/-/expo-device-7.0.3.tgz",
+ "integrity": "sha512-uNGhDYmpDj/3GySWZmRiYSt52Phdim11p0pXfgpCq/nMks0+UPZwl3D0vin5N8/gpVe5yzb13GYuFxiVoDyniw==",
+ "license": "MIT",
+ "dependencies": {
+ "ua-parser-js": "^0.7.33"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-device/node_modules/ua-parser-js": {
+ "version": "0.7.40",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz",
+ "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/faisalman"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "ua-parser-js": "script/cli.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/expo-file-system": {
"version": "18.0.12",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz",
@@ -6922,6 +7079,26 @@
"invariant": "^2.2.4"
}
},
+ "node_modules/expo-notifications": {
+ "version": "0.29.14",
+ "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.29.14.tgz",
+ "integrity": "sha512-AVduNx9mKOgcAqBfrXS1OHC9VAQZrDQLbVbcorMjPDGXW7m0Q5Q+BG6FYM/saVviF2eO8fhQRsTT40yYv5/bhQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/image-utils": "^0.6.5",
+ "@ide/backoff": "^1.0.0",
+ "abort-controller": "^3.0.0",
+ "assert": "^2.0.0",
+ "badgin": "^1.1.5",
+ "expo-application": "~6.0.2",
+ "expo-constants": "~17.0.8"
+ },
+ "peerDependencies": {
+ "expo": "*",
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/expo-router": {
"version": "4.0.20",
"resolved": "https://registry.npmjs.org/expo-router/-/expo-router-4.0.20.tgz",
@@ -7466,6 +7643,21 @@
"integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==",
"license": "BSD-2-Clause"
},
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/foreground-child": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@@ -7809,6 +8001,18 @@
"node": ">=8"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -8179,6 +8383,22 @@
"node": ">= 0.10"
}
},
+ "node_modules/is-arguments": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
+ "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -8191,6 +8411,18 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"license": "MIT"
},
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -8258,6 +8490,24 @@
"node": ">=6"
}
},
+ "node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -8270,6 +8520,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-nan": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
+ "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -8316,6 +8582,24 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
@@ -8325,6 +8609,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -11020,6 +11319,51 @@
"node": ">=0.10.0"
}
},
+ "node_modules/object-is": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -11519,6 +11863,15 @@
"node": ">=4.0.0"
}
},
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/postcss": {
"version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
@@ -12587,6 +12940,23 @@
],
"license": "MIT"
},
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -12851,6 +13221,23 @@
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT"
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -14141,6 +14528,19 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -14409,6 +14809,27 @@
"node": ">= 8"
}
},
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/wonka": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",
diff --git a/package.json b/package.json
index e2cdae2..5c95b20 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"@react-navigation/native": "^6.0.2",
"expo": "^52.0.46",
"expo-constants": "~17.0.8",
+ "expo-device": "^7.0.3",
"expo-font": "~13.0.4",
"expo-linking": "~7.0.5",
+ "expo-notifications": "^0.29.14",
"expo-router": "~4.0.20",
"expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1",