From c62926b8f2ca0359ebd596b3710fec57d4abfffa Mon Sep 17 00:00:00 2001 From: gibbyb Date: Wed, 5 Mar 2025 12:58:18 -0600 Subject: [PATCH] Add Sign in with Apple sorta --- .gitignore | 3 + .prettierrc | 3 +- app.json | 4 +- app/(tabs)/_layout.tsx | 6 +- app/(tabs)/index.tsx | 15 +- app/(tabs)/settings.tsx | 6 +- app/+not-found.tsx | 5 +- app/_layout.tsx | 34 +- components/Account.tsx | 72 +- components/Auth.tsx | 95 --- components/auth/AppleSignIn.tsx | 129 ++++ components/auth/Login.tsx | 162 ++++ components/auth/Logout_Button.tsx | 23 + components/default/Collapsible.tsx | 2 +- components/default/ParallaxScrollView.tsx | 2 +- components/theme/buttons/TextButton.tsx | 33 - .../buttons/{Button.tsx => ThemedButton.tsx} | 29 +- components/theme/buttons/ThemedTextButton.tsx | 56 ++ .../{Theme.tsx => default/ThemedText.tsx} | 10 +- components/theme/default/ThemedView.tsx | 13 + components/theme/index.tsx | 25 + components/theme/inputs/ThemedSearchBar.tsx | 58 ++ components/theme/inputs/ThemedSwitch.tsx | 24 + components/theme/inputs/ThemedTextInput.tsx | 69 ++ components/theme/ui/ThemedAvatar.tsx | 84 +++ components/theme/ui/ThemedBadge.tsx | 58 ++ components/theme/ui/ThemedCard.tsx | 64 ++ components/theme/ui/ThemedDivider.tsx | 37 + components/theme/ui/ThemedIcon.tsx | 20 + constants/Colors.ts | 20 +- lib/supabase.ts | 55 +- package-lock.json | 707 ++++++++---------- package.json | 8 +- scripts/files_to_clipboard | 99 +++ scripts/generate_apple_secret.js | 48 ++ 35 files changed, 1458 insertions(+), 620 deletions(-) delete mode 100644 components/Auth.tsx create mode 100644 components/auth/AppleSignIn.tsx create mode 100644 components/auth/Login.tsx create mode 100644 components/auth/Logout_Button.tsx delete mode 100644 components/theme/buttons/TextButton.tsx rename components/theme/buttons/{Button.tsx => ThemedButton.tsx} (61%) create mode 100644 components/theme/buttons/ThemedTextButton.tsx rename components/theme/{Theme.tsx => default/ThemedText.tsx} (79%) create mode 100644 components/theme/default/ThemedView.tsx create mode 100644 components/theme/index.tsx create mode 100644 components/theme/inputs/ThemedSearchBar.tsx create mode 100644 components/theme/inputs/ThemedSwitch.tsx create mode 100644 components/theme/inputs/ThemedTextInput.tsx create mode 100644 components/theme/ui/ThemedAvatar.tsx create mode 100644 components/theme/ui/ThemedBadge.tsx create mode 100644 components/theme/ui/ThemedCard.tsx create mode 100644 components/theme/ui/ThemedDivider.tsx create mode 100644 components/theme/ui/ThemedIcon.tsx create mode 100755 scripts/files_to_clipboard create mode 100644 scripts/generate_apple_secret.js 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 }) { - - ); -}; -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`);