Add countdown.

This commit is contained in:
Gabriel Brown 2024-10-29 11:23:29 -05:00
parent e84597883b
commit 4fb9e60c6c
13 changed files with 511 additions and 171 deletions

View File

@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native';
import { ThemedView } from '@/components/theme/Theme';
import UserInfo from '@/components/home/UserInfo';
import RelationshipView from '@/components/home/RelationshipView';
import CountdownView from '@/components/home/Countdown';
const IndexScreen = () => {
const [pfpUrl, setPfpUrl] = useState<string | null>(null);
@ -12,6 +13,7 @@ const IndexScreen = () => {
return (
<ThemedView style={styles.container}>
<UserInfo onPfpUpdate={handlePfpUpdate} />
<CountdownView />
<RelationshipView pfpUrl={pfpUrl} />
<ThemedView style={styles.footerContainer}>
</ThemedView>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 720 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 720 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -1,8 +1,10 @@
import React from 'react';
import * as AppleAuthentication from 'expo-apple-authentication';
import { StyleSheet, Alert } from 'react-native';
import { ThemedView } from '@/components/theme/Theme';
import { StyleSheet, Alert, ImageBackground } from 'react-native';
import { ThemedText, ThemedView } from '@/components/theme/Theme';
import { SafeAreaView, SafeAreaProvider } from 'react-native-safe-area-context';
import { useColorScheme } from '@/hooks/useColorScheme';
import { Colors } from '@/constants/Colors';
import * as Notifications from 'expo-notifications';
import Constants from 'expo-constants';
import { saveUser, saveInitialData } from '@/components/services/SecureStore';
@ -13,6 +15,7 @@ import {
} from '@/constants/APIs';
import type { InitialData, User } from '@/constants/Types';
const SignInScreen = ({onSignIn}: {onSignIn: () => void}) => {
const scheme = useColorScheme() ?? 'dark';
@ -86,16 +89,27 @@ const SignInScreen = ({onSignIn}: {onSignIn: () => void}) => {
return (
<ThemedView style={styles.container}>
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={(scheme === 'light') ?
AppleAuthentication.AppleAuthenticationButtonStyle.BLACK :
AppleAuthentication.AppleAuthenticationButtonStyle.WHITE
}
cornerRadius={5}
style={styles.button}
onPress={handleAppleSignIn}
/>
<ImageBackground
source={require('@/assets/images/splash.png')} resizeMode="cover"
style={styles.background}
>
<ThemedText style={[
styles.title,
{textShadowColor: Colors[scheme].background}
]}>
Wavelength
</ThemedText>
<AppleAuthentication.AppleAuthenticationButton
buttonType={AppleAuthentication.AppleAuthenticationButtonType.SIGN_IN}
buttonStyle={(scheme === 'light') ?
AppleAuthentication.AppleAuthenticationButtonStyle.BLACK :
AppleAuthentication.AppleAuthenticationButtonStyle.WHITE
}
cornerRadius={5}
style={styles.button}
onPress={handleAppleSignIn}
/>
</ImageBackground>
</ThemedView>
);
};
@ -104,9 +118,23 @@ export default SignInScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
},
background: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 48,
lineHeight: 48,
fontWeight: 'bold',
marginBottom: 80,
textShadowOffset: {
width: 1,
height: 1,
},
textShadowRadius: 5,
},
button: {
width: 200,
height: 45,

View File

@ -1,7 +1,207 @@
import React, { useEffect, useState } from 'react';
import { StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native';
import { ThemedText, ThemedView } from '@/components/theme/Theme';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Countdown, Relationship, User } from '@/constants/Types';
import { getCountdown } from '@/constants/APIs';
import TextButton from '@/components/theme/buttons/TextButton';
import {
getCountdown as getCountdownFromSecureStore,
getUser,
getRelationship,
saveCountdown,
} from '@/components/services/SecureStore';
import CountdownChangeDateModal from '@/components/home/CountdownChangeDateModal';
const CountdownView = () => {
const [countdownData, setCountdownData] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
});
const [countdown, setCountdown] = useState<Countdown | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [isDateModalOpen, setIsDateModalOpen] = useState(false);
const [user, setUser] = useState<User | null>(null);
const [relationship, setRelationship] = useState<Relationship | null>(null);
useEffect(() => {
const loadData = async () => {
setIsLoading(true);
const userData = await getUser();
setUser(userData);
const relationshipData = await getRelationship();
setRelationship(relationshipData);
const countdownFromSecureStore = await getCountdownFromSecureStore();
if (countdownFromSecureStore) {
setCountdown(countdownFromSecureStore);
} else if (userData) {
const countdownFromServer = await getCountdown(userData.id);
if (countdownFromServer) {
setCountdown(countdownFromServer);
await saveCountdown(countdownFromServer);
}
}
setIsLoading(false);
};
loadData();
}, []);
useEffect(() => {
if (countdown === null) return;
const interval = setInterval(() => {
const now = new Date();
const diff = new Date(countdown.date).getTime() - now.getTime();
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
setCountdownData({ days, hours, minutes, seconds });
if (diff <= 0) {
clearInterval(interval);
setCountdownData({ days: 0, hours: 0, minutes: 0, seconds: 0 });
}
}, 1000);
return () => clearInterval(interval);
}, [countdown]);
const handleCountdownUpdate = async (newDate: Date) => {
if (relationship) {
const newCountdown: Countdown = countdown
? { ...countdown, date: newDate }
: {
id: 0, // This will be set by the server
relationshipId: relationship.id,
title: 'Countdown to Next Visit',
date: newDate,
createdAt: new Date(),
};
setCountdown(newCountdown);
await saveCountdown(newCountdown);
}
};
if (isLoading) {
return (
<ThemedView style={styles.container}>
<ActivityIndicator size='large' color='#0000ff' />
</ThemedView>
);
}
if (!relationship) {
return (
<ThemedView style={styles.container}>
<ThemedText>You are not in a relationship yet.</ThemedText>
</ThemedView>
);
}
if (!countdown) {
return (
<ThemedView style={styles.container}>
<ThemedText>No countdown set yet.</ThemedText>
<TextButton
width={320} height={68}
text='Set Countdown'
fontSize={24}
onPress={() => setIsDateModalOpen(true)}
/>
</ThemedView>
);
}
return (
<ThemedView style={styles.innerContainer}>
<ThemedText style={styles.title}>
{countdown?.title ?? 'Countdown til Next Visit'}
</ThemedText>
<ThemedView style={styles.countdownContainer}>
<CountdownItem
value={countdownData.days}
label={countdownData.days === 1 ? 'Day' : 'Days'}
/>
<CountdownItem
value={countdownData.hours}
label={countdownData.hours === 1 ? 'Hour' : 'Hours'}
/>
<CountdownItem
value={countdownData.minutes}
label={countdownData.minutes === 1 ? 'Minute' : 'Minutes'}
/>
<CountdownItem
value={countdownData.seconds}
label={countdownData.seconds === 1 ? 'Second' : 'Seconds'}
/>
</ThemedView>
<TextButton
width={320} height={68}
text='Change Date'
fontSize={24}
onPress={() => setIsDateModalOpen(true)}
/>
{user && countdown && (
<CountdownChangeDateModal
user={user}
isVisible={isDateModalOpen}
onClose={() => setIsDateModalOpen(false)}
onDateChange={handleCountdownUpdate}
currentCountdown={countdown}
/>
)}
</ThemedView>
);
};
export default CountdownView;
const CountdownItem = ({value, label}: { value: number, label: string }) => {
return (
<ThemedView style={styles.countdownItem}>
<ThemedText style={styles.countdownValue}>{value}</ThemedText>
<ThemedText style={styles.countdownLabel}>{label}</ThemedText>
</ThemedView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'transparent',
},
innerContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 10,
backgroundColor: 'transparent',
},
title: {
fontSize: 32,
lineHeight: 32,
fontWeight: '600',
textAlign: 'center',
marginHorizontal: 'auto',
},
countdownContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
width: '100%',
backgroundColor: 'transparent',
marginVertical: 20,
},
countdownItem: {
alignItems: 'center',
marginHorizontal: 10,
backgroundColor: 'transparent',
},
countdownValue: {
fontSize: 32,
lineHeight: 42,
fontWeight: 'bold',
},
countdownLabel: {
fontSize: 18,
lineHeight: 24,
},
});

