Init commit. Rewrite of old project. Not done
42
app.json
@ -13,13 +13,25 @@
|
|||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
},
|
},
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true
|
"supportsTablet": true,
|
||||||
|
"usesAppleSignIn": true,
|
||||||
|
"config": {
|
||||||
|
"usesNonExemptEncryption": false
|
||||||
|
},
|
||||||
|
"infoPList": {
|
||||||
|
"NSLocationWhenInUseUsageDescription": "This app uses your location in order to allow you to share your location in chat.",
|
||||||
|
"NSCameraUsageDescription": "This app uses your camera to take photos & send them in the chat."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
"foregroundImage": "./assets/images/adaptive-icon.png",
|
"foregroundImage": "./assets/images/adaptive-icon.png",
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
}
|
},
|
||||||
|
"permissions": [
|
||||||
|
"android.permission.ACCESS_COARSE_LOCATION",
|
||||||
|
"android.permission.ACCESS_FINE_LOCATION"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"bundler": "metro",
|
"bundler": "metro",
|
||||||
@ -27,10 +39,32 @@
|
|||||||
"favicon": "./assets/images/favicon.png"
|
"favicon": "./assets/images/favicon.png"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-router"
|
"expo-router",
|
||||||
|
"expo-apple-authentication",
|
||||||
|
[
|
||||||
|
"expo-secure-store",
|
||||||
|
{
|
||||||
|
"faceIDPermission": "Allow $(PRODUCT_NAME) to access your Face ID biometric data."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"expo-location",
|
||||||
|
{
|
||||||
|
"locationAlwaysAndWhenInUsePermission": "Allow $(PRODUCT_NAME) to use your location."
|
||||||
|
}
|
||||||
|
]
|
||||||
],
|
],
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"typedRoutes": true
|
"typedRoutes": true
|
||||||
}
|
},
|
||||||
|
"extra": {
|
||||||
|
"router": {
|
||||||
|
"origin": false
|
||||||
|
},
|
||||||
|
"eas": {
|
||||||
|
"projectId": "c7056557-31c8-4f5c-a3e5-802df6a3bf7d"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"owner": "gibbyb"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,31 @@
|
|||||||
import { Tabs } from 'expo-router';
|
import { Tabs } from "expo-router";
|
||||||
import React from 'react';
|
import { TabBarIcon } from "@/components/navigation/TabBarIcon";
|
||||||
|
import { Colors } from "@/constants/Colors";
|
||||||
|
import { useColorScheme } from "@/hooks/useColorScheme";
|
||||||
|
|
||||||
import { TabBarIcon } from '@/components/navigation/TabBarIcon';
|
const TabLayout = () => {
|
||||||
import { Colors } from '@/constants/Colors';
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
|
||||||
|
|
||||||
export default function TabLayout() {
|
|
||||||
const colorScheme = useColorScheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
tabBarActiveTintColor: Colors[scheme].tint,
|
||||||
headerShown: false,
|
//headerShown: false
|
||||||
}}>
|
headerStyle: {
|
||||||
|
backgroundColor: Colors[scheme].background,
|
||||||
|
},
|
||||||
|
headerShadowVisible: false,
|
||||||
|
headerTintColor: Colors[scheme].tint,
|
||||||
|
tabBarStyle: {
|
||||||
|
backgroundColor: Colors[scheme].background,
|
||||||
|
borderTopColor: Colors[scheme].tint,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name='index'
|
||||||
options={{
|
options={{
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
tabBarIcon: ({ color, focused }) => (
|
tabBarIcon: ({ color, focused }) => (
|
||||||
@ -23,15 +33,28 @@ export default function TabLayout() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="explore"
|
name='messages'
|
||||||
options={{
|
options={{
|
||||||
title: 'Explore',
|
title: 'Messages',
|
||||||
tabBarIcon: ({ color, focused }) => (
|
tabBarIcon: ({ color, focused }) => (
|
||||||
<TabBarIcon name={focused ? 'code-slash' : 'code-slash-outline'} color={color} />
|
<TabBarIcon name={focused ? 'chatbubbles' : 'chatbubbles-outline'} color={color} />
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Tabs.Screen
|
||||||
|
name='settings'
|
||||||
|
options={{
|
||||||
|
title: 'Settings',
|
||||||
|
tabBarIcon: ({ color, focused }) => (
|
||||||
|
<TabBarIcon name={focused ? 'settings' : 'settings-outline'} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default TabLayout;
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
||||||
import { StyleSheet, Image, Platform } from 'react-native';
|
|
||||||
|
|
||||||
import { Collapsible } from '@/components/Collapsible';
|
|
||||||
import { ExternalLink } from '@/components/ExternalLink';
|
|
||||||
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
|
|
||||||
export default function TabTwoScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
|
||||||
headerImage={<Ionicons size={310} name="code-slash" style={styles.headerImage} />}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText type="title">Explore</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
|
||||||
<Collapsible title="File-based routing">
|
|
||||||
<ThemedText>
|
|
||||||
This app has two screens:{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
|
||||||
</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
|
|
||||||
sets up the tab navigator.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Android, iOS, and web support">
|
|
||||||
<ThemedText>
|
|
||||||
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
|
|
||||||
</ThemedText>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Images">
|
|
||||||
<ThemedText>
|
|
||||||
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
|
|
||||||
different screen densities
|
|
||||||
</ThemedText>
|
|
||||||
<Image source={require('@/assets/images/react-logo.png')} style={{ alignSelf: 'center' }} />
|
|
||||||
<ExternalLink href="https://reactnative.dev/docs/images">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Custom fonts">
|
|
||||||
<ThemedText>
|
|
||||||
Open <ThemedText type="defaultSemiBold">app/_layout.tsx</ThemedText> to see how to load{' '}
|
|
||||||
<ThemedText style={{ fontFamily: 'SpaceMono' }}>
|
|
||||||
custom fonts such as this one.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/versions/latest/sdk/font">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Light and dark mode components">
|
|
||||||
<ThemedText>
|
|
||||||
This template has light and dark mode support. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
|
|
||||||
what the user's current color scheme is, and so you can adjust UI colors accordingly.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Animations">
|
|
||||||
<ThemedText>
|
|
||||||
This template includes an example of an animated component. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
|
|
||||||
the powerful <ThemedText type="defaultSemiBold">react-native-reanimated</ThemedText> library
|
|
||||||
to create a waving hand animation.
|
|
||||||
</ThemedText>
|
|
||||||
{Platform.select({
|
|
||||||
ios: (
|
|
||||||
<ThemedText>
|
|
||||||
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
|
|
||||||
component provides a parallax effect for the header image.
|
|
||||||
</ThemedText>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</Collapsible>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerImage: {
|
|
||||||
color: '#808080',
|
|
||||||
bottom: -90,
|
|
||||||
left: -35,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,70 +0,0 @@
|
|||||||
import { Image, StyleSheet, Platform } from 'react-native';
|
|
||||||
|
|
||||||
import { HelloWave } from '@/components/HelloWave';
|
|
||||||
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
|
||||||
headerImage={
|
|
||||||
<Image
|
|
||||||
source={require('@/assets/images/partial-react-logo.png')}
|
|
||||||
style={styles.reactLogo}
|
|
||||||
/>
|
|
||||||
}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText type="title">Welcome!</ThemedText>
|
|
||||||
<HelloWave />
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
|
|
||||||
Press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">
|
|
||||||
{Platform.select({ ios: 'cmd + d', android: 'cmd + m' })}
|
|
||||||
</ThemedText>{' '}
|
|
||||||
to open developer tools.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
Tap the Explore tab to learn more about what's included in this starter app.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
When you're ready, run{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
stepContainer: {
|
|
||||||
gap: 8,
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
reactLogo: {
|
|
||||||
height: 178,
|
|
||||||
width: 290,
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
});
|
|
0
app/(tabs)/messages.tsx
Normal file
0
app/(tabs)/settings.tsx
Normal file
@ -1,39 +0,0 @@
|
|||||||
import { ScrollViewStyleReset } from 'expo-router/html';
|
|
||||||
import { type PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is web-only and used to configure the root HTML for every web page during static rendering.
|
|
||||||
* The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs.
|
|
||||||
*/
|
|
||||||
export default function Root({ children }: PropsWithChildren) {
|
|
||||||
return (
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charSet="utf-8" />
|
|
||||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
|
|
||||||
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
|
|
||||||
*/}
|
|
||||||
<ScrollViewStyleReset />
|
|
||||||
|
|
||||||
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
|
|
||||||
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
|
|
||||||
{/* Add any additional <head> elements that you want globally available on web... */}
|
|
||||||
</head>
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const responsiveBackground = `
|
|
||||||
body {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
body {
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
}`;
|
|
@ -1,32 +1,31 @@
|
|||||||
import { Link, Stack } from 'expo-router';
|
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { ThemedView } from '@/components/theme/Theme';
|
||||||
|
import { Link, Stack } from 'expo-router';
|
||||||
|
import TextButton from '@/components/theme/buttons/TextButton';
|
||||||
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
const NotFoundScreen = () => {
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
<Stack.Screen options={{ title: 'Page not found.' }} />
|
||||||
<ThemedView style={styles.container}>
|
<ThemedView style={styles.container}>
|
||||||
<ThemedText type="title">This screen doesn't exist.</ThemedText>
|
<Link href="/">
|
||||||
<Link href="/" style={styles.link}>
|
<TextButton
|
||||||
<ThemedText type="link">Go to home screen!</ThemedText>
|
width={200}
|
||||||
|
height={45}
|
||||||
|
text="Go to home screen"
|
||||||
|
fontSize={24}
|
||||||
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</ThemedView>
|
</ThemedView>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default NotFoundScreen;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: 20,
|
alignItems: 'center',
|
||||||
},
|
|
||||||
link: {
|
|
||||||
marginTop: 15,
|
|
||||||
paddingVertical: 15,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,37 +1,21 @@
|
|||||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
import { Stack } from "expo-router";
|
||||||
import { useFonts } from 'expo-font';
|
import React, { useState } from "react";
|
||||||
import { Stack } from 'expo-router';
|
import SignInScreen from "@/components/auth/SignInScreen";
|
||||||
import * as SplashScreen from 'expo-splash-screen';
|
import {
|
||||||
import { useEffect } from 'react';
|
PushNotificationManager
|
||||||
import 'react-native-reanimated';
|
} from "@/components/services/notifications/PushNotificationManager";
|
||||||
|
|
||||||
import { useColorScheme } from '@/hooks/useColorScheme';
|
|
||||||
|
|
||||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
|
||||||
SplashScreen.preventAutoHideAsync();
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const colorScheme = useColorScheme();
|
const [isSignedIn, setIsSignedIn] = useState(false);
|
||||||
const [loaded] = useFonts({
|
if (!isSignedIn)
|
||||||
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
return (<SignInScreen onSignIn={() => setIsSignedIn(true)} />);
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (loaded) {
|
|
||||||
SplashScreen.hideAsync();
|
|
||||||
}
|
|
||||||
}, [loaded]);
|
|
||||||
|
|
||||||
if (!loaded) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
<PushNotificationManager>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="+not-found" />
|
<Stack.Screen name="+not-found" />
|
||||||
</Stack>
|
</Stack>
|
||||||
</ThemeProvider>
|
</PushNotificationManager>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 4.0 MiB |
BIN
assets/images/default-profile.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 4.0 MiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 2.6 MiB |
@ -1,37 +0,0 @@
|
|||||||
import { StyleSheet } from 'react-native';
|
|
||||||
import Animated, {
|
|
||||||
useSharedValue,
|
|
||||||
useAnimatedStyle,
|
|
||||||
withTiming,
|
|
||||||
withRepeat,
|
|
||||||
withSequence,
|
|
||||||
} from 'react-native-reanimated';
|
|
||||||
|
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
|
||||||
|
|
||||||
export function HelloWave() {
|
|
||||||
const rotationAnimation = useSharedValue(0);
|
|
||||||
|
|
||||||
rotationAnimation.value = withRepeat(
|
|
||||||
withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
|
|
||||||
4 // Run the animation 4 times
|
|
||||||
);
|
|
||||||
|
|
||||||
const animatedStyle = useAnimatedStyle(() => ({
|
|
||||||
transform: [{ rotate: `${rotationAnimation.value}deg` }],
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Animated.View style={animatedStyle}>
|
|
||||||
<ThemedText style={styles.text}>👋</ThemedText>
|
|
||||||
</Animated.View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
text: {
|
|
||||||
fontSize: 28,
|
|
||||||
lineHeight: 32,
|
|
||||||
marginTop: -6,
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
import { View, type ViewProps } from 'react-native';
|
|
||||||
|
|
||||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
|
||||||
|
|
||||||
export type ThemedViewProps = ViewProps & {
|
|
||||||
lightColor?: string;
|
|
||||||
darkColor?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
|
|
||||||
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
|
||||||
|
|
||||||
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
|
||||||
}
|
|
153
components/auth/SignInScreen.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import React from "react";
|
||||||
|
import * as AppleAuthentication from "expo-apple-authentication";
|
||||||
|
import { StyleSheet, Alert } from "react-native";
|
||||||
|
import { ThemedView } from "@/components/theme/Theme";
|
||||||
|
import { useColorScheme } from "@/hooks/useColorScheme";
|
||||||
|
import * as Notifications from "expo-notifications";
|
||||||
|
import Constants from "expo-constants";
|
||||||
|
import { saveUser, saveInitialData } from "@/components/services/SecureStore";
|
||||||
|
import type { InitialData, User } from "@/constants/Types";
|
||||||
|
|
||||||
|
const SignInScreen({onSignIn}: {onSignIn: () => void}) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
|
||||||
|
const handleAppleSignIn = async () => {
|
||||||
|
try {
|
||||||
|
const credential = await AppleAuthentication.signInAsync({
|
||||||
|
requestedScopes: [
|
||||||
|
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
||||||
|
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
|
||||||
|
if (!projectId) throw new Error('Project ID not found in eas.json');
|
||||||
|
|
||||||
|
const pushToken = await Notifications.getExpoPushTokenAsync({
|
||||||
|
projectId: projectId,
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
credential.user, credential.email, credential.fullName?.givenName,
|
||||||
|
credential.fullName?.familyName, pushToken
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialData = await
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/users/getUserByAppleId` +
|
||||||
|
`?appleId=${credential.user}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(initialData);
|
||||||
|
if (initialData.status === 404 || !initialData.ok) {
|
||||||
|
if (!credential.user || !credential.email ||
|
||||||
|
!credential.fullName?.givenName || !credential.fullName?.familyName ||
|
||||||
|
!pushToken || credential.email.length === 0) {
|
||||||
|
Alert.alert(
|
||||||
|
'Sign in error',
|
||||||
|
'Unable to create an account. ' +
|
||||||
|
'Remove App from Sign in with Apple & try again.',
|
||||||
|
);
|
||||||
|
throw new Error(
|
||||||
|
'Incomplete user data. This shouldn\'t happen & means that user has ' +
|
||||||
|
'previously signed in with Apple, but their user data did not ' +
|
||||||
|
'save in the database.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const userResponse = await
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/users/createUser`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
appleId: credential.user,
|
||||||
|
email: credential.email,
|
||||||
|
fullName:
|
||||||
|
`${credential.fullName?.givenName} ${credential.fullName?.familyName}`,
|
||||||
|
pushToken: pushToken,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!userResponse.ok) {
|
||||||
|
const errorBody = await userResponse.text();
|
||||||
|
console.error(
|
||||||
|
'API error: No user returned: ',
|
||||||
|
userResponse.status, errorBody
|
||||||
|
);
|
||||||
|
throw new Error(`Failed to create user: ${userResponse.status} ${errorBody}`);
|
||||||
|
}
|
||||||
|
const user: User = await userResponse.json() as User;
|
||||||
|
await saveUser(user);
|
||||||
|
} else if (initialData.ok) {
|
||||||
|
const allData: InitialData = await initialData.json() as InitialData;
|
||||||
|
console.log('Existing user found! Saving data...');
|
||||||
|
|
||||||
|
if (allData.user.pushToken !== pushToken.data) {
|
||||||
|
const updatePushTokenResponse =
|
||||||
|
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/users/updatePushToken`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'x-api-key': process.env.NEXT_PUBLIC_API_KEY ?? '',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
userId: allData.user.id,
|
||||||
|
pushToken: pushToken.data,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!updatePushTokenResponse.ok) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to update push token: ${updatePushTokenResponse.status}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Push token is up to date.');
|
||||||
|
}
|
||||||
|
allData.user.pushToken = pushToken.data;
|
||||||
|
await saveInitialData(allData);
|
||||||
|
}
|
||||||
|
onSignIn();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error signing in:', error);
|
||||||
|
if (error.code === 'ERR_REQUEST_CANCELLED') {
|
||||||
|
Alert.alert('Sign in error', 'Sign in was cancelled.');
|
||||||
|
} else {
|
||||||
|
Alert.alert(
|
||||||
|
'Sign in error',
|
||||||
|
'An error occurred while signing in. Please try again or contact support.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SignInScreen;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
width: 200,
|
||||||
|
height: 45,
|
||||||
|
},
|
||||||
|
});
|
@ -1,7 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
import { ThemedText } from '@/components/Theme';
|
||||||
import { ThemedText } from '../ThemedText';
|
|
||||||
|
|
||||||
it(`renders correctly`, () => {
|
it(`renders correctly`, () => {
|
||||||
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
const tree = renderer.create(<ThemedText>Snapshot test!</ThemedText>).toJSON();
|
@ -1,5 +1,4 @@
|
|||||||
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
||||||
|
|
||||||
import Ionicons from '@expo/vector-icons/Ionicons';
|
import Ionicons from '@expo/vector-icons/Ionicons';
|
||||||
import { type IconProps } from '@expo/vector-icons/build/createIconSet';
|
import { type IconProps } from '@expo/vector-icons/build/createIconSet';
|
||||||
import { type ComponentProps } from 'react';
|
import { type ComponentProps } from 'react';
|
||||||
|
95
components/services/SecureStore.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import * as SecureStore from "expo-secure-store";
|
||||||
|
import type {
|
||||||
|
Countdown,
|
||||||
|
InitialData,
|
||||||
|
Relationship,
|
||||||
|
RelationshipData,
|
||||||
|
User,
|
||||||
|
} from "@/constants/Types";
|
||||||
|
|
||||||
|
export const saveUser = async (user: User) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('user', JSON.stringify(user));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving user to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getUser = async () => {
|
||||||
|
try {
|
||||||
|
const user = await SecureStore.getItemAsync('user');
|
||||||
|
return user ? JSON.parse(user) as User : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting user from SecureStore:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const savePartner = async (partner: User) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('partner', JSON.stringify(partner));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving partner to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getPartner = async () => {
|
||||||
|
try {
|
||||||
|
const partner = await SecureStore.getItemAsync('partner');
|
||||||
|
return partner ? JSON.parse(partner) as User : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting partner from SecureStore:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const saveRelationship = async (relationship: Relationship) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('relationship', JSON.stringify(relationship));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving relationship to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getRelationship = async () => {
|
||||||
|
try {
|
||||||
|
const relationship = await SecureStore.getItemAsync('relationship');
|
||||||
|
return relationship ? JSON.parse(relationship) as Relationship : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting relationship from SecureStore:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const saveCountdown = async (countdown: Countdown) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('countdown', JSON.stringify(countdown));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving countdown to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getCountdown = async () => {
|
||||||
|
try {
|
||||||
|
const countdown = await SecureStore.getItemAsync('countdown');
|
||||||
|
return countdown ? JSON.parse(countdown) as Countdown : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting countdown from SecureStore:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const saveRelationshipData = async (relationshipData: RelationshipData) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('partner', JSON.stringify(relationshipData.Partner));
|
||||||
|
await SecureStore.setItemAsync('relationship', JSON.stringify(relationshipData.relationship));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving partner & relationship to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const saveInitialData = async (initialData: InitialData) => {
|
||||||
|
try {
|
||||||
|
await SecureStore.setItemAsync('user', JSON.stringify(initialData.user));
|
||||||
|
await SecureStore.setItemAsync(
|
||||||
|
'relationship', JSON.stringify(initialData.relationshipData.relationship)
|
||||||
|
);
|
||||||
|
await SecureStore.setItemAsync(
|
||||||
|
'partner', JSON.stringify(initialData.relationshipData.Partner)
|
||||||
|
);
|
||||||
|
await SecureStore.setItemAsync('countdown', JSON.stringify(initialData.countdown));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving initial data to SecureStore:', error);
|
||||||
|
}
|
||||||
|
};
|
107
components/services/notifications/PushNotificationManager.tsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React, { 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';
|
||||||
|
import type { NotificationMessage } from '@/constants/Types';
|
||||||
|
|
||||||
|
Notifications.setNotificationHandler({
|
||||||
|
handleNotification: async () => ({
|
||||||
|
shouldShowAlert: true,
|
||||||
|
shouldPlaySound: true,
|
||||||
|
shouldSetBadge: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sendPushNotification = async(expoPushToken: string, notification: NotificationMessage) => {
|
||||||
|
const message = {
|
||||||
|
to: expoPushToken,
|
||||||
|
sound: notification.sound ?? 'default',
|
||||||
|
title: notification.title,
|
||||||
|
body: notification.body,
|
||||||
|
data: notification.data ?? {},
|
||||||
|
};
|
||||||
|
await fetch('https://exp.host/--/api/v2/push/send', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Accept-encoding': 'gzip, deflate',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(message),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRegistrationError = (errorMessage: string) => {
|
||||||
|
alert(errorMessage);
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerforPushNotificationsAsync = async () => {
|
||||||
|
let token;
|
||||||
|
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
await Notifications.setNotificationChannelAsync('default', {
|
||||||
|
name: 'default',
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
|
||||||
|
if (!projectId) {
|
||||||
|
alert('Project ID not found in eas.json');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
|
||||||
|
} else {
|
||||||
|
alert('Must use physical device for Push Notifications');
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PushNotificationManager = ({children}: {children: React.ReactNode}) => {
|
||||||
|
const [expoPushToken, setExpoPushToken] = useState('');
|
||||||
|
const [notification, setNotification] =
|
||||||
|
useState<Notifications.Notification | undefined>(undefined);
|
||||||
|
const notificationListener = useRef<Notifications.Subscription>();
|
||||||
|
const responseListener = useRef<Notifications.Subscription>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerforPushNotificationsAsync()
|
||||||
|
.then(token => setExpoPushToken(token ?? ''))
|
||||||
|
.catch((error: any) => {
|
||||||
|
setExpoPushToken('');
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationListener.current = Notifications.addNotificationReceivedListener(
|
||||||
|
notification => {
|
||||||
|
setNotification(notification);
|
||||||
|
});
|
||||||
|
responseListener.current = Notifications.addNotificationResponseReceivedListener(
|
||||||
|
response => {
|
||||||
|
console.log(response);
|
||||||
|
// Handle notification response here
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
notificationListener.current &&
|
||||||
|
Notifications.removeNotificationSubscription(notificationListener.current);
|
||||||
|
responseListener.current &&
|
||||||
|
Notifications.removeNotificationSubscription(responseListener.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return ( <> {children} </> );
|
||||||
|
};
|
@ -1,13 +1,22 @@
|
|||||||
|
import { View, type ViewProps } from 'react-native';
|
||||||
import { Text, type TextProps, StyleSheet } from 'react-native';
|
import { Text, type TextProps, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
import { useThemeColor } from '@/hooks/useThemeColor';
|
import { useThemeColor } from '@/hooks/useThemeColor';
|
||||||
|
|
||||||
|
export type ThemedViewProps = ViewProps & {
|
||||||
|
lightColor?: string;
|
||||||
|
darkColor?: string;
|
||||||
|
};
|
||||||
export type ThemedTextProps = TextProps & {
|
export type ThemedTextProps = TextProps & {
|
||||||
lightColor?: string;
|
lightColor?: string;
|
||||||
darkColor?: string;
|
darkColor?: string;
|
||||||
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
|
||||||
|
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
|
||||||
|
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||||
|
}
|
||||||
|
|
||||||
export function ThemedText({
|
export function ThemedText({
|
||||||
style,
|
style,
|
||||||
lightColor,
|
lightColor,
|
55
components/theme/buttons/DefaultButton.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { StyleSheet, Pressable } from "react-native";
|
||||||
|
import { ThemedView } from "@/components/theme/Theme";
|
||||||
|
import { Colors } from '@/constants/Colors';
|
||||||
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
|
const DEFAULT_WIDTH = 320;
|
||||||
|
const DEFAULT_HEIGHT = 68;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
onPress?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Button = ({ width, height, children, onPress }: Props & React.ComponentProps<typeof Pressable>) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
return (
|
||||||
|
<ThemedView
|
||||||
|
style={[
|
||||||
|
styles.buttonContainer,
|
||||||
|
{
|
||||||
|
width: (width ?? DEFAULT_WIDTH),
|
||||||
|
height: (height ?? DEFAULT_HEIGHT),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Pressable
|
||||||
|
style={[
|
||||||
|
styles.button,
|
||||||
|
{backgroundColor: Colors[scheme].text}
|
||||||
|
]}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Pressable>
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Button;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttonContainer: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: 3,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
borderRadius: 10,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
});
|
37
components/theme/buttons/TextButton.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import Button from '@/components/theme/buttons/DefaultButton';
|
||||||
|
import { ThemedText } from "@/components/theme/Theme";
|
||||||
|
import { Colors } from '@/constants/Colors';
|
||||||
|
import { useColorScheme } from '@/hooks/useColorScheme';
|
||||||
|
|
||||||
|
const DEFAULT_FONT_SIZE = 16;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
text: string;
|
||||||
|
fontSize?: number;
|
||||||
|
onPress?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TextButton = ({ width, height, text, fontSize, onPress }: Props ) => {
|
||||||
|
const scheme = useColorScheme() ?? 'dark';
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
<ThemedText
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
color: Colors[scheme].text,
|
||||||
|
fontSize: fontSize ?? DEFAULT_FONT_SIZE
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</ThemedText>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default TextButton;
|
131
constants/Types.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/* Types */
|
||||||
|
|
||||||
|
// User Table in DB
|
||||||
|
export type User = {
|
||||||
|
id: number;
|
||||||
|
appleId: string | null;
|
||||||
|
email: string;
|
||||||
|
fullName: string;
|
||||||
|
pfpUrl: string | null;
|
||||||
|
pushToken: string;
|
||||||
|
createdAt: Date;
|
||||||
|
metadata?: Record<string, string>;
|
||||||
|
};
|
||||||
|
// Relationship Table in DB
|
||||||
|
export type Relationship = {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
requestorId: number;
|
||||||
|
isAccepted: boolean;
|
||||||
|
relationshipStartDate: Date;
|
||||||
|
};
|
||||||
|
export type UserRelationship = {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
relationshipId: number;
|
||||||
|
};
|
||||||
|
// Mutated Data from Relationship
|
||||||
|
// & UserRelationship Tables in DB
|
||||||
|
export type RelationshipData = {
|
||||||
|
relationship: Relationship;
|
||||||
|
Partner: User;
|
||||||
|
};
|
||||||
|
// Countdown Table in DB
|
||||||
|
export type Countdown = {
|
||||||
|
id: number;
|
||||||
|
relationshipId: number;
|
||||||
|
title: string;
|
||||||
|
date: Date;
|
||||||
|
createdAt: Date;
|
||||||
|
};
|
||||||
|
// Mutated Data for Login
|
||||||
|
// API Response
|
||||||
|
export type InitialData = {
|
||||||
|
user: User;
|
||||||
|
relationshipData: RelationshipData;
|
||||||
|
countdown: Countdown;
|
||||||
|
};
|
||||||
|
// Message Table in DB
|
||||||
|
export type Message = {
|
||||||
|
id: number;
|
||||||
|
senderId: number;
|
||||||
|
receiverId: number;
|
||||||
|
text: string;
|
||||||
|
createdAt: Date;
|
||||||
|
isRead: boolean;
|
||||||
|
hasLocation: boolean;
|
||||||
|
hasMedia: boolean;
|
||||||
|
hasQuickReply: boolean;
|
||||||
|
};
|
||||||
|
// MessageMedia Table in DB
|
||||||
|
export type MessageMedia = {
|
||||||
|
id: number;
|
||||||
|
messageId: number;
|
||||||
|
mediaType:
|
||||||
|
'image' | 'video' | 'audio' | 'file';
|
||||||
|
url: string;
|
||||||
|
size?: number;
|
||||||
|
metadata?: string;
|
||||||
|
order: number;
|
||||||
|
};
|
||||||
|
// MessageLocation Table in DB
|
||||||
|
export type MessageLocation = {
|
||||||
|
id: number;
|
||||||
|
messageId: number;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
};
|
||||||
|
// Quick Reply Table in DB
|
||||||
|
export type QuickReply = {
|
||||||
|
id: number;
|
||||||
|
messageId: number;
|
||||||
|
type: 'radio' | 'checkbox';
|
||||||
|
keepIt: boolean;
|
||||||
|
};
|
||||||
|
// Quick Reply Option Table in DB
|
||||||
|
export type QuickReplyOption = {
|
||||||
|
id: number;
|
||||||
|
quickReplyId: number;
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GCUser = {
|
||||||
|
_id: number;
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
};
|
||||||
|
export type GCQuickReplies = {
|
||||||
|
type: 'radio' | 'checkbox';
|
||||||
|
values: GCQuickReplyOptions[];
|
||||||
|
keepIt?: boolean;
|
||||||
|
};
|
||||||
|
export type GCQuickReplyOptions = {
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
export type GCLocation = {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
};
|
||||||
|
export type GCMessage = {
|
||||||
|
_id: number;
|
||||||
|
text: string;
|
||||||
|
createdAt: Date;
|
||||||
|
user: GCUser;
|
||||||
|
image?: string;
|
||||||
|
video?: string;
|
||||||
|
audio?: string;
|
||||||
|
location?: GCLocation;
|
||||||
|
system?: boolean;
|
||||||
|
sent?: boolean;
|
||||||
|
received?: boolean;
|
||||||
|
pending?: boolean;
|
||||||
|
quickReplies?: GCQuickReplies;
|
||||||
|
};
|
||||||
|
export type NotificationMessage = {
|
||||||
|
sound?: string;
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
data?: any;
|
||||||
|
};
|
@ -4,5 +4,5 @@
|
|||||||
// to render different styles on the client and server, these aren't directly supported in React Native
|
// to render different styles on the client and server, these aren't directly supported in React Native
|
||||||
// but can be achieved using a styling library like Nativewind.
|
// but can be achieved using a styling library like Nativewind.
|
||||||
export function useColorScheme() {
|
export function useColorScheme() {
|
||||||
return 'light';
|
return 'dark';
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ export function useThemeColor(
|
|||||||
props: { light?: string; dark?: string },
|
props: { light?: string; dark?: string },
|
||||||
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
|
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
|
||||||
) {
|
) {
|
||||||
const theme = useColorScheme() ?? 'light';
|
const theme = useColorScheme() ?? 'dark';
|
||||||
const colorFromProps = props[theme];
|
const colorFromProps = props[theme];
|
||||||
|
|
||||||
if (colorFromProps) {
|
if (colorFromProps) {
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
"reset-project": "node ./scripts/reset-project.js",
|
|
||||||
"android": "expo start --android",
|
"android": "expo start --android",
|
||||||
"ios": "expo start --ios",
|
"ios": "expo start --ios",
|
||||||
"web": "expo start --web",
|
"web": "expo start --web",
|
||||||
@ -18,10 +17,15 @@
|
|||||||
"@expo/vector-icons": "^14.0.2",
|
"@expo/vector-icons": "^14.0.2",
|
||||||
"@react-navigation/native": "^6.0.2",
|
"@react-navigation/native": "^6.0.2",
|
||||||
"expo": "~51.0.28",
|
"expo": "~51.0.28",
|
||||||
|
"expo-apple-authentication": "~6.4.2",
|
||||||
"expo-constants": "~16.0.2",
|
"expo-constants": "~16.0.2",
|
||||||
|
"expo-device": "~6.0.2",
|
||||||
"expo-font": "~12.0.9",
|
"expo-font": "~12.0.9",
|
||||||
"expo-linking": "~6.3.1",
|
"expo-linking": "~6.3.1",
|
||||||
|
"expo-location": "~17.0.1",
|
||||||
|
"expo-notifications": "~0.28.18",
|
||||||
"expo-router": "~3.5.23",
|
"expo-router": "~3.5.23",
|
||||||
|
"expo-secure-store": "~13.0.2",
|
||||||
"expo-splash-screen": "~0.27.5",
|
"expo-splash-screen": "~0.27.5",
|
||||||
"expo-status-bar": "~1.12.1",
|
"expo-status-bar": "~1.12.1",
|
||||||
"expo-system-ui": "~3.0.7",
|
"expo-system-ui": "~3.0.7",
|
||||||
@ -30,6 +34,8 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-native": "0.74.5",
|
"react-native": "0.74.5",
|
||||||
"react-native-gesture-handler": "~2.16.1",
|
"react-native-gesture-handler": "~2.16.1",
|
||||||
|
"react-native-get-random-values": "~1.11.0",
|
||||||
|
"react-native-gifted-chat": "^2.6.4",
|
||||||
"react-native-reanimated": "~3.10.1",
|
"react-native-reanimated": "~3.10.1",
|
||||||
"react-native-safe-area-context": "4.10.5",
|
"react-native-safe-area-context": "4.10.5",
|
||||||
"react-native-screens": "3.31.1",
|
"react-native-screens": "3.31.1",
|
||||||
|
254
pnpm-lock.yaml
generated
@ -17,18 +17,33 @@ importers:
|
|||||||
expo:
|
expo:
|
||||||
specifier: ~51.0.28
|
specifier: ~51.0.28
|
||||||
version: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
version: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
expo-apple-authentication:
|
||||||
|
specifier: ~6.4.2
|
||||||
|
version: 6.4.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
expo-constants:
|
expo-constants:
|
||||||
specifier: ~16.0.2
|
specifier: ~16.0.2
|
||||||
version: 16.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
version: 16.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
|
expo-device:
|
||||||
|
specifier: ~6.0.2
|
||||||
|
version: 6.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
expo-font:
|
expo-font:
|
||||||
specifier: ~12.0.9
|
specifier: ~12.0.9
|
||||||
version: 12.0.10(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
version: 12.0.10(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
expo-linking:
|
expo-linking:
|
||||||
specifier: ~6.3.1
|
specifier: ~6.3.1
|
||||||
version: 6.3.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
version: 6.3.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
|
expo-location:
|
||||||
|
specifier: ~17.0.1
|
||||||
|
version: 17.0.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
|
expo-notifications:
|
||||||
|
specifier: ~0.28.18
|
||||||
|
version: 0.28.18(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
expo-router:
|
expo-router:
|
||||||
specifier: ~3.5.23
|
specifier: ~3.5.23
|
||||||
version: 3.5.23(k73ovpznblgccxklktj7qyihii)
|
version: 3.5.23(k73ovpznblgccxklktj7qyihii)
|
||||||
|
expo-secure-store:
|
||||||
|
specifier: ~13.0.2
|
||||||
|
version: 13.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
expo-splash-screen:
|
expo-splash-screen:
|
||||||
specifier: ~0.27.5
|
specifier: ~0.27.5
|
||||||
version: 0.27.6(expo-modules-autolinking@1.11.3)(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
version: 0.27.6(expo-modules-autolinking@1.11.3)(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
@ -53,6 +68,12 @@ importers:
|
|||||||
react-native-gesture-handler:
|
react-native-gesture-handler:
|
||||||
specifier: ~2.16.1
|
specifier: ~2.16.1
|
||||||
version: 2.16.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
version: 2.16.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
|
react-native-get-random-values:
|
||||||
|
specifier: ~1.11.0
|
||||||
|
version: 1.11.0(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))
|
||||||
|
react-native-gifted-chat:
|
||||||
|
specifier: ^2.6.4
|
||||||
|
version: 2.6.4(react-native-get-random-values@1.11.0(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)))(react-native-reanimated@3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.10.5(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
react-native-reanimated:
|
react-native-reanimated:
|
||||||
specifier: ~3.10.1
|
specifier: ~3.10.1
|
||||||
version: 3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
version: 3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
@ -924,6 +945,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
expo-modules-autolinking: '>=0.8.1'
|
expo-modules-autolinking: '>=0.8.1'
|
||||||
|
|
||||||
|
'@expo/react-native-action-sheet@4.1.0':
|
||||||
|
resolution: {integrity: sha512-RILoWhREgjMdr1NUSmZa/cHg8onV2YPDAMOy0iIP1c3H7nT9QQZf5dQNHK8ehcLM82sarVxriBJyYSSHAx7j6w==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=18.0.0'
|
||||||
|
|
||||||
'@expo/rudder-sdk-node@1.1.1':
|
'@expo/rudder-sdk-node@1.1.1':
|
||||||
resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==}
|
resolution: {integrity: sha512-uy/hS/awclDJ1S88w9UGpc6Nm9XnNUjzOAAib1A3PVAnGQIwebg8DpFqOthFBTlZxeuV/BKbZ5jmTbtNZkp1WQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -956,6 +982,9 @@ packages:
|
|||||||
'@hapi/topo@5.1.0':
|
'@hapi/topo@5.1.0':
|
||||||
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
|
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
|
||||||
|
|
||||||
|
'@ide/backoff@1.0.0':
|
||||||
|
resolution: {integrity: sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==}
|
||||||
|
|
||||||
'@isaacs/cliui@8.0.2':
|
'@isaacs/cliui@8.0.2':
|
||||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -1344,6 +1373,9 @@ packages:
|
|||||||
'@types/hammerjs@2.0.46':
|
'@types/hammerjs@2.0.46':
|
||||||
resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
|
resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
|
||||||
|
|
||||||
|
'@types/hoist-non-react-statics@3.3.5':
|
||||||
|
resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==}
|
||||||
|
|
||||||
'@types/istanbul-lib-coverage@2.0.6':
|
'@types/istanbul-lib-coverage@2.0.6':
|
||||||
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
|
||||||
|
|
||||||
@ -1365,6 +1397,12 @@ packages:
|
|||||||
'@types/json-schema@7.0.15':
|
'@types/json-schema@7.0.15':
|
||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
|
||||||
|
'@types/lodash.isequal@4.5.8':
|
||||||
|
resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==}
|
||||||
|
|
||||||
|
'@types/lodash@4.17.10':
|
||||||
|
resolution: {integrity: sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==}
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||||
|
|
||||||
@ -1552,6 +1590,9 @@ packages:
|
|||||||
asap@2.0.6:
|
asap@2.0.6:
|
||||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||||
|
|
||||||
|
assert@2.1.0:
|
||||||
|
resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
|
||||||
|
|
||||||
ast-types@0.15.2:
|
ast-types@0.15.2:
|
||||||
resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==}
|
resolution: {integrity: sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -1631,6 +1672,9 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0
|
'@babel/core': ^7.0.0
|
||||||
|
|
||||||
|
badgin@1.2.3:
|
||||||
|
resolution: {integrity: sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==}
|
||||||
|
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
@ -2241,6 +2285,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
|
resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
expo-apple-authentication@6.4.2:
|
||||||
|
resolution: {integrity: sha512-X4u1n3Ql1hOpztXHbKNq4I1l4+Ff82gC6RmEeW43Eht7VE6E8PrQBpYKw+JJv8osrCJt7R5O1PZwed6WLN5oig==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
|
expo-application@5.9.1:
|
||||||
|
resolution: {integrity: sha512-uAfLBNZNahnDZLRU41ZFmNSKtetHUT9Ua557/q189ua0AWV7pQjoVAx49E4953feuvqc9swtU3ScZ/hN1XO/FQ==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-asset@10.0.10:
|
expo-asset@10.0.10:
|
||||||
resolution: {integrity: sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==}
|
resolution: {integrity: sha512-0qoTIihB79k+wGus9wy0JMKq7DdenziVx3iUkGvMAy2azscSgWH6bd2gJ9CGnhC6JRd3qTMFBL0ou/fx7WZl7A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2251,6 +2305,11 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
expo: '*'
|
expo: '*'
|
||||||
|
|
||||||
|
expo-device@6.0.2:
|
||||||
|
resolution: {integrity: sha512-sCt91CuTmAuMXX4SlFOn4lIos2UIr8vb0jDstDDZXys6kErcj0uynC7bQAMreU5uRUTKMAl4MAMpKt9ufCXPBw==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-file-system@17.0.1:
|
expo-file-system@17.0.1:
|
||||||
resolution: {integrity: sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==}
|
resolution: {integrity: sha512-dYpnZJqTGj6HCYJyXAgpFkQWsiCH3HY1ek2cFZVHFoEc5tLz9gmdEgTF6nFHurvmvfmXqxi7a5CXyVm0aFYJBw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2269,6 +2328,11 @@ packages:
|
|||||||
expo-linking@6.3.1:
|
expo-linking@6.3.1:
|
||||||
resolution: {integrity: sha512-xuZCntSBGWCD/95iZ+mTUGTwHdy8Sx+immCqbUBxdvZ2TN61P02kKg7SaLS8A4a/hLrSCwrg5tMMwu5wfKr35g==}
|
resolution: {integrity: sha512-xuZCntSBGWCD/95iZ+mTUGTwHdy8Sx+immCqbUBxdvZ2TN61P02kKg7SaLS8A4a/hLrSCwrg5tMMwu5wfKr35g==}
|
||||||
|
|
||||||
|
expo-location@17.0.1:
|
||||||
|
resolution: {integrity: sha512-m+OzotzlAXO3ZZ1uqW5GC25nXW868zN+ROyBA1V4VF6jGay1ZEs4URPglCVUDzZby2F5wt24cMzqDKw2IX6nRw==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-modules-autolinking@1.11.3:
|
expo-modules-autolinking@1.11.3:
|
||||||
resolution: {integrity: sha512-oYh8EZEvYF5TYppxEKUTTJmbr8j7eRRnrIxzZtMvxLTXoujThVPMFS/cbnSnf2bFm1lq50TdDNABhmEi7z0ngQ==}
|
resolution: {integrity: sha512-oYh8EZEvYF5TYppxEKUTTJmbr8j7eRRnrIxzZtMvxLTXoujThVPMFS/cbnSnf2bFm1lq50TdDNABhmEi7z0ngQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -2276,6 +2340,11 @@ packages:
|
|||||||
expo-modules-core@1.12.26:
|
expo-modules-core@1.12.26:
|
||||||
resolution: {integrity: sha512-y8yDWjOi+rQRdO+HY+LnUlz8qzHerUaw/LUjKPU/mX8PRXP4UUPEEp5fjAwBU44xjNmYSHWZDwet4IBBE+yQUA==}
|
resolution: {integrity: sha512-y8yDWjOi+rQRdO+HY+LnUlz8qzHerUaw/LUjKPU/mX8PRXP4UUPEEp5fjAwBU44xjNmYSHWZDwet4IBBE+yQUA==}
|
||||||
|
|
||||||
|
expo-notifications@0.28.18:
|
||||||
|
resolution: {integrity: sha512-oRvr8rYhbbKNhVgcO+fj5g5g6vS0umGcElpeMSWa0KudUfOOgV6nNLvv5M89393z2Ahd7wPK4bnK8lygc0nCPQ==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-router@3.5.23:
|
expo-router@3.5.23:
|
||||||
resolution: {integrity: sha512-Re2kYcxov67hWrcjuu0+3ovsLxYn79PuX6hgtYN20MgigY5ttX79KOIBEVGTO3F3y9dxSrGHyy5Z14BcO+usGQ==}
|
resolution: {integrity: sha512-Re2kYcxov67hWrcjuu0+3ovsLxYn79PuX6hgtYN20MgigY5ttX79KOIBEVGTO3F3y9dxSrGHyy5Z14BcO+usGQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2296,6 +2365,11 @@ packages:
|
|||||||
react-native-reanimated:
|
react-native-reanimated:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
expo-secure-store@13.0.2:
|
||||||
|
resolution: {integrity: sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
|
||||||
expo-splash-screen@0.27.5:
|
expo-splash-screen@0.27.5:
|
||||||
resolution: {integrity: sha512-9rdZuLkFCfgJBxrheUsOEOIW6Rp+9NVlpSE0hgXQwbTCLTncf00IHSE8/L2NbFyeDLNjof1yZBppaV7tXHRUzA==}
|
resolution: {integrity: sha512-9rdZuLkFCfgJBxrheUsOEOIW6Rp+9NVlpSE0hgXQwbTCLTncf00IHSE8/L2NbFyeDLNjof1yZBppaV7tXHRUzA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2326,6 +2400,9 @@ packages:
|
|||||||
exponential-backoff@3.1.1:
|
exponential-backoff@3.1.1:
|
||||||
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
|
resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==}
|
||||||
|
|
||||||
|
fast-base64-decode@1.0.0:
|
||||||
|
resolution: {integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3:
|
fast-deep-equal@3.1.3:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
|
||||||
@ -2779,6 +2856,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==}
|
resolution: {integrity: sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-nan@1.3.2:
|
||||||
|
resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
is-negative-zero@2.0.3:
|
is-negative-zero@2.0.3:
|
||||||
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
|
resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -3204,6 +3285,9 @@ packages:
|
|||||||
lodash.debounce@4.0.8:
|
lodash.debounce@4.0.8:
|
||||||
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
|
|
||||||
|
lodash.isequal@4.5.0:
|
||||||
|
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
|
||||||
|
|
||||||
lodash.throttle@4.1.1:
|
lodash.throttle@4.1.1:
|
||||||
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
|
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
|
||||||
|
|
||||||
@ -3520,6 +3604,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
|
resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
object-is@1.1.6:
|
||||||
|
resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
object-keys@1.1.1:
|
object-keys@1.1.1:
|
||||||
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -3822,17 +3910,52 @@ packages:
|
|||||||
react-is@18.3.1:
|
react-is@18.3.1:
|
||||||
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
|
||||||
|
|
||||||
|
react-native-communications@2.2.1:
|
||||||
|
resolution: {integrity: sha512-5+C0X9mopI0+qxyQHzOPEi5v5rxNBQjxydPPiKMQSlX1RBIcJ8uTcqUPssQ9Mo8p6c1IKIWJUSqCj4jAmD0qVQ==}
|
||||||
|
|
||||||
react-native-gesture-handler@2.16.2:
|
react-native-gesture-handler@2.16.2:
|
||||||
resolution: {integrity: sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==}
|
resolution: {integrity: sha512-vGFlrDKlmyI+BT+FemqVxmvO7nqxU33cgXVsn6IKAFishvlG3oV2Ds67D5nPkHMea8T+s1IcuMm0bF8ntZtAyg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '*'
|
react: '*'
|
||||||
react-native: '*'
|
react-native: '*'
|
||||||
|
|
||||||
|
react-native-get-random-values@1.11.0:
|
||||||
|
resolution: {integrity: sha512-4BTbDbRmS7iPdhYLRcz3PGFIpFJBwNZg9g42iwa2P6FOv9vZj/xJc678RZXnLNZzd0qd7Q3CCF6Yd+CU2eoXKQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react-native: '>=0.56'
|
||||||
|
|
||||||
|
react-native-gifted-chat@2.6.4:
|
||||||
|
resolution: {integrity: sha512-Ut31I1w6g4hG/iMyC1mVNVPZCNAUfijbJaEbRmKQnTPG+nAvYFje57OHr5coe9w+IBWsJKKoNvaI4h8w9Il5aQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
react-native-get-random-values: '*'
|
||||||
|
react-native-reanimated: '*'
|
||||||
|
react-native-safe-area-context: '*'
|
||||||
|
|
||||||
react-native-helmet-async@2.0.4:
|
react-native-helmet-async@2.0.4:
|
||||||
resolution: {integrity: sha512-m3CkXWss6B1dd6mCMleLpzDCJJGGaHOLQsUzZv8kAASJmMfmVT4d2fx375iXKTRWT25ThBfae3dECuX5cq/8hg==}
|
resolution: {integrity: sha512-m3CkXWss6B1dd6mCMleLpzDCJJGGaHOLQsUzZv8kAASJmMfmVT4d2fx375iXKTRWT25ThBfae3dECuX5cq/8hg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^16.6.0 || ^17.0.0 || ^18.0.0
|
react: ^16.6.0 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
|
react-native-iphone-x-helper@1.3.1:
|
||||||
|
resolution: {integrity: sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==}
|
||||||
|
peerDependencies:
|
||||||
|
react-native: '>=0.42.0'
|
||||||
|
|
||||||
|
react-native-lightbox-v2@0.9.2:
|
||||||
|
resolution: {integrity: sha512-+8LwINeSWvPP69YAyWhiJQyaw4Gtu8X4EMvT3PEN615NrOeDaz8jOcIw73pzYJst3z4Z0vxvFB73iH16wCPXtw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-native: '>=0.61.0'
|
||||||
|
|
||||||
|
react-native-parsed-text@0.0.22:
|
||||||
|
resolution: {integrity: sha512-hfD83RDXZf9Fvth3DowR7j65fMnlqM9PpxZBGWkzVcUTFtqe6/yPcIoIAgrJbKn6YmtzkivmhWE2MCE4JKBXrQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '*'
|
||||||
|
react-native: '*'
|
||||||
|
|
||||||
react-native-reanimated@3.10.1:
|
react-native-reanimated@3.10.1:
|
||||||
resolution: {integrity: sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==}
|
resolution: {integrity: sha512-sfxg6vYphrDc/g4jf/7iJ7NRi+26z2+BszPmvmk0Vnrz6FL7HYljJqTf531F1x6tFmsf+FEAmuCtTUIXFLVo9w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -4488,6 +4611,10 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
ua-parser-js@0.7.39:
|
||||||
|
resolution: {integrity: sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
ua-parser-js@1.0.39:
|
ua-parser-js@1.0.39:
|
||||||
resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==}
|
resolution: {integrity: sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -4584,6 +4711,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
uuid@10.0.0:
|
||||||
|
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
uuid@7.0.3:
|
uuid@7.0.3:
|
||||||
resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
|
resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -6074,6 +6205,12 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
'@expo/react-native-action-sheet@4.1.0(react@18.2.0)':
|
||||||
|
dependencies:
|
||||||
|
'@types/hoist-non-react-statics': 3.3.5
|
||||||
|
hoist-non-react-statics: 3.3.2
|
||||||
|
react: 18.2.0
|
||||||
|
|
||||||
'@expo/rudder-sdk-node@1.1.1':
|
'@expo/rudder-sdk-node@1.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/bunyan': 4.0.1
|
'@expo/bunyan': 4.0.1
|
||||||
@ -6123,6 +6260,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/hoek': 9.3.0
|
'@hapi/hoek': 9.3.0
|
||||||
|
|
||||||
|
'@ide/backoff@1.0.0': {}
|
||||||
|
|
||||||
'@isaacs/cliui@8.0.2':
|
'@isaacs/cliui@8.0.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width: 5.1.2
|
string-width: 5.1.2
|
||||||
@ -6863,6 +7002,11 @@ snapshots:
|
|||||||
|
|
||||||
'@types/hammerjs@2.0.46': {}
|
'@types/hammerjs@2.0.46': {}
|
||||||
|
|
||||||
|
'@types/hoist-non-react-statics@3.3.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/react': 18.2.79
|
||||||
|
hoist-non-react-statics: 3.3.2
|
||||||
|
|
||||||
'@types/istanbul-lib-coverage@2.0.6': {}
|
'@types/istanbul-lib-coverage@2.0.6': {}
|
||||||
|
|
||||||
'@types/istanbul-lib-report@3.0.3':
|
'@types/istanbul-lib-report@3.0.3':
|
||||||
@ -6891,6 +7035,12 @@ snapshots:
|
|||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
|
'@types/lodash.isequal@4.5.8':
|
||||||
|
dependencies:
|
||||||
|
'@types/lodash': 4.17.10
|
||||||
|
|
||||||
|
'@types/lodash@4.17.10': {}
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.5
|
'@types/node': 22.7.5
|
||||||
@ -7073,6 +7223,14 @@ snapshots:
|
|||||||
|
|
||||||
asap@2.0.6: {}
|
asap@2.0.6: {}
|
||||||
|
|
||||||
|
assert@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.7
|
||||||
|
is-nan: 1.3.2
|
||||||
|
object-is: 1.1.6
|
||||||
|
object.assign: 4.1.5
|
||||||
|
util: 0.12.5
|
||||||
|
|
||||||
ast-types@0.15.2:
|
ast-types@0.15.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
@ -7207,6 +7365,8 @@ snapshots:
|
|||||||
babel-plugin-jest-hoist: 29.6.3
|
babel-plugin-jest-hoist: 29.6.3
|
||||||
babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.8)
|
babel-preset-current-node-syntax: 1.1.0(@babel/core@7.25.8)
|
||||||
|
|
||||||
|
badgin@1.2.3: {}
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
base64-js@1.5.1: {}
|
base64-js@1.5.1: {}
|
||||||
@ -7834,6 +7994,14 @@ snapshots:
|
|||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
|
|
||||||
|
expo-apple-authentication@6.4.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
|
||||||
|
expo-application@5.9.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
|
||||||
expo-asset@10.0.10(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
expo-asset@10.0.10(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
@ -7851,6 +8019,11 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
expo-device@6.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
ua-parser-js: 0.7.39
|
||||||
|
|
||||||
expo-file-system@17.0.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
expo-file-system@17.0.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
dependencies:
|
dependencies:
|
||||||
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
@ -7872,6 +8045,10 @@ snapshots:
|
|||||||
- expo
|
- expo
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
expo-location@17.0.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
|
||||||
expo-modules-autolinking@1.11.3:
|
expo-modules-autolinking@1.11.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
@ -7886,6 +8063,21 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
invariant: 2.2.4
|
invariant: 2.2.4
|
||||||
|
|
||||||
|
expo-notifications@0.28.18(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
'@expo/image-utils': 0.5.1
|
||||||
|
'@ide/backoff': 1.0.0
|
||||||
|
abort-controller: 3.0.0
|
||||||
|
assert: 2.1.0
|
||||||
|
badgin: 1.2.3
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
expo-application: 5.9.1(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
|
expo-constants: 16.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8)))
|
||||||
|
fs-extra: 9.1.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
- supports-color
|
||||||
|
|
||||||
expo-router@3.5.23(k73ovpznblgccxklktj7qyihii):
|
expo-router@3.5.23(k73ovpznblgccxklktj7qyihii):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/metro-runtime': 3.2.3(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))
|
'@expo/metro-runtime': 3.2.3(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))
|
||||||
@ -7913,6 +8105,10 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
expo-secure-store@13.0.2(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
|
dependencies:
|
||||||
|
expo: 51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))
|
||||||
|
|
||||||
expo-splash-screen@0.27.5(expo-modules-autolinking@1.11.3)(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
expo-splash-screen@0.27.5(expo-modules-autolinking@1.11.3)(expo@51.0.38(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/prebuild-config': 7.0.6(expo-modules-autolinking@1.11.3)
|
'@expo/prebuild-config': 7.0.6(expo-modules-autolinking@1.11.3)
|
||||||
@ -7972,6 +8168,8 @@ snapshots:
|
|||||||
|
|
||||||
exponential-backoff@3.1.1: {}
|
exponential-backoff@3.1.1: {}
|
||||||
|
|
||||||
|
fast-base64-decode@1.0.0: {}
|
||||||
|
|
||||||
fast-deep-equal@3.1.3: {}
|
fast-deep-equal@3.1.3: {}
|
||||||
|
|
||||||
fast-glob@3.3.2:
|
fast-glob@3.3.2:
|
||||||
@ -8438,6 +8636,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 2.0.1
|
is-glob: 2.0.1
|
||||||
|
|
||||||
|
is-nan@1.3.2:
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.7
|
||||||
|
define-properties: 1.2.1
|
||||||
|
|
||||||
is-negative-zero@2.0.3: {}
|
is-negative-zero@2.0.3: {}
|
||||||
|
|
||||||
is-number-object@1.0.7:
|
is-number-object@1.0.7:
|
||||||
@ -9100,6 +9303,8 @@ snapshots:
|
|||||||
|
|
||||||
lodash.debounce@4.0.8: {}
|
lodash.debounce@4.0.8: {}
|
||||||
|
|
||||||
|
lodash.isequal@4.5.0: {}
|
||||||
|
|
||||||
lodash.throttle@4.1.1: {}
|
lodash.throttle@4.1.1: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
@ -9489,6 +9694,11 @@ snapshots:
|
|||||||
|
|
||||||
object-inspect@1.13.2: {}
|
object-inspect@1.13.2: {}
|
||||||
|
|
||||||
|
object-is@1.1.6:
|
||||||
|
dependencies:
|
||||||
|
call-bind: 1.0.7
|
||||||
|
define-properties: 1.2.1
|
||||||
|
|
||||||
object-keys@1.1.1: {}
|
object-keys@1.1.1: {}
|
||||||
|
|
||||||
object.assign@4.1.5:
|
object.assign@4.1.5:
|
||||||
@ -9787,6 +9997,8 @@ snapshots:
|
|||||||
|
|
||||||
react-is@18.3.1: {}
|
react-is@18.3.1: {}
|
||||||
|
|
||||||
|
react-native-communications@2.2.1: {}
|
||||||
|
|
||||||
react-native-gesture-handler@2.16.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
react-native-gesture-handler@2.16.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@egjs/hammerjs': 2.0.17
|
'@egjs/hammerjs': 2.0.17
|
||||||
@ -9797,6 +10009,29 @@ snapshots:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
|
||||||
|
react-native-get-random-values@1.11.0(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)):
|
||||||
|
dependencies:
|
||||||
|
fast-base64-decode: 1.0.0
|
||||||
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
|
||||||
|
react-native-gifted-chat@2.6.4(react-native-get-random-values@1.11.0(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)))(react-native-reanimated@3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native-safe-area-context@4.10.5(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
||||||
|
dependencies:
|
||||||
|
'@expo/react-native-action-sheet': 4.1.0(react@18.2.0)
|
||||||
|
'@types/lodash.isequal': 4.5.8
|
||||||
|
dayjs: 1.11.13
|
||||||
|
lodash.isequal: 4.5.0
|
||||||
|
prop-types: 15.8.1
|
||||||
|
react: 18.2.0
|
||||||
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
react-native-communications: 2.2.1
|
||||||
|
react-native-get-random-values: 1.11.0(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))
|
||||||
|
react-native-iphone-x-helper: 1.3.1(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))
|
||||||
|
react-native-lightbox-v2: 0.9.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
|
react-native-parsed-text: 0.0.22(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
|
react-native-reanimated: 3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
|
react-native-safe-area-context: 4.10.5(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0)
|
||||||
|
uuid: 10.0.0
|
||||||
|
|
||||||
react-native-helmet-async@2.0.4(react@18.2.0):
|
react-native-helmet-async@2.0.4(react@18.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
invariant: 2.2.4
|
invariant: 2.2.4
|
||||||
@ -9804,6 +10039,21 @@ snapshots:
|
|||||||
react-fast-compare: 3.2.2
|
react-fast-compare: 3.2.2
|
||||||
shallowequal: 1.1.0
|
shallowequal: 1.1.0
|
||||||
|
|
||||||
|
react-native-iphone-x-helper@1.3.1(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)):
|
||||||
|
dependencies:
|
||||||
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
|
||||||
|
react-native-lightbox-v2@0.9.2(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
|
||||||
|
react-native-parsed-text@0.0.22(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
||||||
|
dependencies:
|
||||||
|
prop-types: 15.8.1
|
||||||
|
react: 18.2.0
|
||||||
|
react-native: 0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0)
|
||||||
|
|
||||||
react-native-reanimated@3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
react-native-reanimated@3.10.1(@babel/core@7.25.8)(react-native@0.74.5(@babel/core@7.25.8)(@babel/preset-env@7.25.8(@babel/core@7.25.8))(@types/react@18.2.79)(react@18.2.0))(react@18.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.25.8
|
'@babel/core': 7.25.8
|
||||||
@ -10557,6 +10807,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@5.3.3: {}
|
typescript@5.3.3: {}
|
||||||
|
|
||||||
|
ua-parser-js@0.7.39: {}
|
||||||
|
|
||||||
ua-parser-js@1.0.39: {}
|
ua-parser-js@1.0.39: {}
|
||||||
|
|
||||||
unbox-primitive@1.0.2:
|
unbox-primitive@1.0.2:
|
||||||
@ -10638,6 +10890,8 @@ snapshots:
|
|||||||
|
|
||||||
utils-merge@1.0.1: {}
|
utils-merge@1.0.1: {}
|
||||||
|
|
||||||
|
uuid@10.0.0: {}
|
||||||
|
|
||||||
uuid@7.0.3: {}
|
uuid@7.0.3: {}
|
||||||
|
|
||||||
uuid@8.3.2: {}
|
uuid@8.3.2: {}
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This script is used to reset the project to a blank state.
|
|
||||||
* It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file.
|
|
||||||
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const root = process.cwd();
|
|
||||||
const oldDirPath = path.join(root, 'app');
|
|
||||||
const newDirPath = path.join(root, 'app-example');
|
|
||||||
const newAppDirPath = path.join(root, 'app');
|
|
||||||
|
|
||||||
const indexContent = `import { Text, View } from "react-native";
|
|
||||||
|
|
||||||
export default function Index() {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text>Edit app/index.tsx to edit this screen.</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const layoutContent = `import { Stack } from "expo-router";
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
|
||||||
return (
|
|
||||||
<Stack>
|
|
||||||
<Stack.Screen name="index" />
|
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
fs.rename(oldDirPath, newDirPath, (error) => {
|
|
||||||
if (error) {
|
|
||||||
return console.error(`Error renaming directory: ${error}`);
|
|
||||||
}
|
|
||||||
console.log('/app moved to /app-example.');
|
|
||||||
|
|
||||||
fs.mkdir(newAppDirPath, { recursive: true }, (error) => {
|
|
||||||
if (error) {
|
|
||||||
return console.error(`Error creating new app directory: ${error}`);
|
|
||||||
}
|
|
||||||
console.log('New /app directory created.');
|
|
||||||
|
|
||||||
const indexPath = path.join(newAppDirPath, 'index.tsx');
|
|
||||||
fs.writeFile(indexPath, indexContent, (error) => {
|
|
||||||
if (error) {
|
|
||||||
return console.error(`Error creating index.tsx: ${error}`);
|
|
||||||
}
|
|
||||||
console.log('app/index.tsx created.');
|
|
||||||
|
|
||||||
const layoutPath = path.join(newAppDirPath, '_layout.tsx');
|
|
||||||
fs.writeFile(layoutPath, layoutContent, (error) => {
|
|
||||||
if (error) {
|
|
||||||
return console.error(`Error creating _layout.tsx: ${error}`);
|
|
||||||
}
|
|
||||||
console.log('app/_layout.tsx created.');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|