Add Customizable Notifications & Add Global Variables

This commit is contained in:
Matt DiMeglio 2025-04-21 11:41:08 -04:00
parent 5ad7ef9d1a
commit 051837968a
14 changed files with 1008 additions and 504 deletions

59
app/_layout.jsx Normal file
View file

@ -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 (
<Stack
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="login" />
</Stack>
)
}
export default function App() {
return (
<GlobalVariablesProvider>
<WebSocketProvider>
<AppTest />
</WebSocketProvider>
</GlobalVariablesProvider>
);
}

View file

@ -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 (
<WebSocketProvider>
<Stack
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="login" />
</Stack>
</WebSocketProvider>
);
}

View file

@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useCallFeed } from '../hooks/useCallFeed'; import { useCallFeed } from '@/hooks';
import { router } from 'expo-router'; import { router } from 'expo-router';
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native'; import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';

View file

@ -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 (
<GlobalVariablesContext.Provider value={exportedVariables}>
{children}
</GlobalVariablesContext.Provider>
)
};

2
contexts/index.js Normal file
View file

@ -0,0 +1,2 @@
export { GlobalVariablesProvider, GlobalVariablesContext } from './GlobalVariablesContext';
export { WebSocketProvider, WebSocketContext } from './WebSocketContext';

View file

@ -1,2 +1,5 @@
export { useCallFeed } from './useCallFeed'; export { useCallFeed } from './useCallFeed';
export { useDepartments } from './useDepartments'; export { useDepartments } from './useDepartments';
export { useGlobalVariablesContext } from './useGlobalVariablesContext';
export { useNotifications } from './useNotifications';
export { useWebSocketContext } from './useWebSocketContext';

View file

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { useDepartments } from '../useDepartments'; import { useDepartments } from '../useDepartments';
import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline"; import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline";
import { useWebSocketContext } from '../useWebSocketContext'; import { useNotifications, useWebSocketContext } from '@/hooks';
const callIconMap = { const callIconMap = {
"CHEST PAIN|HEART PROBLEMS": Cardiology, "CHEST PAIN|HEART PROBLEMS": Cardiology,
@ -37,119 +37,6 @@ const callIconMap = {
// Bacteria - Sick // Bacteria - Sick
// RuralClinic - Medical Facility Response // 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 formatCallTimePast = (callValue) => {
const initDate = new Date(callValue); const initDate = new Date(callValue);
const currentTime = new Date(); const currentTime = new Date();

View file

@ -0,0 +1 @@
export { useGlobalVariablesContext } from './useGlobalVariablesContext';

View file

@ -0,0 +1,4 @@
import { useContext } from 'react';
import { GlobalVariablesContext } from '../../contexts';
export const useGlobalVariablesContext = () => useContext(GlobalVariablesContext);

View file

@ -0,0 +1 @@
export { useNotifications } from './useNotifications';

View file

@ -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,
});
}

View file

@ -1,4 +1,4 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { WebSocketContext } from '../../contexts/WebSocketContext'; import { WebSocketContext } from '../../contexts';
export const useWebSocketContext = () => useContext(WebSocketContext); export const useWebSocketContext = () => useContext(WebSocketContext);

421
package-lock.json generated
View file

@ -12,8 +12,10 @@
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"expo": "^52.0.46", "expo": "^52.0.46",
"expo-constants": "~17.0.8", "expo-constants": "~17.0.8",
"expo-device": "^7.0.3",
"expo-font": "~13.0.4", "expo-font": "~13.0.4",
"expo-linking": "~7.0.5", "expo-linking": "~7.0.5",
"expo-notifications": "^0.29.14",
"expo-router": "~4.0.20", "expo-router": "~4.0.20",
"expo-splash-screen": "~0.29.24", "expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1", "expo-status-bar": "~2.0.1",
@ -2908,6 +2910,12 @@
"js-yaml": "bin/js-yaml.js" "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": { "node_modules/@isaacs/cliui": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@ -4750,6 +4758,19 @@
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"license": "MIT" "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": { "node_modules/ast-types": {
"version": "0.15.2", "version": "0.15.2",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.15.2.tgz",
@ -4783,6 +4804,21 @@
"node": ">= 4.0.0" "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": { "node_modules/babel-core": {
"version": "7.0.0-bridge.0", "version": "7.0.0-bridge.0",
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz",
@ -5009,6 +5045,12 @@
"@babel/core": "^7.0.0" "@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": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -5253,6 +5295,24 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC" "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": { "node_modules/call-bind-apply-helpers": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "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": ">= 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": { "node_modules/caller-callsite": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
@ -6080,6 +6156,23 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/define-lazy-prop": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
@ -6089,6 +6182,23 @@
"node": ">=8" "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": { "node_modules/del": {
"version": "6.1.1", "version": "6.1.1",
"resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", "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": { "node_modules/expo-asset": {
"version": "11.0.5", "version": "11.0.5",
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.5.tgz", "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.5.tgz",
@ -6808,6 +6927,44 @@
"react-native": "*" "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": { "node_modules/expo-file-system": {
"version": "18.0.12", "version": "18.0.12",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz",
@ -6922,6 +7079,26 @@
"invariant": "^2.2.4" "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": { "node_modules/expo-router": {
"version": "4.0.20", "version": "4.0.20",
"resolved": "https://registry.npmjs.org/expo-router/-/expo-router-4.0.20.tgz", "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-4.0.20.tgz",
@ -7466,6 +7643,21 @@
"integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==", "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==",
"license": "BSD-2-Clause" "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": { "node_modules/foreground-child": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
@ -7809,6 +8001,18 @@
"node": ">=8" "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": { "node_modules/has-symbols": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@ -8179,6 +8383,22 @@
"node": ">= 0.10" "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": { "node_modules/is-arrayish": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@ -8191,6 +8411,18 @@
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"license": "MIT" "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": { "node_modules/is-core-module": {
"version": "2.16.1", "version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
@ -8258,6 +8490,24 @@
"node": ">=6" "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": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@ -8270,6 +8520,22 @@
"node": ">=0.10.0" "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": { "node_modules/is-number": {
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -8316,6 +8582,24 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
@ -8325,6 +8609,21 @@
"node": ">=0.10.0" "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": { "node_modules/is-wsl": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@ -11020,6 +11319,51 @@
"node": ">=0.10.0" "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": { "node_modules/on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@ -11519,6 +11863,15 @@
"node": ">=4.0.0" "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": { "node_modules/postcss": {
"version": "8.4.49", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
@ -12587,6 +12940,23 @@
], ],
"license": "MIT" "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": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@ -12851,6 +13221,23 @@
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT" "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": { "node_modules/setimmediate": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "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" "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": { "node_modules/utils-merge": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -14409,6 +14809,27 @@
"node": ">= 8" "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": { "node_modules/wonka": {
"version": "6.3.5", "version": "6.3.5",
"resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",

View file

@ -19,8 +19,10 @@
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"expo": "^52.0.46", "expo": "^52.0.46",
"expo-constants": "~17.0.8", "expo-constants": "~17.0.8",
"expo-device": "^7.0.3",
"expo-font": "~13.0.4", "expo-font": "~13.0.4",
"expo-linking": "~7.0.5", "expo-linking": "~7.0.5",
"expo-notifications": "^0.29.14",
"expo-router": "~4.0.20", "expo-router": "~4.0.20",
"expo-splash-screen": "~0.29.24", "expo-splash-screen": "~0.29.24",
"expo-status-bar": "~2.0.1", "expo-status-bar": "~2.0.1",