View File

@ -0,0 +1,162 @@
import React, { useState } from 'react';
import { StyleSheet, Modal } from 'react-native';
import { ThemedText, ThemedView } from '@/components/theme/Theme';
import DateTimePicker from '@react-native-community/datetimepicker';
import { Countdown, User } from '@/constants/Types';
import { setCountdown } from '@/constants/APIs';
import TextButton from '@/components/theme/buttons/TextButton';
type ChangeDateModalProps = {
user: User;
isVisible: boolean;
onClose: () => void;
onDateChange: (date: Date) => void;
currentCountdown?: Countdown;
};
const ChangeDateModal = ({
user, isVisible, onClose, onDateChange, currentCountdown
}: ChangeDateModalProps) => {
const [date, setDate] = useState(currentCountdown ? new Date(currentCountdown.date) : new Date());
const handleDateChange = (event: any, selectedDate?: Date) => {
const currentDate = selectedDate ?? date;
setDate(currentDate);
};
const handleTimeChange = (event: any, selectedTime?: Date) => {
const currentTime = selectedTime ?? date;
setDate(currentTime);
};
const handleSave = async () => {
try {
let updatedCountdown: Countdown;
if (currentCountdown) {
updatedCountdown = { ...currentCountdown, date: date };
await setCountdown(user.id, updatedCountdown);
onDateChange(date);
onClose();
}
} catch (error) {
console.error('Error saving countdown:', error);
}
};
return (
<Modal
animationType="slide"
transparent={true}
visible={isVisible}
onRequestClose={onClose}
>
<ThemedView style={styles.centeredView}>
<ThemedView style={styles.modalView}>
<ThemedText style={styles.modalText}>
Set New Countdown
</ThemedText>
<ThemedView style={styles.container}>
<ThemedView style={styles.dateContainer}>
<DateTimePicker
testID="datePicker"
value={date}
mode="date"
is24Hour={true}
display="default"
onChange={handleDateChange}
/>
</ThemedView>
<ThemedView style={styles.timeContainer}>
<DateTimePicker
testID="timePicker"
value={date}
mode="time"
is24Hour={true}
display="default"
onChange={handleTimeChange}
/>
</ThemedView>
</ThemedView>
<ThemedView style={styles.buttonContainer}>
<TextButton
width={120}
height={60}
text='Save'
fontSize={18}
onPress={handleSave}
/>
<TextButton
width={120}
height={60}
text='Cancel'
fontSize={18}
onPress={onClose}
/>
</ThemedView>
</ThemedView>
</ThemedView>
</Modal>
);
};
export default ChangeDateModal;
const styles = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
},
modalView: {
margin: 10,
padding: 35,
borderRadius: 40,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
width: '80%',
},
modalText: {
marginBottom: 20,
textAlign: 'center',
fontWeight: 'bold',
fontSize: 24,
lineHeight: 32,
},
container: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
margin: 'auto',
marginBottom: 20,
backgroundColor: 'transparent',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
margin: 'auto',
backgroundColor: 'transparent',
},
dateContainer: {
width: '50%',
justifyContent: 'center',
alignItems: 'center',
margin: 'auto',
backgroundColor: 'transparent',
},
timeContainer: {
width: '50%',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
minWidth: 120,
},
});

