diff --git a/.gitignore b/.gitignore
index 700564e..f4b6515 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,9 @@ yarn-error.*
.DS_Store
*.pem
+# Apple Secret
+AuthKey_*.p8
+
# local env files
.env
.env*.local
diff --git a/.prettierrc b/.prettierrc
index cc73513..3e4aa83 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -4,5 +4,6 @@
"useTabs": false,
"singleQuote": true,
"jsxSingleQuote": true,
- "trailingComma": "all"
+ "trailingComma": "all",
+ "semi": true
}
diff --git a/app.json b/app.json
index 9715ee8..928863b 100644
--- a/app.json
+++ b/app.json
@@ -9,6 +9,7 @@
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
+ "usesAppleSignIn": true,
"supportsTablet": true,
"bundleIdentifier": "com.gibbyb.techtrackerexpo"
},
@@ -35,7 +36,8 @@
"backgroundColor": "#ffffff"
}
],
- "expo-secure-store"
+ "expo-secure-store",
+ "expo-apple-authentication"
],
"experiments": {
"typedRoutes": true
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index e1e3489..db364e6 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -37,16 +37,14 @@ const TabLayout = () => {
name='index'
options={{
title: 'Home',
- tabBarIcon: ({ color }) =>
- ,
+ tabBarIcon: ({ color }) => ,
}}
/>
- ,
+ tabBarIcon: ({ color }) => ,
}}
/>
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index 91f6d3f..cd221da 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -1,23 +1,20 @@
import { Image, StyleSheet, Platform } from 'react-native';
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
-import { ThemedText, ThemedView } from '@/components/theme/Theme';
+import { ThemedText, ThemedView } from '@/components/theme';
const HomeScreen = () => {
return (
+
}
headerTitle={
- Tech Tracker
+
+ Tech Tracker
+
}
>
-
-
-
+
);
};
diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx
index e9a3b37..d3da691 100644
--- a/app/(tabs)/settings.tsx
+++ b/app/(tabs)/settings.tsx
@@ -1,9 +1,8 @@
import { StyleSheet, Image, Platform } from 'react-native';
-import { Collapsible } from '@/components/default/Collapsible';
-import { ExternalLink } from '@/components/default/ExternalLink';
import ParallaxScrollView from '@/components/default/ParallaxScrollView';
-import { ThemedText, ThemedView } from '@/components/theme/Theme';
+import { ThemedText, ThemedView } from '@/components/theme';
import { IconSymbol } from '@/components/ui/IconSymbol';
+import Logout_Button from '@/components/auth/Logout_Button';
const TabTwoScreen = () => {
return (
@@ -17,6 +16,7 @@ const TabTwoScreen = () => {
}
>
+
);
};
diff --git a/app/+not-found.tsx b/app/+not-found.tsx
index 59a6497..9637b39 100644
--- a/app/+not-found.tsx
+++ b/app/+not-found.tsx
@@ -1,7 +1,6 @@
import { Link, Stack } from 'expo-router';
import { StyleSheet } from 'react-native';
-import { ThemedText, ThemedView } from '@/components/theme/Theme';
-import TextButton from '@/components/theme/buttons/TextButton';
+import { ThemedText, ThemedView, ThemedTextButton } from '@/components/theme';
const NotFoundScreen = () => {
return (
@@ -10,7 +9,7 @@ const NotFoundScreen = () => {
This screen doesn't exist.
-
+
>
diff --git a/app/_layout.tsx b/app/_layout.tsx
index efe8a45..b231886 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -3,19 +3,36 @@ import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import 'react-native-reanimated';
+import { ThemedView } from '@/components/theme';
+import { Session } from '@supabase/supabase-js';
import { useColorScheme } from '@/hooks/useColorScheme';
+import { supabase } from '@/lib/supabase';
+import LoginPage from '@/components/auth/Login';
+import Account from '@/components/Account';
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
const RootLayout = () => {
const scheme = useColorScheme() ?? 'dark';
+ const [session, setSession] = useState(null);
+
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
+ useEffect(() => {
+ supabase.auth.getSession().then(({ data: { session } }) => {
+ setSession(session);
+ });
+
+ supabase.auth.onAuthStateChange((_event, session) => {
+ setSession(session);
+ });
+ }, []);
+
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
@@ -28,12 +45,19 @@ const RootLayout = () => {
return (
-
-
-
-
+ {session && session.user ? (
+
+
+
+
+ ) : (
+
+
+
+ )}
);
};
+
export default RootLayout;
diff --git a/components/Account.tsx b/components/Account.tsx
index 9b68b4a..1604c1b 100644
--- a/components/Account.tsx
+++ b/components/Account.tsx
@@ -1,44 +1,44 @@
-import { useState, useEffect } from 'react'
-import { supabase } from '../lib/supabase'
-import { StyleSheet, View, Alert } from 'react-native'
-import { Button, Input } from '@rneui/themed'
-import { Session } from '@supabase/supabase-js'
+import { useState, useEffect } from 'react';
+import { supabase } from '../lib/supabase';
+import { StyleSheet, View, Alert } from 'react-native';
+import { Button, Input } from '@rneui/themed';
+import { Session } from '@supabase/supabase-js';
export default function Account({ session }: { session: Session }) {
- const [loading, setLoading] = useState(true)
- const [username, setUsername] = useState('')
- const [website, setWebsite] = useState('')
- const [avatarUrl, setAvatarUrl] = useState('')
+ const [loading, setLoading] = useState(true);
+ const [username, setUsername] = useState('');
+ const [website, setWebsite] = useState('');
+ const [avatarUrl, setAvatarUrl] = useState('');
useEffect(() => {
- if (session) getProfile()
- }, [session])
+ if (session) getProfile();
+ }, [session]);
async function getProfile() {
try {
- setLoading(true)
- if (!session?.user) throw new Error('No user on the session!')
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
const { data, error, status } = await supabase
.from('profiles')
.select(`username, website, avatar_url`)
.eq('id', session?.user.id)
- .single()
+ .single();
if (error && status !== 406) {
- throw error
+ throw error;
}
if (data) {
- setUsername(data.username)
- setWebsite(data.website)
- setAvatarUrl(data.avatar_url)
+ setUsername(data.username);
+ setWebsite(data.website);
+ setAvatarUrl(data.avatar_url);
}
} catch (error) {
if (error instanceof Error) {
- Alert.alert(error.message)
+ Alert.alert(error.message);
}
} finally {
- setLoading(false)
+ setLoading(false);
}
}
@@ -47,13 +47,13 @@ export default function Account({ session }: { session: Session }) {
website,
avatar_url,
}: {
- username: string
- website: string
- avatar_url: string
+ username: string;
+ website: string;
+ avatar_url: string;
}) {
try {
- setLoading(true)
- if (!session?.user) throw new Error('No user on the session!')
+ setLoading(true);
+ if (!session?.user) throw new Error('No user on the session!');
const updates = {
id: session?.user.id,
@@ -61,32 +61,32 @@ export default function Account({ session }: { session: Session }) {
website,
avatar_url,
updated_at: new Date(),
- }
+ };
- const { error } = await supabase.from('profiles').upsert(updates)
+ const { error } = await supabase.from('profiles').upsert(updates);
if (error) {
- throw error
+ throw error;
}
} catch (error) {
if (error instanceof Error) {
- Alert.alert(error.message)
+ Alert.alert(error.message);
}
} finally {
- setLoading(false)
+ setLoading(false);
}
}
return (
-
+
- setUsername(text)} />
+ setUsername(text)} />
- setWebsite(text)} />
+ setWebsite(text)} />
@@ -98,10 +98,10 @@ export default function Account({ session }: { session: Session }) {
-
- )
+ );
}
const styles = StyleSheet.create({
@@ -117,4 +117,4 @@ const styles = StyleSheet.create({
mt20: {
marginTop: 20,
},
-})
+});
diff --git a/components/Auth.tsx b/components/Auth.tsx
deleted file mode 100644
index a8a3054..0000000
--- a/components/Auth.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React, { useState } from 'react'
-import { Alert, StyleSheet, View, AppState } from 'react-native'
-import { supabase } from '../lib/supabase'
-import { Button, Input } from '@rneui/themed'
-
-// Tells Supabase Auth to continuously refresh the session automatically if
-// the app is in the foreground. When this is added, you will continue to receive
-// `onAuthStateChange` events with the `TOKEN_REFRESHED` or `SIGNED_OUT` event
-// if the user's session is terminated. This should only be registered once.
-AppState.addEventListener('change', (state) => {
- if (state === 'active') {
- supabase.auth.startAutoRefresh()
- } else {
- supabase.auth.stopAutoRefresh()
- }
-})
-
-export default function Auth() {
- const [email, setEmail] = useState('')
- const [password, setPassword] = useState('')
- const [loading, setLoading] = useState(false)
-
- async function signInWithEmail() {
- setLoading(true)
- const { error } = await supabase.auth.signInWithPassword({
- email: email,
- password: password,
- })
-
- if (error) Alert.alert(error.message)
- setLoading(false)
- }
-
- async function signUpWithEmail() {
- setLoading(true)
- const {
- data: { session },
- error,
- } = await supabase.auth.signUp({
- email: email,
- password: password,
- })
-
- if (error) Alert.alert(error.message)
- if (!session) Alert.alert('Please check your inbox for email verification!')
- setLoading(false)
- }
-
- return (
-
-
- setEmail(text)}
- value={email}
- placeholder="email@address.com"
- autoCapitalize={'none'}
- />
-
-
- setPassword(text)}
- value={password}
- secureTextEntry={true}
- placeholder="Password"
- autoCapitalize={'none'}
- />
-
-
- signInWithEmail()} />
-
-
- signUpWithEmail()} />
-
-
- )
-}
-
-const styles = StyleSheet.create({
- container: {
- marginTop: 40,
- padding: 12,
- },
- verticallySpaced: {
- paddingTop: 4,
- paddingBottom: 4,
- alignSelf: 'stretch',
- },
- mt20: {
- marginTop: 20,
- },
-})
diff --git a/components/auth/AppleSignIn.tsx b/components/auth/AppleSignIn.tsx
new file mode 100644
index 0000000..eb2597b
--- /dev/null
+++ b/components/auth/AppleSignIn.tsx
@@ -0,0 +1,129 @@
+import React, { useState, useEffect } from 'react';
+import { StyleSheet, Alert, Platform } from 'react-native';
+import * as AppleAuthentication from 'expo-apple-authentication';
+import { supabase } from '@/lib/supabase';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type AppleSignInProps = {
+ onSignInStart?: () => void;
+ onSignInComplete?: () => void;
+ onSignInError?: (error: any) => void;
+};
+
+const AppleSignIn: React.FC = ({
+ onSignInStart,
+ onSignInComplete,
+ onSignInError,
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+ const [isAppleAuthAvailable, setIsAppleAuthAvailable] = useState(false);
+
+ useEffect(() => {
+ if (Platform.OS === 'ios') {
+ AppleAuthentication.isAvailableAsync().then(setIsAppleAuthAvailable);
+ }
+ }, []);
+
+ const handleAppleSignIn = async () => {
+ try {
+ onSignInStart?.();
+
+ // Get credentials from Apple
+ const credential = await AppleAuthentication.signInAsync({
+ requestedScopes: [
+ AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
+ AppleAuthentication.AppleAuthenticationScope.EMAIL,
+ ],
+ });
+
+ if (!credential.email) {
+ throw new Error('Email is required for Apple Sign In');
+ }
+
+ // Extract user information
+ const { email, fullName, user: appleUserId } = credential;
+
+ // Create a name from the fullName object if available
+ let name = null;
+ if (fullName?.givenName || fullName?.familyName) {
+ name = `${fullName?.givenName || ''} ${fullName?.familyName || ''}`.trim();
+ }
+
+ // Create a deterministic password based on the Apple user ID
+ // This way the user can sign in again with the same password
+ const password = `Apple-${appleUserId.substring(0, 16)}`;
+
+ // First try to sign in (in case the user already exists)
+ const { data: signInData, error: signInError } = await supabase.auth.signInWithPassword({
+ email,
+ password,
+ });
+
+ if (!signInError && signInData?.user) {
+ // User exists and signed in successfully
+ onSignInComplete?.();
+ return;
+ }
+
+ // If sign-in failed, create a new user
+ const { data: signUpData, error: signUpError } = await supabase.auth.signUp({
+ email,
+ password,
+ options: {
+ data: {
+ full_name: name,
+ }
+ }
+ });
+
+ if (signUpError) {
+ throw signUpError;
+ }
+
+ // User created successfully
+ onSignInComplete?.();
+
+ } catch (error) {
+ console.error('Apple sign in error:', error);
+
+ if (error.code === 'ERR_REQUEST_CANCELED') {
+ console.log('Sign in was canceled');
+ } else {
+ Alert.alert(
+ 'Sign in error',
+ 'An error occurred while signing in with Apple. Please try again.'
+ );
+ onSignInError?.(error);
+ }
+ }
+ };
+
+ // Only render on iOS and if Apple Authentication is available
+ if (Platform.OS !== 'ios' || !isAppleAuthAvailable) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+const styles = StyleSheet.create({
+ button: {
+ width: 320,
+ height: 50,
+ marginVertical: 10,
+ },
+});
+
+export default AppleSignIn;
diff --git a/components/auth/Login.tsx b/components/auth/Login.tsx
new file mode 100644
index 0000000..1aa80bd
--- /dev/null
+++ b/components/auth/Login.tsx
@@ -0,0 +1,162 @@
+import React, { useState, useEffect } from 'react';
+import { Alert, StyleSheet, AppState, Image, Platform } from 'react-native';
+import { supabase } from '@/lib/supabase';
+import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
+import AppleSignIn from '@/components/auth/AppleSignIn';
+
+// Tells Supabase Auth to continuously refresh the session automatically if
+// the app is in the foreground. When this is added, you will continue to receive
+// `onAuthStateChange` events with the `TOKEN_REFRESHED` or `SIGNED_OUT` event
+// if the user's session is terminated. This should only be registered once.
+if (Platform.OS !== 'web') {
+ AppState.addEventListener('change', (state) => {
+ if (state === 'active') {
+ supabase.auth.startAutoRefresh();
+ } else {
+ supabase.auth.stopAutoRefresh();
+ }
+ });
+}
+
+const LoginPage = () => {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [loading, setLoading] = useState(false);
+
+ // Set up auto-refreshing for web
+ useEffect(() => {
+ if (Platform.OS === 'web') {
+ supabase.auth.startAutoRefresh();
+ return () => {
+ supabase.auth.stopAutoRefresh();
+ };
+ }
+ }, []);
+
+ const signInWithEmail = async () => {
+ setLoading(true);
+ const { error } = await supabase.auth.signInWithPassword({
+ email: email,
+ password: password,
+ });
+ if (error) Alert.alert(error.message);
+ setLoading(false);
+ };
+
+ const signUpWithEmail = async () => {
+ setLoading(true);
+ const {
+ data: { session },
+ error,
+ } = await supabase.auth.signUp({
+ email: email,
+ password: password,
+ });
+ if (error) Alert.alert(error.message);
+ else if (!session) Alert.alert('Please check your inbox for email verification!');
+ setLoading(false);
+ };
+
+ return (
+
+
+
+
+ Tech Tracker
+
+
+
+
+ setEmail(text)}
+ value={email}
+ placeholder='email@address.com'
+ />
+
+
+
+ setPassword(text)}
+ value={password}
+ secureTextEntry={true}
+ placeholder='Password'
+ />
+
+
+
+ signInWithEmail()}
+ fontSize={24}
+ />
+
+
+
+ signUpWithEmail()}
+ fontSize={24}
+ />
+
+
+ {/* Apple Sign In - Only shows on iOS */}
+
+ setLoading(true)}
+ onSignInComplete={() => setLoading(false)}
+ onSignInError={() => setLoading(false)}
+ />
+
+
+ );
+};
+
+export default LoginPage;
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 12,
+ height: '100%',
+ },
+ verticallySpaced: {
+ paddingTop: 4,
+ paddingBottom: 4,
+ alignItems: 'center',
+ },
+ mt20: {
+ marginTop: 20,
+ },
+ reactLogo: {
+ height: 70,
+ width: 72,
+ },
+ titleContainer: {
+ marginTop: 60,
+ marginBottom: 20,
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ gap: 8,
+ },
+ headerTitle: {
+ textAlign: 'center',
+ fontSize: 48,
+ lineHeight: 64,
+ fontWeight: 'bold',
+ },
+ divider: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginVertical: 20,
+ },
+ dividerText: {
+ marginHorizontal: 10,
+ fontSize: 16,
+ opacity: 0.7,
+ },
+});
diff --git a/components/auth/Logout_Button.tsx b/components/auth/Logout_Button.tsx
new file mode 100644
index 0000000..de7a513
--- /dev/null
+++ b/components/auth/Logout_Button.tsx
@@ -0,0 +1,23 @@
+import { supabase } from '@/lib/supabase';
+import { ThemedView, ThemedText, ThemedTextButton, ThemedTextInput } from '@/components/theme';
+import { Alert, StyleSheet, AppState } from 'react-native';
+
+const Logout_Button = () => {
+ const signOut = async () => {
+ const { error } = await supabase.auth.signOut();
+ if (error) Alert.alert(error.message);
+ };
+
+ return (
+ signOut()}
+ />
+ );
+};
+export default Logout_Button;
+
+const styles = StyleSheet.create({});
diff --git a/components/default/Collapsible.tsx b/components/default/Collapsible.tsx
index 90219fb..47a2b8d 100644
--- a/components/default/Collapsible.tsx
+++ b/components/default/Collapsible.tsx
@@ -1,6 +1,6 @@
import { PropsWithChildren, useState } from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';
-import { ThemedText, ThemedView } from '@/components/theme/Theme';
+import { ThemedText, ThemedView } from '@/components/theme';
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
diff --git a/components/default/ParallaxScrollView.tsx b/components/default/ParallaxScrollView.tsx
index 71b947d..8667e7e 100644
--- a/components/default/ParallaxScrollView.tsx
+++ b/components/default/ParallaxScrollView.tsx
@@ -6,7 +6,7 @@ import Animated, {
useAnimatedStyle,
useScrollViewOffset,
} from 'react-native-reanimated';
-import { ThemedText, ThemedView } from '@/components/theme/Theme';
+import { ThemedText, ThemedView } from '@/components/theme';
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
diff --git a/components/theme/buttons/TextButton.tsx b/components/theme/buttons/TextButton.tsx
deleted file mode 100644
index 65490f5..0000000
--- a/components/theme/buttons/TextButton.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import Button from '@/components/theme/buttons/Button';
-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 (
-
-
- {text}
-
-
- );
-};
-export default TextButton;
diff --git a/components/theme/buttons/Button.tsx b/components/theme/buttons/ThemedButton.tsx
similarity index 61%
rename from components/theme/buttons/Button.tsx
rename to components/theme/buttons/ThemedButton.tsx
index 46bf696..3931666 100644
--- a/components/theme/buttons/Button.tsx
+++ b/components/theme/buttons/ThemedButton.tsx
@@ -1,25 +1,30 @@
-import { StyleSheet, Pressable } from 'react-native';
-import { ThemedView } from '@/components/theme/Theme';
+import React from 'react';
+import { StyleSheet, Pressable, PressableProps } from 'react-native';
+import ThemedView from '@/components/theme/default/ThemedView';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
-import { StyleProp, ViewStyle } from 'react-native';
const DEFAULT_WIDTH = 320;
const DEFAULT_HEIGHT = 68;
-type Props = {
+type ThemedButtonProps = PressableProps & {
width?: number;
height?: number;
- onPress?: () => void;
+ containerStyle?: object;
+ buttonStyle?: object;
};
-const Button = ({
+const ThemedButton: React.FC = ({
width,
height,
children,
- onPress,
-}: Props & React.ComponentProps) => {
+ containerStyle,
+ buttonStyle,
+ style,
+ ...restProps // This now includes onPress automatically
+}) => {
const scheme = useColorScheme() ?? 'dark';
+
return (
{children}
);
};
-export default Button;
+
+export default ThemedButton;
const styles = StyleSheet.create({
buttonContainer: {
diff --git a/components/theme/buttons/ThemedTextButton.tsx b/components/theme/buttons/ThemedTextButton.tsx
new file mode 100644
index 0000000..02ffd32
--- /dev/null
+++ b/components/theme/buttons/ThemedTextButton.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import { TextStyle, PressableProps } from 'react-native';
+import ThemedButton from '@/components/theme/buttons/ThemedButton';
+import ThemedText from '@/components/theme/default/ThemedText';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+const DEFAULT_FONT_SIZE = 16;
+
+// Extend ThemedButton props (which already extends PressableProps)
+type ThemedTextButtonProps = Omit & {
+ width?: number;
+ height?: number;
+ text: string;
+ fontSize?: number;
+ textStyle?: TextStyle;
+ containerStyle?: object;
+ buttonStyle?: object;
+};
+
+const ThemedTextButton: React.FC = ({
+ width,
+ height,
+ text,
+ fontSize,
+ textStyle,
+ containerStyle,
+ buttonStyle,
+ ...restProps // This includes onPress and all other Pressable props
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+
+ return (
+
+
+ {text}
+
+
+ );
+};
+
+export default ThemedTextButton;
diff --git a/components/theme/Theme.tsx b/components/theme/default/ThemedText.tsx
similarity index 79%
rename from components/theme/Theme.tsx
rename to components/theme/default/ThemedText.tsx
index 0defec1..7aefcc1 100644
--- a/components/theme/Theme.tsx
+++ b/components/theme/default/ThemedText.tsx
@@ -1,4 +1,4 @@
-import { View, type ViewProps } from 'react-native';
+import { type ViewProps } from 'react-native';
import { Text, type TextProps, StyleSheet } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor';
@@ -12,12 +12,7 @@ export type ThemedTextProps = TextProps & {
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
};
-export const ThemedView = ({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) => {
- const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
- return ;
-};
-
-export const ThemedText = ({
+const ThemedText = ({
style,
lightColor,
darkColor,
@@ -40,6 +35,7 @@ export const ThemedText = ({
/>
);
};
+export default ThemedText;
const styles = StyleSheet.create({
default: {
diff --git a/components/theme/default/ThemedView.tsx b/components/theme/default/ThemedView.tsx
new file mode 100644
index 0000000..411c581
--- /dev/null
+++ b/components/theme/default/ThemedView.tsx
@@ -0,0 +1,13 @@
+import { View, type ViewProps } from 'react-native';
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+export type ThemedViewProps = ViewProps & {
+ lightColor?: string;
+ darkColor?: string;
+};
+
+const ThemedView = ({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) => {
+ const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
+ return ;
+};
+export default ThemedView;
diff --git a/components/theme/index.tsx b/components/theme/index.tsx
new file mode 100644
index 0000000..526ecbd
--- /dev/null
+++ b/components/theme/index.tsx
@@ -0,0 +1,25 @@
+import ThemedText from '@/components/theme/default/ThemedText';
+import ThemedView from '@/components/theme/default/ThemedView';
+import ThemedButton from '@/components/theme/buttons/ThemedButton';
+import ThemedTextButton from '@/components/theme/buttons/ThemedTextButton';
+import ThemedTextInput from '@/components/theme/inputs/ThemedTextInput';
+import ThemedCard from '@/components/theme/ui/ThemedCard';
+import ThemedBadge from '@/components/theme/ui/ThemedBadge';
+import ThemedIcon from '@/components/theme/ui/ThemedIcon';
+import ThemedSwitch from '@/components/theme/inputs/ThemedSwitch';
+import ThemedAvatar from '@/components/theme/ui/ThemedAvatar';
+import ThemedSearchBar from '@/components/theme/inputs/ThemedSearchBar';
+
+export {
+ ThemedText,
+ ThemedView,
+ ThemedButton,
+ ThemedTextButton,
+ ThemedTextInput,
+ ThemedCard,
+ ThemedBadge,
+ ThemedIcon,
+ ThemedSwitch,
+ ThemedAvatar,
+ ThemedSearchBar,
+};
diff --git a/components/theme/inputs/ThemedSearchBar.tsx b/components/theme/inputs/ThemedSearchBar.tsx
new file mode 100644
index 0000000..cc85298
--- /dev/null
+++ b/components/theme/inputs/ThemedSearchBar.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { StyleSheet, ViewProps, DimensionValue } from 'react-native';
+import ThemedView from '@/components/theme/default/ThemedView';
+import ThemedTextInput from '@/components/theme/inputs/ThemedTextInput';
+import { Ionicons } from '@expo/vector-icons';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedSearchBarProps = ViewProps & {
+ placeholder?: string;
+ value: string;
+ onChangeText: (text: string) => void;
+ width?: DimensionValue;
+ height?: number;
+};
+
+const ThemedSearchBar: React.FC = ({
+ placeholder = 'Search',
+ value,
+ onChangeText,
+ width = '100%',
+ height = 40,
+ style,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+
+ return (
+
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ icon: {
+ position: 'absolute',
+ zIndex: 1,
+ left: 15,
+ },
+ input: {
+ paddingLeft: 45,
+ },
+});
+
+export default ThemedSearchBar;
diff --git a/components/theme/inputs/ThemedSwitch.tsx b/components/theme/inputs/ThemedSwitch.tsx
new file mode 100644
index 0000000..6047981
--- /dev/null
+++ b/components/theme/inputs/ThemedSwitch.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { Switch, SwitchProps } from 'react-native';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedSwitchProps = SwitchProps;
+
+const ThemedSwitch: React.FC = ({ ...props }) => {
+ const scheme = useColorScheme() ?? 'dark';
+
+ return (
+
+ );
+};
+
+export default ThemedSwitch;
diff --git a/components/theme/inputs/ThemedTextInput.tsx b/components/theme/inputs/ThemedTextInput.tsx
new file mode 100644
index 0000000..30cdce8
--- /dev/null
+++ b/components/theme/inputs/ThemedTextInput.tsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { StyleSheet, TextInput, TextInputProps, DimensionValue } from 'react-native';
+import ThemedView from '@/components/theme/default/ThemedView';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+const DEFAULT_WIDTH = 320;
+const DEFAULT_HEIGHT = 50;
+
+type ThemedTextInputProps = TextInputProps & {
+ width?: DimensionValue;
+ height?: DimensionValue;
+ fontSize?: number;
+ containerStyle?: object;
+};
+
+const ThemedTextInput: React.FC = ({
+ width = DEFAULT_WIDTH,
+ height = DEFAULT_HEIGHT,
+ fontSize = 16,
+ containerStyle,
+ style,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+
+ return (
+
+
+
+ );
+};
+
+export default ThemedTextInput;
+
+const styles = StyleSheet.create({
+ inputContainer: {
+ padding: 3,
+ borderRadius: 10,
+ borderWidth: 1,
+ },
+ input: {
+ width: '100%',
+ height: '100%',
+ borderRadius: 8,
+ paddingHorizontal: 15,
+ },
+});
diff --git a/components/theme/ui/ThemedAvatar.tsx b/components/theme/ui/ThemedAvatar.tsx
new file mode 100644
index 0000000..5d5cbbb
--- /dev/null
+++ b/components/theme/ui/ThemedAvatar.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { StyleSheet, Image, ImageProps, View } from 'react-native';
+import ThemedText from '@/components/theme/default/ThemedText';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedAvatarProps = Omit & {
+ size?: number;
+ source?: ImageProps['source'];
+ name?: string;
+ borderWidth?: number;
+ borderColor?: string;
+};
+
+const ThemedAvatar: React.FC = ({
+ size = 50,
+ source,
+ name,
+ borderWidth = 0,
+ borderColor,
+ style,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+ const border = borderColor || Colors[scheme].tint;
+
+ // Get initials from name
+ const getInitials = (name?: string) => {
+ if (!name) return '';
+ return name
+ .split(' ')
+ .map((part) => part[0])
+ .join('')
+ .toUpperCase()
+ .substring(0, 2);
+ };
+
+ const initials = getInitials(name);
+
+ return (
+
+ {source ? (
+
+ ) : (
+
+ {initials}
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ overflow: 'hidden',
+ },
+});
+
+export default ThemedAvatar;
diff --git a/components/theme/ui/ThemedBadge.tsx b/components/theme/ui/ThemedBadge.tsx
new file mode 100644
index 0000000..cb8df1b
--- /dev/null
+++ b/components/theme/ui/ThemedBadge.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { StyleSheet, ViewProps } from 'react-native';
+import ThemedText from '@/components/theme/default/ThemedText';
+import ThemedView from '@/components/theme/default/ThemedView';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedBadgeProps = ViewProps & {
+ value: number | string;
+ size?: number;
+ color?: string;
+ textColor?: string;
+};
+
+const ThemedBadge: React.FC = ({
+ value,
+ size = 24,
+ color,
+ textColor,
+ style,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+ const badgeColor = color || Colors[scheme].tint;
+ const badgeTextColor = textColor || Colors[scheme].background;
+
+ return (
+
+
+ {value}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ badge: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ text: {
+ fontWeight: 'bold',
+ },
+});
+
+export default ThemedBadge;
diff --git a/components/theme/ui/ThemedCard.tsx b/components/theme/ui/ThemedCard.tsx
new file mode 100644
index 0000000..8a8c995
--- /dev/null
+++ b/components/theme/ui/ThemedCard.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import { StyleSheet, ViewProps, Platform, DimensionValue } from 'react-native';
+import ThemedView from '@/components/theme/default/ThemedView';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedCardProps = ViewProps & {
+ width?: DimensionValue;
+ height?: DimensionValue;
+ padding?: number;
+ elevation?: number;
+ borderRadius?: number;
+};
+
+const ThemedCard: React.FC = ({
+ width = '100%',
+ height,
+ padding = 16,
+ elevation = 3,
+ borderRadius = 12,
+ style,
+ children,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+
+ return (
+
+ {children}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ card: {
+ overflow: 'hidden',
+ },
+});
+
+export default ThemedCard;
diff --git a/components/theme/ui/ThemedDivider.tsx b/components/theme/ui/ThemedDivider.tsx
new file mode 100644
index 0000000..a3484ce
--- /dev/null
+++ b/components/theme/ui/ThemedDivider.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { StyleSheet, ViewProps, View, DimensionValue } from 'react-native';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedDividerProps = ViewProps & {
+ orientation?: 'horizontal' | 'vertical';
+ thickness?: DimensionValue;
+ length?: DimensionValue;
+ color?: string;
+};
+
+const ThemedDivider: React.FC = ({
+ orientation = 'horizontal',
+ thickness = 1,
+ length = '100%',
+ style,
+ ...restProps
+}) => {
+ const scheme = useColorScheme() ?? 'dark';
+ const color = restProps.color || Colors[scheme].border;
+
+ return (
+
+ );
+};
+
+export default ThemedDivider;
diff --git a/components/theme/ui/ThemedIcon.tsx b/components/theme/ui/ThemedIcon.tsx
new file mode 100644
index 0000000..c79f6fa
--- /dev/null
+++ b/components/theme/ui/ThemedIcon.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { StyleSheet, ViewProps } from 'react-native';
+import { Ionicons } from '@expo/vector-icons'; // Or your preferred icon library
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+type ThemedIconProps = ViewProps & {
+ name: string;
+ size?: number;
+ color?: string;
+};
+
+const ThemedIcon: React.FC = ({ name, size = 24, color, style, ...restProps }) => {
+ const scheme = useColorScheme() ?? 'dark';
+ const iconColor = color || Colors[scheme].text;
+
+ return ;
+};
+
+export default ThemedIcon;
diff --git a/constants/Colors.ts b/constants/Colors.ts
index 071b220..4349fc6 100644
--- a/constants/Colors.ts
+++ b/constants/Colors.ts
@@ -1,5 +1,5 @@
const black = '#1f1e2c';
-const white = '#ECEDEE'
+const white = '#ECEDEE';
const darkTint = '#fff';
const lightTint = '#0a7ea4';
const darkIcon = '#9BA1A6';
@@ -18,6 +18,15 @@ export const Colors = {
tabIconSelected: lightTint,
ttBlue,
accent: ttLightBlue,
+ card: '#ffffff',
+ border: '#d0d7de',
+ notification: '#f85149',
+ placeholder: '#8b949e',
+ inactive: '#afb8c1',
+ subtle: '#f6f8fa',
+ error: '#e5484d',
+ success: '#46954a',
+ warning: '#daaa3f',
},
dark: {
text: white,
@@ -28,5 +37,14 @@ export const Colors = {
tabIconSelected: darkTint,
ttBlue,
accent: ttDarkBlue,
+ card: '#3a3b4a',
+ border: '#444c56',
+ notification: '#ff6a69',
+ placeholder: '#636e7b',
+ inactive: '#4d5560',
+ subtle: '#272934',
+ error: '#ff6369',
+ success: '#3fb950',
+ warning: '#d29922',
},
};
diff --git a/lib/supabase.ts b/lib/supabase.ts
index 8f3343f..10845cf 100644
--- a/lib/supabase.ts
+++ b/lib/supabase.ts
@@ -1,21 +1,43 @@
-import { createClient } from "@supabase/supabase-js";
-import AsyncStorage from "@react-native-async-storage/async-storage";
+// lib/supabase.ts
+import { createClient } from '@supabase/supabase-js';
+import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import * as aesjs from 'aes-js';
import 'react-native-get-random-values';
+import { Platform } from 'react-native';
-// As Expo's SecureStore does not support values larger than 2048
-// bytes, an AES-256 key is generated and stored in SecureStore, while
-// it is used to encrypt/decrypt values stored in AsyncStorage.
+// Create a web-compatible storage implementation
+class WebStorage {
+ async getItem(key: string) {
+ // In SSR context, return null
+ if (typeof window === 'undefined') {
+ return null;
+ }
+ return localStorage.getItem(key);
+ }
+
+ async removeItem(key: string) {
+ if (typeof window === 'undefined') {
+ return;
+ }
+ localStorage.removeItem(key);
+ }
+
+ async setItem(key: string, value: string) {
+ if (typeof window === 'undefined') {
+ return;
+ }
+ localStorage.setItem(key, value);
+ }
+}
+
+// Original LargeSecureStore implementation for native platforms
class LargeSecureStore {
private async _encrypt(key: string, value: string) {
const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));
-
const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));
-
await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));
-
return aesjs.utils.hex.fromBytes(encryptedBytes);
}
@@ -24,17 +46,19 @@ class LargeSecureStore {
if (!encryptionKeyHex) {
return encryptionKeyHex;
}
-
- const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
+ const cipher = new aesjs.ModeOfOperation.ctr(
+ aesjs.utils.hex.toBytes(encryptionKeyHex),
+ new aesjs.Counter(1),
+ );
const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));
-
return aesjs.utils.utf8.fromBytes(decryptedBytes);
}
async getItem(key: string) {
const encrypted = await AsyncStorage.getItem(key);
- if (!encrypted) { return encrypted; }
-
+ if (!encrypted) {
+ return encrypted;
+ }
return await this._decrypt(key, encrypted);
}
@@ -45,17 +69,18 @@ class LargeSecureStore {
async setItem(key: string, value: string) {
const encrypted = await this._encrypt(key, value);
-
await AsyncStorage.setItem(key, encrypted);
}
}
+// Choose the appropriate storage implementation based on platform
+const storage = Platform.OS === 'web' ? new WebStorage() : new LargeSecureStore();
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL as string;
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY as string;
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
- storage: new LargeSecureStore(),
+ storage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false,
diff --git a/package-lock.json b/package-lock.json
index 2b68863..7f05e38 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,14 +9,15 @@
"version": "52.0.59",
"license": "0BSD",
"dependencies": {
+ "@expo/metro-runtime": "~4.0.1",
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",
- "@rneui/themed": "^4.0.0-rc.8",
"@supabase/supabase-js": "^2.48.1",
"aes-js": "^3.1.2",
"expo": "~52.0.28",
+ "expo-apple-authentication": "~7.1.3",
"expo-blur": "~14.0.3",
"expo-constants": "~17.0.5",
"expo-dev-client": "~5.0.10",
@@ -32,6 +33,7 @@
"expo-system-ui": "~4.0.7",
"expo-updates": "~0.26.13",
"expo-web-browser": "~14.0.2",
+ "jsonwebtoken": "^9.0.2",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.6",
@@ -56,9 +58,9 @@
}
},
"node_modules/@0no-co/graphql.web": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.1.tgz",
- "integrity": "sha512-F2i3xdycesw78QCOBHmpTn7eaD2iNXGwB2gkfwxcOfBbeauYpr8RBSyJOkDrFtKtVRMclg8Sg3n1ip0ACyUuag==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.1.2.tgz",
+ "integrity": "sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==",
"license": "MIT",
"peerDependencies": {
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
@@ -2394,14 +2396,14 @@
}
},
"node_modules/@expo/config-plugins": {
- "version": "9.0.15",
- "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.15.tgz",
- "integrity": "sha512-elKY/zIpAJ40RH26iwfyp+hwgeyPgIXX0SrCSOcjeJLsMsCmMac9ewvb+AN8y4k+N7m5lD/dMZupsaateKTFwA==",
+ "version": "9.0.16",
+ "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.16.tgz",
+ "integrity": "sha512-AnJzmFB7ztM0JZBn+Ut6BQYC2WeGDzfIhBZVOIPMQbdBqvwJ7TmFEsGTGSxdwU/VqJaJK2sWxyt1zbWkpIYCEA==",
"license": "MIT",
"dependencies": {
- "@expo/config-types": "^52.0.4",
- "@expo/json-file": "~9.0.1",
- "@expo/plist": "^0.2.1",
+ "@expo/config-types": "^52.0.5",
+ "@expo/json-file": "~9.0.2",
+ "@expo/plist": "^0.2.2",
"@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2",
"debug": "^4.3.5",
@@ -2428,9 +2430,9 @@
}
},
"node_modules/@expo/config-types": {
- "version": "52.0.4",
- "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.4.tgz",
- "integrity": "sha512-oMGrb2o3niVCIfjnIHFrOoiDA9jGb0lc3G4RI1UiO//KjULBaQr3QTBoKDzZQwMqDV1AgYgSr9mgEcnX3LqhIg==",
+ "version": "52.0.5",
+ "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.5.tgz",
+ "integrity": "sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==",
"license": "MIT"
},
"node_modules/@expo/config/node_modules/@babel/code-frame": {
@@ -4125,16 +4127,16 @@
}
},
"node_modules/@react-navigation/bottom-tabs": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.2.0.tgz",
- "integrity": "sha512-1LxjgnbPyFINyf9Qr5d1YE0pYhuJayg5TCIIFQmbcX4PRhX7FKUXV7cX8OzrKXEdZi/UE/VNXugtozPAR9zgvA==",
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.2.1.tgz",
+ "integrity": "sha512-UGC7csRD/1+SJKLbaEg8K44KvkdBaEBIro0PpIRawRmHS93emf4LNfDafd8y6pReJN93OVVnqLWqcYozx4lZ4A==",
"license": "MIT",
"dependencies": {
- "@react-navigation/elements": "^2.2.5",
+ "@react-navigation/elements": "^2.2.6",
"color": "^4.2.3"
},
"peerDependencies": {
- "@react-navigation/native": "^7.0.14",
+ "@react-navigation/native": "^7.0.15",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0",
@@ -4142,12 +4144,12 @@
}
},
"node_modules/@react-navigation/core": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.3.1.tgz",
- "integrity": "sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.4.0.tgz",
+ "integrity": "sha512-URiDluFl7cLA7GtOAsEvRqPmEMlSSXadqQ3wi9+Dl43dNRMqnoF76WQ9BCXeUPLydJq4yVte9XeMPyD6a0lY1g==",
"license": "MIT",
"dependencies": {
- "@react-navigation/routers": "^7.1.2",
+ "@react-navigation/routers": "^7.2.0",
"escape-string-regexp": "^4.0.0",
"nanoid": "3.3.8",
"query-string": "^7.1.3",
@@ -4160,16 +4162,16 @@
}
},
"node_modules/@react-navigation/elements": {
- "version": "2.2.5",
- "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.2.5.tgz",
- "integrity": "sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg==",
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.2.6.tgz",
+ "integrity": "sha512-UPeaCcEDSDRaxjG+qEHbur6jmNW/f9QNCyPsUt6NMgPEdbB5Z8K8oSx2swIaiCnvUbs/K5G3MuWkqQxGj8QXXA==",
"license": "MIT",
"dependencies": {
"color": "^4.2.3"
},
"peerDependencies": {
"@react-native-masked-view/masked-view": ">= 0.2.0",
- "@react-navigation/native": "^7.0.14",
+ "@react-navigation/native": "^7.0.15",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0"
@@ -4181,12 +4183,12 @@
}
},
"node_modules/@react-navigation/native": {
- "version": "7.0.14",
- "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.14.tgz",
- "integrity": "sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw==",
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.0.15.tgz",
+ "integrity": "sha512-72PabJJ8VY3GM8i/DCutFW+ATED96ZV6NH+bW+ry1EL0ZFGHoie96H+KzTqktsrUbBw1rd9KXbEQhBQgo0N7iQ==",
"license": "MIT",
"dependencies": {
- "@react-navigation/core": "^7.3.1",
+ "@react-navigation/core": "^7.4.0",
"escape-string-regexp": "^4.0.0",
"fast-deep-equal": "^3.1.3",
"nanoid": "3.3.8",
@@ -4198,16 +4200,16 @@
}
},
"node_modules/@react-navigation/native-stack": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.2.0.tgz",
- "integrity": "sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA==",
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.2.1.tgz",
+ "integrity": "sha512-zqC6DVpO4pFZrl+8JuIgV8qk+AGdTuv+hJ5EHePmzs9gYSUrDpw6LahFCiXshwBvi9LinIw9Do7mtnQK2Q8AGA==",
"license": "MIT",
"dependencies": {
- "@react-navigation/elements": "^2.2.5",
+ "@react-navigation/elements": "^2.2.6",
"warn-once": "^0.1.1"
},
"peerDependencies": {
- "@react-navigation/native": "^7.0.14",
+ "@react-navigation/native": "^7.0.15",
"react": ">= 18.2.0",
"react-native": "*",
"react-native-safe-area-context": ">= 4.0.0",
@@ -4215,21 +4217,21 @@
}
},
"node_modules/@react-navigation/routers": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.1.2.tgz",
- "integrity": "sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.2.0.tgz",
+ "integrity": "sha512-lMyib39H4a//u+eiyt162U6TwCfI8zJbjl9ovjKtDddQ4/Vf7b8/OhyimnJH09N2CBfm4pv0gCV6Q0WnZcfaJg==",
"license": "MIT",
"dependencies": {
"nanoid": "3.3.8"
}
},
"node_modules/@remix-run/node": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.15.3.tgz",
- "integrity": "sha512-TYfS6BPhbABBpSRZ6WBA4qIWSwWvJhRVQGXCHUtgOwkuW863rcFmjh9g2Xj/IHyTmbOYPdcjHsIgZ9el4CHOKQ==",
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/node/-/node-2.16.0.tgz",
+ "integrity": "sha512-9yYBYCHYO1+bIScGAtOy5/r4BoTS8E5lpQmjWP99UxSCSiKHPEO76V9Z8mmmarTNis/FPN+sUwfmbQWNHLA2vw==",
"license": "MIT",
"dependencies": {
- "@remix-run/server-runtime": "2.15.3",
+ "@remix-run/server-runtime": "2.16.0",
"@remix-run/web-fetch": "^4.4.2",
"@web3-storage/multipart-parser": "^1.0.0",
"cookie-signature": "^1.1.0",
@@ -4250,21 +4252,21 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.22.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
- "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==",
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
+ "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@remix-run/server-runtime": {
- "version": "2.15.3",
- "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.15.3.tgz",
- "integrity": "sha512-taHBe1DEqxZNjjj6OfkSYbup+sZPjbTgUhykaI+nHqrC2NDQuTiisBXhLwtx60GctONR/x0lWhF7R9ZGC5WsHw==",
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/server-runtime/-/server-runtime-2.16.0.tgz",
+ "integrity": "sha512-gbuc4slxPi+pT47MrUYprX/wCuDlYL6H3LHZSvimWO1kDCBt8oefHzdHDPjLi4B1xzqXZomswTbuJzpZ7xRRTg==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.22.0",
+ "@remix-run/router": "1.23.0",
"@types/cookie": "^0.6.0",
"@web3-storage/multipart-parser": "^1.0.0",
"cookie": "^0.6.0",
@@ -4340,70 +4342,6 @@
"web-streams-polyfill": "^3.1.1"
}
},
- "node_modules/@rneui/base": {
- "version": "4.0.0-rc.7",
- "resolved": "https://registry.npmjs.org/@rneui/base/-/base-4.0.0-rc.7.tgz",
- "integrity": "sha512-dffzoYek3Qp+7wJzC42QjI/Fu1HOUNxFIR88t1laDrBV5QZQB55f+Vu5zLbC80/bh1b8fYtl63HTIWpORuA3Eg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/react-native-vector-icons": "^6.4.10",
- "color": "^3.2.1",
- "deepmerge": "^4.2.2",
- "hoist-non-react-statics": "^3.3.2",
- "react-native-ratings": "^8.1.0",
- "react-native-size-matters": "^0.4.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/react-native-elements"
- },
- "peerDependencies": {
- "react-native-safe-area-context": "^3.1.9 || ^4.0.0",
- "react-native-vector-icons": ">7.0.0"
- }
- },
- "node_modules/@rneui/base/node_modules/color": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
- "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "color-convert": "^1.9.3",
- "color-string": "^1.6.0"
- }
- },
- "node_modules/@rneui/base/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@rneui/base/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/@rneui/themed": {
- "version": "4.0.0-rc.8",
- "resolved": "https://registry.npmjs.org/@rneui/themed/-/themed-4.0.0-rc.8.tgz",
- "integrity": "sha512-8L/XOrL9OK/r+/iBLvx63TbIdZOXF8SIjN9eArMYm6kRbMr8m4BitXllDN8nBhBsSPNYvL6EAgjk+i2MfY4sBA==",
- "license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/react-native-elements"
- },
- "peerDependencies": {
- "@rneui/base": "4.0.0-rc.7"
- }
- },
"node_modules/@segment/loosely-validate-event": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz",
@@ -4669,9 +4607,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "22.13.5",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz",
- "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==",
+ "version": "22.13.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
+ "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
@@ -4696,39 +4634,20 @@
"version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
- "node_modules/@types/react-native": {
- "version": "0.70.19",
- "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.19.tgz",
- "integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/react-native-vector-icons": {
- "version": "6.4.18",
- "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz",
- "integrity": "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/react": "*",
- "@types/react-native": "^0.70"
- }
- },
"node_modules/@types/react-test-renderer": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.1.tgz",
@@ -4777,9 +4696,9 @@
"license": "MIT"
},
"node_modules/@urql/core": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.1.0.tgz",
- "integrity": "sha512-yC3sw8yqjbX45GbXxfiBY8GLYCiyW/hLBbQF9l3TJrv4ro00Y0ChkKaD9I2KntRxAVm9IYBqh0awX8fwWAe/Yw==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@urql/core/-/core-5.1.1.tgz",
+ "integrity": "sha512-aGh024z5v2oINGD/In6rAtVKTm4VmQ2TxKQBAtk2ZSME5dunZFcjltw4p5ENQg+5CBhZ3FHMzl0Oa+rwqiWqlg==",
"license": "MIT",
"dependencies": {
"@0no-co/graphql.web": "^1.0.5",
@@ -4787,12 +4706,12 @@
}
},
"node_modules/@urql/exchange-retry": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.0.tgz",
- "integrity": "sha512-FLt+d81gP4oiHah4hWFDApimc+/xABWMU1AMYsZ1PVB0L0YPtrMCjbOp9WMM7hBzy4gbTDrG24sio0dCfSh/HQ==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.3.1.tgz",
+ "integrity": "sha512-EEmtFu8JTuwsInqMakhLq+U3qN8ZMd5V3pX44q0EqD2imqTDsa8ikZqJ1schVrN8HljOdN+C08cwZ1/r5uIgLw==",
"license": "MIT",
"dependencies": {
- "@urql/core": "^5.0.0",
+ "@urql/core": "^5.1.1",
"wonka": "^6.3.2"
},
"peerDependencies": {
@@ -5747,6 +5666,12 @@
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
"license": "MIT"
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@@ -5829,13 +5754,13 @@
}
},
"node_modules/call-bound": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
- "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
- "call-bind-apply-helpers": "^1.0.1",
- "get-intrinsic": "^1.2.6"
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
@@ -5897,9 +5822,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001700",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz",
- "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==",
+ "version": "1.0.30001702",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz",
+ "integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==",
"funding": [
{
"type": "opencollective",
@@ -6361,12 +6286,12 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.40.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz",
- "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==",
+ "version": "3.41.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz",
+ "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.24.3"
+ "browserslist": "^4.24.4"
},
"funding": {
"type": "opencollective",
@@ -6504,6 +6429,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "devOptional": true,
"license": "MIT"
},
"node_modules/data-uri-to-buffer": {
@@ -6829,6 +6755,15 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT"
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -6836,9 +6771,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.103",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz",
- "integrity": "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA==",
+ "version": "1.5.112",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.112.tgz",
+ "integrity": "sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==",
"license": "ISC"
},
"node_modules/emittery": {
@@ -7323,6 +7258,16 @@
}
}
},
+ "node_modules/expo-apple-authentication": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/expo-apple-authentication/-/expo-apple-authentication-7.1.3.tgz",
+ "integrity": "sha512-TRaF513oDGjGx3hRiAwkMiSnKLN8BIR9Se5Gi3ttz2UUgP9y+tNHV6Ji6/oztJo9ON7zerHg2mn5Y+3B8c2vTQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/expo-asset": {
"version": "11.0.4",
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-11.0.4.tgz",
@@ -7418,9 +7363,9 @@
}
},
"node_modules/expo-eas-client": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-0.13.2.tgz",
- "integrity": "sha512-2RAAGtkO9vseoJZuW4mhJkiNQ6+FfLrX66OTMq4Qj9mRKZV2Uq/ZquxUGIeJyYqBy4vNYeKbuPd2oJtsV9LBGQ==",
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-0.13.3.tgz",
+ "integrity": "sha512-t+1F1tiDocSot8iSnrn/CjTUMvVvPV2DpafSVcticpbSzMGybEN7wcamO1t18fK7WxGXpZE9gxtd80qwv/LLqQ==",
"license": "MIT"
},
"node_modules/expo-file-system": {
@@ -7501,12 +7446,12 @@
}
},
"node_modules/expo-manifests": {
- "version": "0.15.6",
- "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.6.tgz",
- "integrity": "sha512-z+TFICrijMaqBvcJkVx8WzgmOsV6ZJGvaPNQKZr4DA6uqugFMtvAQVikDjIq7SEc3n7IgPk0GR4ZN3/KnnkeVA==",
+ "version": "0.15.7",
+ "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-0.15.7.tgz",
+ "integrity": "sha512-IVzLcPamzUi4Br96xw6JPHaa1vjYupUnMqYyV1Mtd9VQojS5hJsf5VcVzbAMZE/cFGzWLZ1oJa6QXxYjN39Uww==",
"license": "MIT",
"dependencies": {
- "@expo/config": "~10.0.9",
+ "@expo/config": "~10.0.10",
"expo-json-utils": "~0.14.0"
},
"peerDependencies": {
@@ -7800,9 +7745,9 @@
"license": "MIT"
},
"node_modules/fastq": {
- "version": "1.19.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
- "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -8039,9 +7984,9 @@
"license": "MIT"
},
"node_modules/flow-parser": {
- "version": "0.261.2",
- "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.261.2.tgz",
- "integrity": "sha512-RtunoakA3YjtpAxPSOBVW6lmP5NYmETwkpAfNkdr8Ovf86ENkbD3mtPWnswFTIUtRvjwv0i8ZSkHK+AzsUg1JA==",
+ "version": "0.262.0",
+ "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.262.0.tgz",
+ "integrity": "sha512-K3asSw4s2/sRoUC4xD2OfGi04gdYCCFRgkcwEXi5JyfFhS0HrFWLcDPp55ttv95OY5970WKl4T+7hWrnuOAUMQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
@@ -9497,14 +9442,14 @@
}
},
"node_modules/jest-expo": {
- "version": "52.0.4",
- "resolved": "https://registry.npmjs.org/jest-expo/-/jest-expo-52.0.4.tgz",
- "integrity": "sha512-6+MDQnpwWi3Cka+GvzncCEw8y8LTLiulf9RMII9MZMmML68dRp+njYvZQQutRkF+WwVZLM2id59puYAsKBL1Qg==",
+ "version": "52.0.5",
+ "resolved": "https://registry.npmjs.org/jest-expo/-/jest-expo-52.0.5.tgz",
+ "integrity": "sha512-2jjdE81tWxH5PPe5SGR9rxLZbPtuM7ihEL+43xlJIjgxcplsMpFV9Nb8r5h3IpG+aVDjMiQXqF25n9dqqD09OQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@expo/config": "~10.0.9",
- "@expo/json-file": "^9.0.1",
+ "@expo/config": "~10.0.10",
+ "@expo/json-file": "^9.0.2",
"@jest/create-cache-key-function": "^29.2.1",
"@jest/globals": "^29.2.1",
"babel-jest": "^29.2.1",
@@ -10322,6 +10267,61 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kind-of": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -10646,6 +10646,48 @@
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
@@ -10869,9 +10911,9 @@
}
},
"node_modules/metro": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro/-/metro-0.81.1.tgz",
- "integrity": "sha512-fqRu4fg8ONW7VfqWFMGgKAcOuMzyoQah2azv9Y3VyFXAmG+AoTU6YIFWqAADESCGVWuWEIvxTJhMf3jxU6jwjA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro/-/metro-0.81.3.tgz",
+ "integrity": "sha512-upilFs7z1uLKvdzFYHiVKrGT/uC7h7d53R0g/FaJoQvLfA8jQG2V69jeOcGi4wCsFYvl1zBSZvKxpQb0nA3giQ==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.24.7",
@@ -10892,21 +10934,21 @@
"hermes-parser": "0.25.1",
"image-size": "^1.0.2",
"invariant": "^2.2.4",
- "jest-worker": "^29.6.3",
+ "jest-worker": "^29.7.0",
"jsc-safe-url": "^0.2.2",
"lodash.throttle": "^4.1.1",
- "metro-babel-transformer": "0.81.1",
- "metro-cache": "0.81.1",
- "metro-cache-key": "0.81.1",
- "metro-config": "0.81.1",
- "metro-core": "0.81.1",
- "metro-file-map": "0.81.1",
- "metro-resolver": "0.81.1",
- "metro-runtime": "0.81.1",
- "metro-source-map": "0.81.1",
- "metro-symbolicate": "0.81.1",
- "metro-transform-plugins": "0.81.1",
- "metro-transform-worker": "0.81.1",
+ "metro-babel-transformer": "0.81.3",
+ "metro-cache": "0.81.3",
+ "metro-cache-key": "0.81.3",
+ "metro-config": "0.81.3",
+ "metro-core": "0.81.3",
+ "metro-file-map": "0.81.3",
+ "metro-resolver": "0.81.3",
+ "metro-runtime": "0.81.3",
+ "metro-source-map": "0.81.3",
+ "metro-symbolicate": "0.81.3",
+ "metro-transform-plugins": "0.81.3",
+ "metro-transform-worker": "0.81.3",
"mime-types": "^2.1.27",
"nullthrows": "^1.1.1",
"serialize-error": "^2.1.0",
@@ -10923,9 +10965,9 @@
}
},
"node_modules/metro-babel-transformer": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.81.1.tgz",
- "integrity": "sha512-JECKDrQaUnDmj0x/Q/c8c5YwsatVx38Lu+BfCwX9fR8bWipAzkvJocBpq5rOAJRDXRgDcPv2VO4Q4nFYrpYNQg==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.81.3.tgz",
+ "integrity": "sha512-ENqtnPy2mQZFOuKrbqHRcAwZuaYe43X+30xIF0xlkLuMyCvc0CsFzrrSK9EqrQwexhVlqaRALb0GQbBMcE/y8g==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
@@ -10953,23 +10995,23 @@
}
},
"node_modules/metro-cache": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.81.1.tgz",
- "integrity": "sha512-Uqcmn6sZ+Y0VJHM88VrG5xCvSeU7RnuvmjPmSOpEcyJJBe02QkfHL05MX2ZyGDTyZdbKCzaX0IijrTe4hN3F0Q==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.81.3.tgz",
+ "integrity": "sha512-6UelMQYjlto/79tTXu0vsTxAX4e+Bkf0tgtDL1BNx3wd68pBg8qKIYpJPaUlOIaNUzFXTBDjYwUverkEW0KAtA==",
"license": "MIT",
"dependencies": {
"exponential-backoff": "^3.1.1",
"flow-enums-runtime": "^0.0.6",
- "metro-core": "0.81.1"
+ "metro-core": "0.81.3"
},
"engines": {
"node": ">=18.18"
}
},
"node_modules/metro-cache-key": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.81.1.tgz",
- "integrity": "sha512-5fDaHR1yTvpaQuwMAeEoZGsVyvjrkw9IFAS7WixSPvaNY5YfleqoJICPc6hbXFJjvwCCpwmIYFkjqzR/qJ6yqA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.81.3.tgz",
+ "integrity": "sha512-KPsPSRUd6uRva7k7k/DqiiD8td7URQWx0RkX/Cj5+bed5zSXEg/XoQA+b+DmMxS5C7TqP61Fh3XvHx6TQRW82A==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6"
@@ -10979,42 +11021,42 @@
}
},
"node_modules/metro-config": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.81.1.tgz",
- "integrity": "sha512-VAAJmxsKIZ+Fz5/z1LVgxa32gE6+2TvrDSSx45g85WoX4EtLmdBGP3DSlpQW3DqFUfNHJCGwMLGXpJnxifd08g==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.81.3.tgz",
+ "integrity": "sha512-WpTaT0iQr5juVY50Y/cyacG2ggZqF38VshEQepT+ovPK8E/xUVxlbO5yxLSXUxxUXX3Hka9r6g64+y2WC6c/xQ==",
"license": "MIT",
"dependencies": {
"connect": "^3.6.5",
"cosmiconfig": "^5.0.5",
"flow-enums-runtime": "^0.0.6",
- "jest-validate": "^29.6.3",
- "metro": "0.81.1",
- "metro-cache": "0.81.1",
- "metro-core": "0.81.1",
- "metro-runtime": "0.81.1"
+ "jest-validate": "^29.7.0",
+ "metro": "0.81.3",
+ "metro-cache": "0.81.3",
+ "metro-core": "0.81.3",
+ "metro-runtime": "0.81.3"
},
"engines": {
"node": ">=18.18"
}
},
"node_modules/metro-core": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.81.1.tgz",
- "integrity": "sha512-4d2/+02IYqOwJs4dmM0dC8hIZqTzgnx2nzN4GTCaXb3Dhtmi/SJ3v6744zZRnithhN4lxf8TTJSHnQV75M7SSA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.81.3.tgz",
+ "integrity": "sha512-WZ+qohnpvvSWdPj1VJPUrZz+2ik29M+UUpMU6YrmzQUfDyZ6JYHhzlw5WVBtwpt/+2xTsIyrZ2C1fByT/DsLQA==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6",
"lodash.throttle": "^4.1.1",
- "metro-resolver": "0.81.1"
+ "metro-resolver": "0.81.3"
},
"engines": {
"node": ">=18.18"
}
},
"node_modules/metro-file-map": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.81.1.tgz",
- "integrity": "sha512-aY72H2ujmRfFxcsbyh83JgqFF+uQ4HFN1VhV2FmcfQG4s1bGKf2Vbkk+vtZ1+EswcBwDZFbkpvAjN49oqwGzAA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.81.3.tgz",
+ "integrity": "sha512-F+t4lnVRoauJxtr9xmI4pWIOE77/vl0IrHDGeJSI9cW6LmuqxkpOlZHTKpbs/hMAo6+KhG2JMJACQDvXDLd/GA==",
"license": "MIT",
"dependencies": {
"debug": "^2.2.0",
@@ -11022,7 +11064,7 @@
"flow-enums-runtime": "^0.0.6",
"graceful-fs": "^4.2.4",
"invariant": "^2.2.4",
- "jest-worker": "^29.6.3",
+ "jest-worker": "^29.7.0",
"micromatch": "^4.0.4",
"nullthrows": "^1.1.1",
"walker": "^1.0.7"
@@ -11047,9 +11089,9 @@
"license": "MIT"
},
"node_modules/metro-minify-terser": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.81.1.tgz",
- "integrity": "sha512-p/Qz3NNh1nebSqMlxlUALAnESo6heQrnvgHtAuxufRPtKvghnVDq9hGGex8H7z7YYLsqe42PWdt4JxTA3mgkvg==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.81.3.tgz",
+ "integrity": "sha512-912AYv3OmwcbUwzCdWbdQRk+RV6kXXluHKlhBdYFD3kr4Ece691rzlofU/Mlt9qZrhHtctD5Q8cFqOEf9Z69bQ==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6",
@@ -11060,9 +11102,9 @@
}
},
"node_modules/metro-resolver": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.81.1.tgz",
- "integrity": "sha512-E61t6fxRoYRkl6Zo3iUfCKW4DYfum/bLjcejXBMt1y3I7LFkK84TCR/Rs9OAwsMCY/7GOPB4+CREYZOtCC7CNA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.81.3.tgz",
+ "integrity": "sha512-XnjENY1c6jcsEfFVIjN/8McUIInCVgGxv5eva+9ZWeCTyiAE/L5HPj2ai/Myb349+6QuSMR0dscTkKCnOwWXdw==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6"
@@ -11072,9 +11114,9 @@
}
},
"node_modules/metro-runtime": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.81.1.tgz",
- "integrity": "sha512-pqu5j5d01rjF85V/K8SDDJ0NR3dRp6bE3z5bKVVb5O2Rx0nbR9KreUxYALQCRCcQHaYySqCg5fYbGKBHC295YQ==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.81.3.tgz",
+ "integrity": "sha512-neuGRMC2pgGKIFPbmbrxW41/SmvL7OX4i1LN+saUY2t1cZfxf9haQHUMCGhO3498uEL2N+ulKRSlQrHt6XwGaw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.0",
@@ -11085,9 +11127,9 @@
}
},
"node_modules/metro-source-map": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.81.1.tgz",
- "integrity": "sha512-1i8ROpNNiga43F0ZixAXoFE/SS3RqcRDCCslpynb+ytym0VI7pkTH1woAN2HI9pczYtPrp3Nq0AjRpsuY35ieA==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.81.3.tgz",
+ "integrity": "sha512-BHJJurmDQRn3hCbBawh/UHzPz3duMpwpE3ofImO2DoWHYzn6nSg/D4wfCN4y14d9fFLE4e0I+BAOX1HWNP4jsw==",
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.25.3",
@@ -11095,9 +11137,9 @@
"@babel/types": "^7.25.2",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
- "metro-symbolicate": "0.81.1",
+ "metro-symbolicate": "0.81.3",
"nullthrows": "^1.1.1",
- "ob1": "0.81.1",
+ "ob1": "0.81.3",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
},
@@ -11115,14 +11157,14 @@
}
},
"node_modules/metro-symbolicate": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.81.1.tgz",
- "integrity": "sha512-Lgk0qjEigtFtsM7C0miXITbcV47E1ZYIfB+m/hCraihiwRWkNUQEPCWvqZmwXKSwVE5mXA0EzQtghAvQSjZDxw==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.81.3.tgz",
+ "integrity": "sha512-LQLT6WopQmIz2SDSVh3Lw7nLzF58HpsrPYqRB7RpRXBYhYmPFIjiGaP8qqtKHXczM/5YAOJzpgt8t/OGZgh6Eg==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
- "metro-source-map": "0.81.1",
+ "metro-source-map": "0.81.3",
"nullthrows": "^1.1.1",
"source-map": "^0.5.6",
"vlq": "^1.0.0"
@@ -11144,9 +11186,9 @@
}
},
"node_modules/metro-transform-plugins": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.81.1.tgz",
- "integrity": "sha512-7L1lI44/CyjIoBaORhY9fVkoNe8hrzgxjSCQ/lQlcfrV31cZb7u0RGOQrKmUX7Bw4FpejrB70ArQ7Mse9mk7+Q==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.81.3.tgz",
+ "integrity": "sha512-4JMUXhBB5y4h3dyA272k7T7+U3+J4fSBcct0Y8Yur9ziZB/dK8fieEQg5ZPfEGsgOGI+54zTzOUqga6AgmZSNg==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
@@ -11161,9 +11203,9 @@
}
},
"node_modules/metro-transform-worker": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.81.1.tgz",
- "integrity": "sha512-M+2hVT3rEy5K7PBmGDgQNq3Zx53TjScOcO/CieyLnCRFtBGWZiSJ2+bLAXXOKyKa/y3bI3i0owxtyxuPGDwbZg==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.81.3.tgz",
+ "integrity": "sha512-KZqm9sVyBKRygUxRm+yP4DguE9R1EEv28KJhIxghNp5dcdVXBYUPe1xHoc3QVdzD9c3tf8JFzA2FBlKTlwMwNg==",
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
@@ -11171,13 +11213,13 @@
"@babel/parser": "^7.25.3",
"@babel/types": "^7.25.2",
"flow-enums-runtime": "^0.0.6",
- "metro": "0.81.1",
- "metro-babel-transformer": "0.81.1",
- "metro-cache": "0.81.1",
- "metro-cache-key": "0.81.1",
- "metro-minify-terser": "0.81.1",
- "metro-source-map": "0.81.1",
- "metro-transform-plugins": "0.81.1",
+ "metro": "0.81.3",
+ "metro-babel-transformer": "0.81.3",
+ "metro-cache": "0.81.3",
+ "metro-cache-key": "0.81.3",
+ "metro-minify-terser": "0.81.3",
+ "metro-source-map": "0.81.3",
+ "metro-transform-plugins": "0.81.3",
"nullthrows": "^1.1.1"
},
"engines": {
@@ -11666,16 +11708,16 @@
"license": "MIT"
},
"node_modules/nwsapi": {
- "version": "2.2.16",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz",
- "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==",
+ "version": "2.2.18",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.18.tgz",
+ "integrity": "sha512-p1TRH/edngVEHVbwqWnxUViEmq5znDvyB+Sik5cmuLpGOIfDf/39zLiq3swPF8Vakqn+gvNiOQAZu8djYlQILA==",
"dev": true,
"license": "MIT"
},
"node_modules/ob1": {
- "version": "0.81.1",
- "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.81.1.tgz",
- "integrity": "sha512-1PEbvI+AFvOcgdNcO79FtDI1TUO8S3lhiKOyAiyWQF3sFDDKS+aw2/BZvGlArFnSmqckwOOB9chQuIX0/OahoQ==",
+ "version": "0.81.3",
+ "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.81.3.tgz",
+ "integrity": "sha512-wd8zdH0DWsn2iDVn2zT/QURihcqoc73K8FhNCmQ16qkJaoYJLNb/N+huOwdCgsbNP8Lk/s1+dPnDETx+RzsrWA==",
"license": "MIT",
"dependencies": {
"flow-enums-runtime": "^0.0.6"
@@ -12255,9 +12297,9 @@
"license": "MIT"
},
"node_modules/prettier": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
- "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -12730,20 +12772,6 @@
"react-native": ">=0.73.0"
}
},
- "node_modules/react-native-ratings": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-8.1.0.tgz",
- "integrity": "sha512-+QOJ4G3NjVkI1D+tk4EGx1dCvVfbD2nQdkrj9cXrcAoEiwmbep4z4bZbCKmWMpQ5h2dqbxABU8/eBnbDmvAc3g==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "lodash": "^4.17.15"
- },
- "peerDependencies": {
- "react": "*",
- "react-native": "*"
- }
- },
"node_modules/react-native-reanimated": {
"version": "3.16.7",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.16.7.tgz",
@@ -12792,109 +12820,6 @@
"react-native": "*"
}
},
- "node_modules/react-native-size-matters": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.4.2.tgz",
- "integrity": "sha512-DKE3f/sdcozd24oASgkP1iGg+YU3HoajRa5k3a4wkRzpiqREq8SGX12Y5zBgAt/8IivLQoTMYkyQu1/Giuy+zQ==",
- "license": "MIT",
- "peer": true,
- "peerDependencies": {
- "react-native": "*"
- }
- },
- "node_modules/react-native-vector-icons": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.2.0.tgz",
- "integrity": "sha512-n5HGcxUuVaTf9QJPs/W22xQpC2Z9u0nb0KgLPnVltP8vdUvOp6+R26gF55kilP/fV4eL4vsAHUqUjewppJMBOQ==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "prop-types": "^15.7.2",
- "yargs": "^16.1.1"
- },
- "bin": {
- "fa-upgrade.sh": "bin/fa-upgrade.sh",
- "fa5-upgrade": "bin/fa5-upgrade.sh",
- "fa6-upgrade": "bin/fa6-upgrade.sh",
- "generate-icon": "bin/generate-icon.js"
- }
- },
- "node_modules/react-native-vector-icons/node_modules/cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
- "license": "ISC",
- "peer": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
- }
- },
- "node_modules/react-native-vector-icons/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "license": "MIT",
- "peer": true
- },
- "node_modules/react-native-vector-icons/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/react-native-vector-icons/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "ansi-regex": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/react-native-vector-icons/node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/react-native-vector-icons/node_modules/yargs-parser": {
- "version": "20.2.9",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
- "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
- "license": "ISC",
- "peer": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/react-native-web": {
"version": "0.19.13",
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.19.13.tgz",
@@ -13331,9 +13256,9 @@
"license": "ISC"
},
"node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -14543,9 +14468,9 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.11",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz",
- "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==",
+ "version": "5.3.12",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.12.tgz",
+ "integrity": "sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==",
"dev": true,
"license": "MIT",
"peer": true,
@@ -14796,9 +14721,9 @@
}
},
"node_modules/typescript": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
- "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "version": "5.8.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
@@ -14945,9 +14870,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz",
- "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
"funding": [
{
"type": "opencollective",
@@ -15328,9 +15253,9 @@
}
},
"node_modules/wonka": {
- "version": "6.3.4",
- "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.4.tgz",
- "integrity": "sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg==",
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz",
+ "integrity": "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==",
"license": "MIT"
},
"node_modules/wrap-ansi": {
diff --git a/package.json b/package.json
index 59316c6..829cffd 100644
--- a/package.json
+++ b/package.json
@@ -18,14 +18,15 @@
"preset": "jest-expo"
},
"dependencies": {
+ "@expo/metro-runtime": "~4.0.1",
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",
- "@rneui/themed": "^4.0.0-rc.8",
"@supabase/supabase-js": "^2.48.1",
"aes-js": "^3.1.2",
"expo": "~52.0.28",
+ "expo-apple-authentication": "~7.1.3",
"expo-blur": "~14.0.3",
"expo-constants": "~17.0.5",
"expo-dev-client": "~5.0.10",
@@ -34,12 +35,14 @@
"expo-insights": "~0.8.2",
"expo-linking": "~7.0.5",
"expo-router": "~4.0.17",
+ "expo-secure-store": "~14.0.1",
"expo-splash-screen": "~0.29.21",
"expo-status-bar": "~2.0.1",
"expo-symbols": "~0.2.1",
"expo-system-ui": "~4.0.7",
"expo-updates": "~0.26.13",
"expo-web-browser": "~14.0.2",
+ "jsonwebtoken": "^9.0.2",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.6",
@@ -49,8 +52,7 @@
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "~4.4.0",
"react-native-web": "~0.19.13",
- "react-native-webview": "13.12.5",
- "expo-secure-store": "~14.0.1"
+ "react-native-webview": "13.12.5"
},
"devDependencies": {
"@babel/core": "^7.25.2",
diff --git a/scripts/files_to_clipboard b/scripts/files_to_clipboard
new file mode 100755
index 0000000..9be0ff2
--- /dev/null
+++ b/scripts/files_to_clipboard
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import argparse
+from pathlib import Path
+import pyperclip
+import questionary
+
+# List of directories to exclude
+EXCLUDED_DIRS = {'node_modules', '.next', '.venv', '.git', '__pycache__', '.idea', '.vscode', 'ui'}
+
+def collect_files(project_path):
+ """
+ Collects files from the project directory, excluding specified directories and filtering by extensions.
+ Returns a list of file paths relative to the project directory.
+ """
+ collected_files = []
+
+ for root, dirs, files in os.walk(project_path):
+ # Exclude specified directories
+ dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
+
+ for file in files:
+ file_path = Path(root) / file
+ relative_path = file_path.relative_to(project_path)
+ collected_files.append(relative_path)
+
+ return collected_files
+
+def main():
+ # Parse command-line arguments
+ parser = argparse.ArgumentParser(description='Generate Markdown from selected files.')
+ parser.add_argument('path', nargs='?', default='.', help='Path to the project directory')
+ args = parser.parse_args()
+
+ project_path = Path(args.path).resolve()
+ if not project_path.is_dir():
+ print(f"Error: '{project_path}' is not a directory.")
+ sys.exit(1)
+
+ # Collect files from the project directory
+ file_list = collect_files(project_path)
+
+ if not file_list:
+ print("No files found in the project directory with the specified extensions.")
+ sys.exit(1)
+
+ # Sort file_list for better organization
+ file_list.sort()
+
+ # Interactive file selection using questionary
+ print("\nSelect the files you want to include:")
+ selected_files = questionary.checkbox(
+ "Press space to select files, and Enter when you're done:",
+ choices=[str(f) for f in file_list]
+ ).ask()
+
+ if not selected_files:
+ print("No files selected.")
+ sys.exit(1)
+
+ # Generate markdown
+ markdown_lines = []
+ markdown_lines.append('')
+
+ for selected_file in selected_files:
+ file_path = project_path / selected_file
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+ # Determine the language for code block from file extension
+ language = file_path.suffix.lstrip('.')
+ markdown_lines.append(f'{selected_file}')
+ markdown_lines.append(f'```{language}')
+ markdown_lines.append(content)
+ markdown_lines.append('```')
+ markdown_lines.append('')
+ except Exception as e:
+ print(f"Error reading file {selected_file}: {e}")
+
+ markdown_text = '\n'.join(markdown_lines)
+
+ # Copy markdown content to clipboard
+ pyperclip.copy(markdown_text)
+ print("Markdown content has been copied to the clipboard.")
+
+if __name__ == "__main__":
+ # Check if required libraries are installed
+ try:
+ import questionary
+ import pyperclip
+ except ImportError as e:
+ missing_module = e.name
+ print(f"Error: Missing required module '{missing_module}'.")
+ print(f"Please install it by running: pip install {missing_module}")
+ sys.exit(1)
+
+ main()
diff --git a/scripts/generate_apple_secret.js b/scripts/generate_apple_secret.js
new file mode 100644
index 0000000..0715662
--- /dev/null
+++ b/scripts/generate_apple_secret.js
@@ -0,0 +1,48 @@
+const fs = require('fs');
+const path = require('path');
+const jwt = require('jsonwebtoken');
+const dotenv = require('dotenv');
+
+// Load environment variables from .env file
+dotenv.config();
+
+const generateAppleSecret = (config) => {
+ const { teamId, clientId, keyId, privateKeyPath } = config;
+ const privateKey = fs.readFileSync(privateKeyPath).toString();
+ const now = Math.floor(Date.now() / 1000);
+ const payload = {
+ iss: teamId,
+ iat: now,
+ exp: now + 86400 * 180, // 180 days
+ aud: 'https://appleid.apple.com',
+ sub: clientId,
+ };
+ const headers = {
+ alg: 'ES256',
+ kid: keyId,
+ };
+ return jwt.sign(payload, privateKey, { algorithm: 'ES256', header: headers });
+};
+
+const config = {
+ teamId: process.env.APPLE_TEAM_ID || '',
+ clientId: process.env.AUTH_APPLE_ID || '',
+ keyId: process.env.APPLE_KEY_ID || '',
+ privateKeyPath: path.resolve(
+ __dirname,
+ process.env.APPLE_PRIVATE_KEY_PATH || '',
+ ),
+};
+
+if (
+ !config.teamId ||
+ !config.clientId ||
+ !config.keyId ||
+ !config.privateKeyPath
+) {
+ console.error('Missing necessary Apple configuration');
+ process.exit(1);
+}
+
+const appleSecret = generateAppleSecret(config);
+console.log(`Your Apple Secret:\n\n${appleSecret}\n`);