View File

@ -1,74 +0,0 @@
import React, { useEffect, useState } from 'react';
import { StyleSheet, Alert } from 'react-native';
import { ThemedText } from '@/components/theme/Theme';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import FontAwesome from '@expo/vector-icons/FontAwesome';
import Button from '@/components/theme/buttons/DefaultButton';
import { getUser } from '@/components/services/SecureStore';
import { sendPushNotification } from '@/components/services/notifications/PushNotificationManager';
import type { NotificationMessage } from '@/constants/Types';
const TestPush = () => {
const scheme = useColorScheme() ?? 'dark';
const [pushToken, setPushToken] = useState<string | null>(null);
useEffect(() => {
const fetchUserData = async () => {
const user = await getUser();
if (user) {
setPushToken(user.pushToken);
}
};
fetchUserData();
}, []);
const message: NotificationMessage = {
sound: 'default',
title: 'Test push notification',
body: 'This is a test push notification',
data: {
test: 'test',
},
};
const handleSendPushNotification = async () => {
try {
await sendPushNotification(pushToken, message);
Alert.alert('Success', 'Push notification sent successfully.');
} catch (error) {
Alert.alert('Error', 'Failed to send push notification.');
}
};
return (
<Button
width={220} height={60}
onPress={handleSendPushNotification}
>
<FontAwesome
name='bell' size={18}
color={Colors[scheme].background}
style={styles.buttonIcon}
/>
<ThemedText
style={[
styles.buttonLabel,
{color: Colors[scheme].background}
]}
>
Send Push Notification
</ThemedText>
</Button>
);
};
export default TestPush;
const styles = StyleSheet.create({
buttonLabel: {
fontSize: 16,
},
buttonIcon: {
paddingRight: 8,
},
});

View File

@ -2,6 +2,7 @@ import { StyleSheet, Pressable } from "react-native";
import { ThemedView } from "@/components/theme/Theme";
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import { StyleProp, ViewStyle } from 'react-native';
const DEFAULT_WIDTH = 320;
const DEFAULT_HEIGHT = 68;

View File

@ -15,6 +15,7 @@
},
"dependencies": {
"@expo/vector-icons": "^14.0.4",
"@react-native-community/datetimepicker": "8.0.1",
"@react-navigation/native": "^6.1.18",
"@teovilla/react-native-web-maps": "^0.9.5",
"@types/lodash.debounce": "^4.0.9",

188
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
'@expo/vector-icons':
specifier: ^14.0.4
version: 14.0.4
'@react-native-community/datetimepicker':
specifier: 8.0.1
version: 8.0.1(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.25.9(@babel/core@7.26.0))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
'@react-navigation/native':
specifier: ^6.1.18
version: 6.1.18(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.25.9(@babel/core@7.26.0))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
@ -131,10 +134,10 @@ importers:
version: 18.3.0
jest:
specifier: ^29.7.0
version: 29.7.0(@types/node@22.8.0)
version: 29.7.0(@types/node@22.8.2)
jest-expo:
specifier: ~51.0.4
version: 51.0.4(@babel/core@7.26.0)(jest@29.7.0(@types/node@22.8.0))(react@18.2.0)
version: 51.0.4(@babel/core@7.26.0)(jest@29.7.0(@types/node@22.8.2))(react@18.2.0)
react-test-renderer:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
@ -269,8 +272,8 @@ packages:
resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.26.0':
resolution: {integrity: sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA==}
'@babel/parser@7.26.1':
resolution: {integrity: sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==}
engines: {node: '>=6.0.0'}
hasBin: true
@ -1216,6 +1219,16 @@ packages:
engines: {node: '>=18'}
hasBin: true
'@react-native-community/datetimepicker@8.0.1':
resolution: {integrity: sha512-4BO0t3geMNNw9cIIm9p9FNUzwMXexdzD4pAH0AaUAycs3BS71HLrX8jHbrI7nzq/+8O7cLAXn5Gudte+YpTV8Q==}
peerDependencies:
react: '*'
react-native: '*'
react-native-windows: '*'
peerDependenciesMeta:
react-native-windows:
optional: true
'@react-native/assets-registry@0.74.87':
resolution: {integrity: sha512-1XmRhqQchN+pXPKEKYdpJlwESxVomJOxtEnIkbo7GAlaN2sym84fHEGDXAjLilih5GVPpcpSmFzTy8jx3LtaFg==}
engines: {node: '>=18'}
@ -1475,11 +1488,11 @@ packages:
'@types/node-forge@1.3.11':
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
'@types/node@18.19.59':
resolution: {integrity: sha512-vizm2EqwV/7Zay+A6J3tGl9Lhr7CjZe2HmWS988sefiEmsyP9CeXEleho6i4hJk/8UtZAo0bWN4QPZZr83RxvQ==}
'@types/node@18.19.60':
resolution: {integrity: sha512-cYRj7igVqgxhlHFdBHHpU2SNw3+dN2x0VTZJtLYk6y/ieuGN4XiBgtDjYVktM/yk2y/8pKMileNc6IoEzEJnUw==}
'@types/node@22.8.0':
resolution: {integrity: sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg==}
'@types/node@22.8.2':
resolution: {integrity: sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw==}
'@types/prop-types@15.7.13':
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
@ -1524,6 +1537,7 @@ packages:
'@xmldom/xmldom@0.7.13':
resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==}
engines: {node: '>=10.0.0'}
deprecated: this version is no longer supported, please update to at least 0.8.*
'@xmldom/xmldom@0.8.10':
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
@ -1551,8 +1565,8 @@ packages:
resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
engines: {node: '>=0.4.0'}
acorn@8.13.0:
resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==}
acorn@8.14.0:
resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
engines: {node: '>=0.4.0'}
hasBin: true
@ -1847,8 +1861,8 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
caniuse-lite@1.0.30001669:
resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==}
caniuse-lite@1.0.30001674:
resolution: {integrity: sha512-jOsKlZVRnzfhLojb+Ykb+gyUSp9Xb57So+fAiFlLzzTKpqg8xxSav0e40c8/4F/v9N8QSvrRRaLeVzQbLqomYw==}
chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@ -2216,8 +2230,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.45:
resolution: {integrity: sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw==}
electron-to-chromium@1.5.49:
resolution: {integrity: sha512-ZXfs1Of8fDb6z7WEYZjXpgIRF6MEu8JdeGA0A40aZq6OQbS+eJpnnV49epZRna2DU/YsEjSQuGtQPPtvt6J65A==}
emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@ -3825,8 +3839,8 @@ packages:
resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==}
engines: {node: '>=10'}
parse5@7.2.0:
resolution: {integrity: sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==}
parse5@7.2.1:
resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
@ -4169,8 +4183,8 @@ packages:
regjsgen@0.8.0:
resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==}
regjsparser@0.11.1:
resolution: {integrity: sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==}
regjsparser@0.11.2:
resolution: {integrity: sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==}
hasBin: true
remove-trailing-slash@0.1.1:
@ -5121,7 +5135,7 @@ snapshots:
'@babel/helper-compilation-targets': 7.25.9
'@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
'@babel/helpers': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/template': 7.25.9
'@babel/traverse': 7.25.9
'@babel/types': 7.26.0
@ -5143,7 +5157,7 @@ snapshots:
'@babel/generator@7.26.0':
dependencies:
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
@ -5290,7 +5304,7 @@ snapshots:
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/parser@7.26.0':
'@babel/parser@7.26.1':
dependencies:
'@babel/types': 7.26.0
@ -6035,14 +6049,14 @@ snapshots:
'@babel/template@7.25.9':
dependencies:
'@babel/code-frame': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
'@babel/traverse@7.25.9':
dependencies:
'@babel/code-frame': 7.26.0
'@babel/generator': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/template': 7.25.9
'@babel/types': 7.26.0
debug: 4.3.7
@ -6246,7 +6260,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/generator': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
'@expo/config': 9.0.4
'@expo/env': 0.3.0
@ -6437,7 +6451,7 @@ snapshots:
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
@ -6450,14 +6464,14 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@22.8.0)
jest-config: 29.7.0(@types/node@22.8.2)
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@ -6486,7 +6500,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@ -6504,7 +6518,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@ -6526,7 +6540,7 @@ snapshots:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.25
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
collect-v8-coverage: 1.0.2
exit: 0.1.2
@ -6601,7 +6615,7 @@ snapshots:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.8.0
'@types/node': 22.8.2
'@types/yargs': 15.0.19
chalk: 4.1.2
@ -6610,7 +6624,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.8.0
'@types/node': 22.8.2
'@types/yargs': 17.0.33
chalk: 4.1.2
@ -6828,6 +6842,12 @@ snapshots:
- supports-color
- utf-8-validate
'@react-native-community/datetimepicker@8.0.1(react-native@0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.25.9(@babel/core@7.26.0))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)':
dependencies:
invariant: 2.2.4
react: 18.2.0
react-native: 0.74.5(@babel/core@7.26.0)(@babel/preset-env@7.25.9(@babel/core@7.26.0))(@types/react@18.2.79)(react@18.2.0)
'@react-native/assets-registry@0.74.87': {}
'@react-native/babel-plugin-codegen@0.74.87(@babel/preset-env@7.25.9(@babel/core@7.26.0))':
@ -6888,7 +6908,7 @@ snapshots:
'@react-native/codegen@0.74.87(@babel/preset-env@7.25.9(@babel/core@7.26.0))':
dependencies:
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/preset-env': 7.25.9(@babel/core@7.26.0)
glob: 7.2.3
hermes-parser: 0.19.1
@ -7105,7 +7125,7 @@ snapshots:
'@rnx-kit/chromium-edge-launcher@1.0.0':
dependencies:
'@types/node': 18.19.59
'@types/node': 18.19.60
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@ -7152,7 +7172,7 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
@ -7164,7 +7184,7 @@ snapshots:
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
'@types/babel__traverse@7.20.6':
@ -7179,7 +7199,7 @@ snapshots:
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
'@types/hammerjs@2.0.46': {}
@ -7210,9 +7230,9 @@ snapshots:
'@types/jsdom@20.0.1':
dependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
'@types/tough-cookie': 4.0.5
parse5: 7.2.0
parse5: 7.2.1
'@types/json-schema@7.0.15': {}
@ -7228,13 +7248,13 @@ snapshots:
'@types/node-forge@1.3.11':
dependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
'@types/node@18.19.59':
'@types/node@18.19.60':
dependencies:
undici-types: 5.26.5
'@types/node@22.8.0':
'@types/node@22.8.2':
dependencies:
undici-types: 6.19.8
@ -7301,14 +7321,14 @@ snapshots:
acorn-globals@7.0.1:
dependencies:
acorn: 8.13.0
acorn: 8.14.0
acorn-walk: 8.3.4
acorn-walk@8.3.4:
dependencies:
acorn: 8.13.0
acorn: 8.14.0
acorn@8.13.0: {}
acorn@8.14.0: {}
agent-base@6.0.2:
dependencies:
@ -7599,8 +7619,8 @@ snapshots:
browserslist@4.24.2:
dependencies:
caniuse-lite: 1.0.30001669
electron-to-chromium: 1.5.45
caniuse-lite: 1.0.30001674
electron-to-chromium: 1.5.49
node-releases: 2.0.18
update-browserslist-db: 1.1.1(browserslist@4.24.2)
@ -7667,7 +7687,7 @@ snapshots:
camelcase@6.3.0: {}
caniuse-lite@1.0.30001669: {}
caniuse-lite@1.0.30001674: {}
chalk@2.4.2:
dependencies:
@ -7695,7 +7715,7 @@ snapshots:
chrome-launcher@0.15.2:
dependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@ -7834,13 +7854,13 @@ snapshots:
js-yaml: 3.14.1
parse-json: 4.0.0
create-jest@29.7.0(@types/node@22.8.0):
create-jest@29.7.0(@types/node@22.8.2):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.8.0)
jest-config: 29.7.0(@types/node@22.8.2)
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@ -8009,7 +8029,7 @@ snapshots:
ee-first@1.1.1: {}
electron-to-chromium@1.5.45: {}
electron-to-chromium@1.5.49: {}
emittery@0.13.1: {}
@ -8934,7 +8954,7 @@ snapshots:
istanbul-lib-instrument@5.2.1:
dependencies:
'@babel/core': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 6.3.1
@ -8944,7 +8964,7 @@ snapshots:
istanbul-lib-instrument@6.0.3:
dependencies:
'@babel/core': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@istanbuljs/schema': 0.1.3
istanbul-lib-coverage: 3.2.2
semver: 7.6.3
@ -8988,7 +9008,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
co: 4.6.0
dedent: 1.5.3
@ -9008,16 +9028,16 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-cli@29.7.0(@types/node@22.8.0):
jest-cli@29.7.0(@types/node@22.8.2):
dependencies:
'@jest/core': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.8.0)
create-jest: 29.7.0(@types/node@22.8.2)
exit: 0.1.2
import-local: 3.2.0
jest-config: 29.7.0(@types/node@22.8.0)
jest-config: 29.7.0(@types/node@22.8.2)
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@ -9027,7 +9047,7 @@ snapshots:
- supports-color
- ts-node
jest-config@29.7.0(@types/node@22.8.0):
jest-config@29.7.0(@types/node@22.8.2):
dependencies:
'@babel/core': 7.26.0
'@jest/test-sequencer': 29.7.0
@ -9052,7 +9072,7 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@ -9082,7 +9102,7 @@ snapshots:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/jsdom': 20.0.1
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-mock: 29.7.0
jest-util: 29.7.0
jsdom: 20.0.3
@ -9096,11 +9116,11 @@ snapshots:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-mock: 29.7.0
jest-util: 29.7.0
jest-expo@51.0.4(@babel/core@7.26.0)(jest@29.7.0(@types/node@22.8.0))(react@18.2.0):
jest-expo@51.0.4(@babel/core@7.26.0)(jest@29.7.0(@types/node@22.8.2))(react@18.2.0):
dependencies:
'@expo/config': 9.0.4
'@expo/json-file': 8.3.3
@ -9109,7 +9129,7 @@ snapshots:
find-up: 5.0.0
jest-environment-jsdom: 29.7.0
jest-watch-select-projects: 2.0.0
jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@22.8.0))
jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@22.8.2))
json5: 2.2.3
lodash: 4.17.21
react-test-renderer: 18.2.0(react@18.2.0)
@ -9129,7 +9149,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
'@types/node': 22.8.0
'@types/node': 22.8.2
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@ -9168,7 +9188,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@ -9203,7 +9223,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@ -9231,7 +9251,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
cjs-module-lexer: 1.4.1
collect-v8-coverage: 1.0.2
@ -9277,7 +9297,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@ -9298,11 +9318,11 @@ snapshots:
chalk: 3.0.0
prompts: 2.4.2
jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@22.8.0)):
jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@22.8.2)):
dependencies:
ansi-escapes: 6.2.1
chalk: 4.1.2
jest: 29.7.0(@types/node@22.8.0)
jest: 29.7.0(@types/node@22.8.2)
jest-regex-util: 29.6.3
jest-watcher: 29.7.0
slash: 5.1.0
@ -9313,7 +9333,7 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.8.0
'@types/node': 22.8.2
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@ -9322,17 +9342,17 @@ snapshots:
jest-worker@29.7.0:
dependencies:
'@types/node': 22.8.0
'@types/node': 22.8.2
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
jest@29.7.0(@types/node@22.8.0):
jest@29.7.0(@types/node@22.8.2):
dependencies:
'@jest/core': 29.7.0
'@jest/types': 29.6.3
import-local: 3.2.0
jest-cli: 29.7.0(@types/node@22.8.0)
jest-cli: 29.7.0(@types/node@22.8.2)
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@ -9369,7 +9389,7 @@ snapshots:
jscodeshift@0.14.0(@babel/preset-env@7.25.9(@babel/core@7.26.0)):
dependencies:
'@babel/core': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.26.0)
'@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.26.0)
'@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.26.0)
@ -9394,7 +9414,7 @@ snapshots:
jsdom@20.0.3:
dependencies:
abab: 2.0.6
acorn: 8.13.0
acorn: 8.14.0
acorn-globals: 7.0.1
cssom: 0.5.0
cssstyle: 2.3.0
@ -9408,7 +9428,7 @@ snapshots:
https-proxy-agent: 5.0.1
is-potential-custom-element-name: 1.0.1
nwsapi: 2.2.13
parse5: 7.2.0
parse5: 7.2.1
saxes: 6.0.0
symbol-tree: 3.2.4
tough-cookie: 4.1.4
@ -9719,7 +9739,7 @@ snapshots:
dependencies:
'@babel/core': 7.26.0
'@babel/generator': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/types': 7.26.0
flow-enums-runtime: 0.0.6
metro: 0.80.12
@ -9740,7 +9760,7 @@ snapshots:
'@babel/code-frame': 7.26.0
'@babel/core': 7.26.0
'@babel/generator': 7.26.0
'@babel/parser': 7.26.0
'@babel/parser': 7.26.1
'@babel/template': 7.25.9
'@babel/traverse': 7.25.9
'@babel/types': 7.26.0
@ -10046,7 +10066,7 @@ snapshots:
dependencies:
pngjs: 3.4.0
parse5@7.2.0:
parse5@7.2.1:
dependencies:
entities: 4.5.0
@ -10450,13 +10470,13 @@ snapshots:
regenerate: 1.4.2
regenerate-unicode-properties: 10.2.0
regjsgen: 0.8.0
regjsparser: 0.11.1
regjsparser: 0.11.2
unicode-match-property-ecmascript: 2.0.0
unicode-match-property-value-ecmascript: 2.2.0
regjsgen@0.8.0: {}
regjsparser@0.11.1:
regjsparser@0.11.2:
dependencies:
jsesc: 3.0.2
@ -10943,7 +10963,7 @@ snapshots:
terser@5.36.0:
dependencies:
'@jridgewell/source-map': 0.3.6
acorn: 8.13.0
acorn: 8.14.0
commander: 2.20.3
source-map-support: 0.5.21