diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..73cbdbc --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +# Copy to .env and fill in real values. +EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID=(string) Your Google Web Client ID diff --git a/.gitignore b/.gitignore index 558f569..2bbdcef 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ yarn-error.* *.pem # local env files +.env .env*.local # typescript diff --git a/GoogleService-Info.plist b/GoogleService-Info.plist new file mode 100644 index 0000000..a31c626 --- /dev/null +++ b/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 208563783798-uj5b0cm8o236k439pfqbv57r1gds98s7.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.208563783798-uj5b0cm8o236k439pfqbv57r1gds98s7 + API_KEY + AIzaSyD7eTTXkW0crVL4hVT4cn72fgzoR9Mb0co + GCM_SENDER_ID + 208563783798 + PLIST_VERSION + 1 + BUNDLE_ID + com.magicmedia.maplace + PROJECT_ID + ma-place + STORAGE_BUCKET + ma-place.firebasestorage.app + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:208563783798:ios:2359abd3461abd4fcf7757 + DATABASE_URL + https://ma-place.firebaseio.com + + \ No newline at end of file diff --git a/README.md b/README.md index 33cb239..1379682 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ Project created with ```pnpm dlx create-expo-app --template default@54```, then - Create a `global.css` file - Update `postcss.config.mjs` - Update `metro.config.js` -- XXX +- Firebase `@react-native-firebase/app` - only working with `development build` + - auth - `@react-native-firebase/auth` and `@react-native-google-signin/google-signin`. Note that the Firebase Android app has to be with generate a SHA-fingerprint in order the Google sign-in method to work. One can be generated with `./android/gradlew signingReport` and added to the Firebase Android app [generate](https://developers.google.com/android/guides/client-auth) diff --git a/app.json b/app.json index e22d0f5..01592c6 100644 --- a/app.json +++ b/app.json @@ -9,7 +9,9 @@ "userInterfaceStyle": "automatic", "newArchEnabled": true, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.magicmedia.maplace", + "googleServicesFile": "./GoogleService-Info.plist" }, "android": { "adaptiveIcon": { @@ -19,7 +21,9 @@ "monochromeImage": "./assets/images/android-icon-monochrome.png" }, "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false + "predictiveBackGestureEnabled": false, + "package": "com.magicmedia.maplace", + "googleServicesFile": "./google-services.json" }, "web": { "output": "static", @@ -28,6 +32,8 @@ }, "plugins": [ "expo-router", + "@react-native-firebase/app", + "@react-native-google-signin/google-signin", [ "expo-splash-screen", { diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx index f99109c..cae522a 100644 --- a/app/(auth)/_layout.tsx +++ b/app/(auth)/_layout.tsx @@ -1,7 +1,15 @@ -import { Stack } from "expo-router"; +import { Stack } from "expo-router"; import "@/assets/global.css"; +import { colors } from "@/constants/theme"; -export default function RootLayout() { - return ; +export default function AuthLayout() { + return ( + + ); } diff --git a/app/(auth)/sign-in.tsx b/app/(auth)/sign-in.tsx index 5b3e478..be7e560 100644 --- a/app/(auth)/sign-in.tsx +++ b/app/(auth)/sign-in.tsx @@ -1,14 +1,168 @@ +import { useState } from "react"; +import { + ActivityIndicator, + KeyboardAvoidingView, + Platform, + Pressable, + ScrollView, + TextInput, + View, +} from "react-native"; import { Link } from "expo-router"; -import { View } from "react-native"; import Text from "@/app/components/Text"; +import { friendlyAuthError, useAuth } from "@/lib/auth"; +import { cn } from "@/lib/utils"; + +export default function SignInScreen() { + const { signIn, signInWithGoogle } = useAuth(); + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); + const [googleSubmitting, setGoogleSubmitting] = useState(false); + + const canSubmit = + email.trim().length > 0 && password.length > 0 && !submitting; + + // The root layout's auth gate redirects on success, so no manual navigation here. + async function handleSignIn() { + if (!canSubmit) return; + setError(null); + setSubmitting(true); + try { + await signIn(email.trim(), password); + } catch (err) { + setError(friendlyAuthError(err)); + } finally { + setSubmitting(false); + } + } + + async function handleGoogle() { + setError(null); + setGoogleSubmitting(true); + try { + await signInWithGoogle(); + } catch (err) { + setError(friendlyAuthError(err)); + } finally { + setGoogleSubmitting(false); + } + } -export default function SignIn() { return ( - - SignIn - Go to SignUp - Home - + + + + + + M + + + ma-place + subscriptions + + + Welcome back + + Sign in to manage your places and subscriptions. + + + + + + + Email + + + + + Password + + + + {error ? {error} : null} + + + {submitting ? ( + + ) : ( + Sign in + )} + + + + + + + or + + + + + {googleSubmitting ? ( + + ) : ( + + Continue with Google + + )} + + + + Don't have an account? + + + Create account + + + + + ); } diff --git a/app/(auth)/sign-up.tsx b/app/(auth)/sign-up.tsx index 2978815..6e181a5 100644 --- a/app/(auth)/sign-up.tsx +++ b/app/(auth)/sign-up.tsx @@ -1,14 +1,160 @@ +import { useState } from "react"; +import { + ActivityIndicator, + KeyboardAvoidingView, + Platform, + Pressable, + ScrollView, + TextInput, + View, +} from "react-native"; import { Link } from "expo-router"; -import { View } from "react-native"; import Text from "@/app/components/Text"; +import { friendlyAuthError, useAuth } from "@/lib/auth"; +import { cn } from "@/lib/utils"; + +export default function SignUpScreen() { + const { signUp } = useAuth(); + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); + + const canSubmit = + !submitting && + email.trim().length > 0 && + password.length > 0 && + confirmPassword.length > 0; + + function validate(): string | null { + if (!email.trim().includes("@")) return "Enter a valid email address."; + if (password.length < 6) return "Password must be at least 6 characters."; + if (password !== confirmPassword) return "Passwords don't match."; + return null; + } + + // The root layout's auth gate redirects on success, so no manual navigation here. + async function handleSignUp() { + const validationError = validate(); + if (validationError) { + setError(validationError); + return; + } + setError(null); + setSubmitting(true); + try { + await signUp(email.trim(), password); + } catch (err) { + setError(friendlyAuthError(err)); + } finally { + setSubmitting(false); + } + } -export default function SignUp() { return ( - - SignIn - {/* Note: both "/sign-up" and "/(auth)/sign-up" values are valid*/} - Go to SignUp - + + + + + + M + + + ma-place + subscriptions + + + Create account + + Start tracking your places and subscriptions in seconds. + + + + + + + Email + + + + + Password + + + + + Confirm password + + + + {error ? {error} : null} + + + {submitting ? ( + + ) : ( + Sign up + )} + + + + + + Already have an account? + + + Sign in + + + + + ); } diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index 68e553c..35068f4 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -1,10 +1,54 @@ +import { useState } from "react"; +import { ActivityIndicator, Pressable, View } from "react-native"; + import ScreenBase from "@/app/components/ScreenBase"; import Text from "@/app/components/Text"; +import { useAuth } from "@/lib/auth"; +import { cn } from "@/lib/utils"; + +export default function SettingsScreen() { + const { user, signOut } = useAuth(); + const [submitting, setSubmitting] = useState(false); + + // The root layout's auth gate redirects back to sign-in after sign-out. + async function handleSignOut() { + setSubmitting(true); + try { + await signOut(); + } catch { + // ignore — staying signed in is harmless + } finally { + setSubmitting(false); + } + } -export default function Settings() { return ( - Settings + Settings + + + + Signed in as + + + {user?.email ?? "Unknown"} + + + + + {submitting ? ( + + ) : ( + Sign out + )} + ); } diff --git a/app/_layout.tsx b/app/_layout.tsx index 2473f16..194c140 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,8 +1,15 @@ import { useEffect } from "react"; import { useFonts } from "expo-font"; -import { SplashScreen, Stack } from "expo-router"; +import { + SplashScreen, + Stack, + useRootNavigationState, + useRouter, + useSegments, +} from "expo-router"; import "@/assets/global.css"; +import { AuthProvider, useAuth } from "@/lib/auth"; // do not auto-hide the splash screen while we fetch resources, hide it when the app is ready to render SplashScreen.preventAutoHideAsync(); @@ -11,10 +18,15 @@ export default function RootLayout() { // NOTE: The `expo-router` already wraps the root-layout in , // so no need to wrap it again. Thus `useSafeAreaInsets` can be used directly even here - // NOTE: Boot into the (tabs) group, not (auth)/sign-in. Without this, expo-router - // uses the first group in file order — (auth) — so sign-in shows first. + return ( + + + + ); +} - const [loaded, error] = useFonts({ +function RootNavigator() { + const [fontsLoaded, fontError] = useFonts({ "sans-regular": require("@/assets/fonts/PlusJakartaSans-Regular.ttf"), "sans-light": require("@/assets/fonts/PlusJakartaSans-Light.ttf"), "sans-medium": require("@/assets/fonts/PlusJakartaSans-Medium.ttf"), @@ -23,16 +35,42 @@ export default function RootLayout() { "sans-extrabold": require("@/assets/fonts/PlusJakartaSans-ExtraBold.ttf"), }); + const { user, initializing } = useAuth(); + const segments = useSegments(); + const router = useRouter(); + const navigationState = useRootNavigationState(); + + const fontsReady = fontsLoaded || fontError; + + // Keep the splash screen up until both fonts and the persisted auth session are + // resolved, so we render the correct group first (no auth flash). useEffect(() => { - if (loaded || error) { + if (fontsReady && !initializing) { SplashScreen.hideAsync(); } - }, [loaded, error]); + }, [fontsReady, initializing]); - if (!loaded && !error) { + // Redirect based on auth state once the router is ready. + useEffect(() => { + if (initializing || !navigationState?.key) return; + + const inAuthGroup = segments[0] === "(auth)"; + + if (!user && !inAuthGroup) { + // Signed out and trying to view app content → send to sign-in. + router.replace("/(auth)/sign-in"); + } else if (user && inAuthGroup) { + // Signed in but still on an auth screen → send into the app. + router.replace("/(tabs)"); + } + }, [user, segments, initializing, navigationState?.key, router]); + + if (!fontsReady || initializing) { return null; } + // NOTE: Boot into the (tabs) group, not (auth)/sign-in. Without this, expo-router + // uses the first group in file order — (auth) — so sign-in shows first. return ( ); diff --git a/assets/global.css b/assets/global.css index 0d01725..bfe15b9 100644 --- a/assets/global.css +++ b/assets/global.css @@ -1,4 +1,5 @@ -@layer theme, base, components; +/* NOTE: Do not declare layers order as NativeWind will not transform CSS to native styles properly*/ +/* @layer theme, base, components, utilities; */ @import "tailwindcss/theme.css" layer(theme); @import "tailwindcss/preflight.css" layer(base); @@ -228,10 +229,6 @@ @apply py-4 text-sm font-sans-medium text-black/60; } - .auth-safe-area { - @apply flex-1 bg-background; - } - .auth-screen { @apply flex-1 bg-background; } @@ -349,7 +346,7 @@ } .auth-link { - @apply text-sm font-sans-bold text-accent; + @apply text-sm font-sans-bold text-accent underline; } .modal-overlay { diff --git a/google-services.json b/google-services.json new file mode 100644 index 0000000..ae101fd --- /dev/null +++ b/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "208563783798", + "firebase_url": "https://ma-place.firebaseio.com", + "project_id": "ma-place", + "storage_bucket": "ma-place.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:208563783798:android:d30f23cca28b2abecf7757", + "android_client_info": { + "package_name": "com.magicmedia.maplace" + } + }, + "oauth_client": [ + { + "client_id": "208563783798-lgn7r07qir9tgv2jth8h6vj5r9ht7f4r.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyBRdMCa2oceCtQVcxH_YMf-dgfXIO3LeNs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "208563783798-lgn7r07qir9tgv2jth8h6vj5r9ht7f4r.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/lib/auth.tsx b/lib/auth.tsx new file mode 100644 index 0000000..e697d2b --- /dev/null +++ b/lib/auth.tsx @@ -0,0 +1,130 @@ +import { + createContext, + useContext, + useEffect, + useRef, + useState, + type ReactNode, +} from "react"; +import * as auth from "@react-native-firebase/auth"; +import { GoogleSignin } from "@react-native-google-signin/google-signin"; + +import { GOOGLE_WEB_CLIENT_ID } from "@/lib/env"; + +// Configure Google Sign-in once. No-op until you set EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID. +if (GOOGLE_WEB_CLIENT_ID) { + GoogleSignin.configure({ webClientId: GOOGLE_WEB_CLIENT_ID }); +} + +type AuthContextValue = { + user: auth.User | null; + initializing: boolean; + signIn: (email: string, password: string) => Promise; + signUp: (email: string, password: string) => Promise; + signInWithGoogle: () => Promise; + signOut: () => Promise; +}; + +const AuthContext = createContext(undefined); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + const [initializing, setInitializing] = useState(true); + // onAuthStateChanged fires (possibly with null) while the persisted session is restored. + const initialized = useRef(false); + + useEffect(() => { + const unsubscribe = auth.onAuthStateChanged(auth.getAuth(), (nextUser) => { + setUser(nextUser); + if (!initialized.current) { + initialized.current = true; + setInitializing(false); + } + }); + return unsubscribe; + }, []); + + const value: AuthContextValue = { + user, + initializing, + async signIn(email, password) { + await auth.signInWithEmailAndPassword(auth.getAuth(), email, password); + }, + async signUp(email, password) { + await auth.createUserWithEmailAndPassword( + auth.getAuth(), + email, + password, + ); + }, + async signInWithGoogle() { + // hasPlayServices is a no-op on iOS; on Android it prompts to update if required. + await GoogleSignin.hasPlayServices({ + showPlayServicesUpdateDialog: true, + }); + + // Get the users ID token + const signInResult = await GoogleSignin.signIn(); + // Try the new style of google-sign in result, from v13+ of that module + const idToken = signInResult.data?.idToken; + + if (!idToken) { + throw Object.assign(new Error("Google sign-in was cancelled."), { + code: "auth/cancelled", + }); + } + const credential = auth.GoogleAuthProvider.credential(idToken); + await auth.signInWithCredential(auth.getAuth(), credential); + }, + async signOut() { + try { + if (GoogleSignin.getCurrentUser()) { + await GoogleSignin.revokeAccess(); + await GoogleSignin.signOut(); + } + } catch { + // ignore — the user may not have signed in via Google + } + await auth.signOut(auth.getAuth()); + }, + }; + + return {children}; +} + +export function useAuth(): AuthContextValue { + const ctx = useContext(AuthContext); + if (!ctx) { + throw new Error("useAuth must be used within an ."); + } + return ctx; +} + +/** Map common Firebase Auth error codes to short, human-friendly messages. */ +export function friendlyAuthError(error: unknown): string { + const code = (error as { code?: string })?.code ?? ""; + switch (code) { + case "auth/cancelled": + return "Sign-in was cancelled."; + case "auth/invalid-email": + return "That email address looks invalid."; + case "auth/user-disabled": + return "This account has been disabled."; + case "auth/user-not-found": + case "auth/wrong-password": + case "auth/invalid-credential": + return "Incorrect email or password."; + case "auth/email-already-in-use": + return "An account with this email already exists."; + case "auth/weak-password": + return "Choose a stronger password (at least 6 characters)."; + case "auth/network-request-failed": + return "Network error — check your connection."; + case "auth/operation-not-allowed": + return "This sign-in method isn't enabled in Firebase."; + case "auth/too-many-requests": + return "Too many attempts. Try again later."; + default: + return "Something went wrong. Please try again."; + } +} diff --git a/lib/env.ts b/lib/env.ts new file mode 100644 index 0000000..e92c4b2 --- /dev/null +++ b/lib/env.ts @@ -0,0 +1,5 @@ +// Env vars are injected by Expo at build time — only `EXPO_PUBLIC_*` keys are bundled. +const env = process.env as Record; + +/** OAuth Web Client ID for Google Sign-in (see .env → EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID). */ +export const GOOGLE_WEB_CLIENT_ID = env.EXPO_PUBLIC_GOOGLE_WEB_CLIENT_ID?.trim() ?? ""; diff --git a/package.json b/package.json index d897622..6dc98b6 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,17 @@ "scripts": { "start": "expo start", "reset-project": "node ./scripts/reset-project.js", - "android": "expo start --android", - "ios": "expo start --ios", + "android": "expo run:android", + "ios": "expo run:ios", "web": "expo start --web", - "lint": "expo lint" + "lint": "expo lint", + "android:fingerprint": "./android/gradlew signingReport" }, "dependencies": { "@expo/vector-icons": "^15.0.3", + "@react-native-firebase/app": "^26.1.0", + "@react-native-firebase/auth": "^26.1.0", + "@react-native-google-signin/google-signin": "^16.1.4", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a73b5e..eb0071d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,15 @@ importers: '@expo/vector-icons': specifier: ^15.0.3 version: 15.1.1(expo-font@14.0.12(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-native-firebase/app': + specifier: ^26.1.0 + version: 26.1.0(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-native-firebase/auth': + specifier: ^26.1.0 + version: 26.1.0(@react-native-firebase/app@26.1.0(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo@54.0.36) + '@react-native-google-signin/google-signin': + specifier: ^16.1.4 + version: 16.1.4(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-navigation/bottom-tabs': specifier: ^7.4.0 version: 7.18.14(@react-navigation/native@7.3.14(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) @@ -825,6 +834,243 @@ packages: resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} hasBin: true + '@firebase/ai@2.14.0': + resolution: {integrity: sha512-TYEQqCQUTyVHuG/HVi9vau6F9kvEaS49o/hmdn/yUuN6ZXQkwIml2nNJTIBfjNl/r9LOxwUNILgcOY16nxObug==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-types': 0.x + + '@firebase/analytics-compat@0.2.29': + resolution: {integrity: sha512-allztvCvCUlItZzD97TiRAtGoFJzR1FQFmLxbaLc6PvgscqD9cl5NdKPTtka6keShVYXvCZJpzWcRoH4TME8rw==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/analytics-types@0.8.4': + resolution: {integrity: sha512-zQ+XTgkwH6CY/eUSHJRP7e4LxM30RCxlCmob5sy2axs25GE3Ny0XdgpDscMTHHQIGqWkxPXad4w2Mw9sCgT8zQ==} + + '@firebase/analytics@0.10.23': + resolution: {integrity: sha512-34ALWXzWA6PTRUA5hipZmsm1RKzeecw5J1+qTCXsiMzwLqONC+GuTIQSdmm91MmTAEA+wG1Q5t0IFahcYQOqAA==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/app-check-compat@0.4.6': + resolution: {integrity: sha512-2pzNEZEkX84jSqy6TH6FI1HSLA1lc7kakRUybBbKjg9YhIttPlW/XX3N9CDtChji2PTTPWVPZiWhB10exHfA+A==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/app-check-interop-types@0.3.4': + resolution: {integrity: sha512-zz3i6e13B8BfWiLy8MABtTh8aGIACgKbf9UVnyHcWs+yQzJXgQcl8A46b0zfaiJHdQ+niF0ouAfcpuf+3LMPQg==} + + '@firebase/app-check-types@0.5.4': + resolution: {integrity: sha512-xV7JsIyzVr15aA7f3Pi0rB9gdBuVubs89FGA8VkRYA4g0l78poADgdfrScgf7NndSg9mm7cR7PJyY0+t22KaGw==} + + '@firebase/app-check@0.13.0': + resolution: {integrity: sha512-AbMttBKazQvGVXBZhQdVAdPzRhwHyJAY3Ghu5y2C7IZKIDIppzNYz0shTZ1mP4FBJa+28BuC4t+5h1Q6pT3Asg==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/app-compat@0.5.16': + resolution: {integrity: sha512-shQq37O8qELDzvsVwYPlDXwD1zlcrZ0m2bpBF5ov2HSbY8x+AHsnL5TtJ2e1JAfkQN05qHao1AfabS69PN6GiA==} + engines: {node: '>=20.0.0'} + + '@firebase/app-types@0.9.5': + resolution: {integrity: sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A==} + + '@firebase/app@0.16.0': + resolution: {integrity: sha512-G+ZGEyVP8YTb3ay6A+XpcYgFH3sTESHcnHU/EyTktodqhz2BHkLq+QEP7IVwjiMX0cxYwpVKip0/wC0KZcn9vQ==} + engines: {node: '>=20.0.0'} + + '@firebase/auth-compat@0.6.9': + resolution: {integrity: sha512-/hHeTBmQ61+N5J1RECls+WfskZTY78JXr7aO5EMOfUpqJvDqvoS+568k0rp6Ss/4UWwBjadILs+H+SGy1zCS3A==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/auth-interop-types@0.2.5': + resolution: {integrity: sha512-1Li/YuBDBAXcKv7BzY4U28gontUmAaw53sYiqbaVOMCFb2lFKK/c3CGMUWqtwe7+TXrl3poWnTCL5umYBg85Eg==} + + '@firebase/auth-types@0.13.1': + resolution: {integrity: sha512-0c1Mnid0uMDfGJHeUS4zfvBa4/CedJXotGy/n/NZJnBjwiJawt0ZYU+wH2VAVLiRCEfG2ncCkAX3yd1/2nrB7g==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/auth@1.13.4': + resolution: {integrity: sha512-s+NS1aV0DDyyfoIMeSz53HXnVTv7ufJjJfrP63XyaWHweJ5vOoxKWrTm5tO7S7PDqvyOa/Wi3oP0dgAo6JTMMA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@react-native-async-storage/async-storage': ^2.2.0 || ^3.0.0 + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true + + '@firebase/component@0.7.4': + resolution: {integrity: sha512-tLpOaaCol9ugUIYp2R3CbWPPA8Ajg/papX/XHEy8U52b/QXH3BbX8tTJX9aShDCjp+9sMAxMLD94i7lresdugQ==} + engines: {node: '>=20.0.0'} + + '@firebase/data-connect@0.7.2': + resolution: {integrity: sha512-Z64TRTp5KsvZtuCS1BhEg0H63TTDIi6k7idGG+z1ImAnP2qHv+xt0S5rzAONpiO7Z1geldWhpu1iY/ju+l3a3w==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/database-compat@2.1.5': + resolution: {integrity: sha512-m2KZDNXrg8DBzXWQNbbrjOhsJnM+ctsSFaDYKrqj1gEetQ8BSAwRuMUdeWLM9a6qPBgOvOA+o09j1BSEzdFqOg==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + peerDependenciesMeta: + '@firebase/app': + optional: true + '@firebase/app-compat': + optional: true + + '@firebase/database-types@1.0.21': + resolution: {integrity: sha512-SX1jUqhttKgg/m9dYRTvqU9QvucBooziWfA986r4cpsbi4zlsvewe424j3Vpduwd6DG1MSAMfBVT2VqA61FnkA==} + + '@firebase/database@1.1.4': + resolution: {integrity: sha512-D+j4+8uhGtNd1tVD+X+c8JrC4ppStGJKyujSQt2NPwdN26QcCk0BeIxue+UqspHkHiFHyQOimwlzjLewGq6S+A==} + engines: {node: '>=20.0.0'} + + '@firebase/firestore-compat@0.4.12': + resolution: {integrity: sha512-k2uX81Ao/S0jnFcWGPOQpKK1cPlJHvD9WIqh/RE1XBDP2yg5zhE4rHhSg1rtB11k39q3nKon9XLNDDrPjGclag==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/firestore-types@3.0.4': + resolution: {integrity: sha512-jGn+JSS4X9zZsrfu7Yw66v5YRdOLD1oyQh4USR0xWl4CUqV/DA6bNIXRPpxH/cUl3iVTNiP6MN7g+EL42A4qfA==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/firestore@4.17.0': + resolution: {integrity: sha512-P9tof6pyO1bnLlMWbux+5O7WFJqlb7OTPMKxxOiXKYiQl7mxykAvxr1BFCgWeEXUU7DZxQncyJ040B0IhFVZCg==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/functions-compat@0.4.6': + resolution: {integrity: sha512-dj9sOet+FIU91jeU4A3vGJoXHty7NqkSfjRLCwLgJXPDk1m72KFuxD3nlFgw/yXx/Fr7UjqzbxZ0LrIOdpx7+w==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/functions-types@0.6.4': + resolution: {integrity: sha512-zV6kgqtduR4rUAdC/ilS7kmb93XD7bEZoJDlVBZqlOw2uGGGCNBQBuleww2rr0Ulr3L9o2TDjumEt68/l1f9DQ==} + + '@firebase/functions@0.13.6': + resolution: {integrity: sha512-9obLnzeQUivK5lmtGFOU2ucQ38BjTp+jpPtbfFp/mDsdVCvEpRqdWNvMMQ6aQwR4vcVc/utsvngm5BRkXbc7ZA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/installations-compat@0.2.23': + resolution: {integrity: sha512-isaXmjb9roM83eVeXAe+ZRNKYNsSo2s0aNM+cy04AAGEyVL/d8Aa11GwEXovRFeYjl9+1yRAOxRDTOukZRwTxA==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/installations-types@0.5.4': + resolution: {integrity: sha512-U2eFapdHwjb43Vx9o+Pmj4dFfvcHEK1IirEFLqMtWrTHvmdrS3gBpBD1kmJk/9HjsOtoHZxJ2Paoe79e+L1ZPg==} + peerDependencies: + '@firebase/app-types': 0.x + + '@firebase/installations@0.6.23': + resolution: {integrity: sha512-MBkbcQfd+3qHjW+slsH4s7jH5qTdGlYpwqmxEZ7QcIpgDxu1SKyU0f+mCZhCt1BCacLNiOWF5L0R06N0LtlfMg==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/logger@0.5.1': + resolution: {integrity: sha512-vZKLsqE1ABOy8OjQiE7cUTFn4gvaqlk88yp8N94Pk/sDpq61YqZGqmVFZTvOyflTwuYFcWirBdYGoJgbDaXKYQ==} + engines: {node: '>=20.0.0'} + + '@firebase/messaging-compat@0.2.28': + resolution: {integrity: sha512-/AmMqHRnSQhPsdeED3ocs+s30/tpFvZDiiwIYY2uXFRvLujo1fnbPOeCFoe4Y+dRy1LCSjpvJf+dy5ZTsxi1yg==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/messaging-interop-types@0.2.5': + resolution: {integrity: sha512-tUEKnaAP2Y/MNIqgnriPpV6e5l13Vs/+p2yrd6NGlncPJT9O3a8muYZtdnWe+IJ4fgKLHJVC79n/asxk/N5Msw==} + + '@firebase/messaging@0.13.1': + resolution: {integrity: sha512-kL8fdjbNBI7hprlXJrUjktDWosrpT4JtfwXtVVevImPF/rBRAsC+LS/jIs+kgQVuotnvMhaBCgAFipBoY9YU9g==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/performance-compat@0.2.26': + resolution: {integrity: sha512-jgoocXLN6ao26xWQ8pzosmzQ33uLzGBJQPNK0NTbVy1XvIHr5pfgBf9hWLOxsWe+R7sJq5bjD+8ybXprmt61mA==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/performance-types@0.2.4': + resolution: {integrity: sha512-kJSEk7b0uhpcPRyL4SQ/GPujLqk52XNKcXlnsKDbWGAb9vugcLvOU3u6zfEdwd+d8hWJb5S5ZizV1JFFI0nkKg==} + + '@firebase/performance@0.7.13': + resolution: {integrity: sha512-1u6fuXP9cj0s+lkTFAspr/ttfPebPbEdpx+5Wdr4mPZbp8qH2KCMxOddEAR1ZMRa5GI0E7hDYSnolEmbqOFOAg==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/remote-config-compat@0.2.28': + resolution: {integrity: sha512-kEO9Gn6fbmVj7eNUtZ6d59mLgUDUD0qo7aCicGOWNfuRWTaUv3CF9DMYychO61zaEQ3cfA+CEny4V1E8A1gRGA==} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/remote-config-types@0.5.1': + resolution: {integrity: sha512-cX/1LT6KQwkXzck2eSzeKnuvXZCyr8qaPpDcikoJs7jmI+oBOXixpDLeDtWj1U6GNMkIoXrEDNoyT2Ypcyp5/A==} + + '@firebase/remote-config@0.9.1': + resolution: {integrity: sha512-nzQUSJnk1zAZEl2Q5O3I7Z61cYLK5JI4H6wyyOiHkVZ+bmgy1YXNNMptNbVjixMQ/eCzgA6nZRaC+1eBcJGUFA==} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/storage-compat@0.4.4': + resolution: {integrity: sha512-qSRgCB9f2R/nCp8t/8OC101cIFBFeUazlRInOMdzbnLzvrQBzEfx19SrR4pvdj/0+M+P/y8AK/a2s+3EB+B1Pw==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-compat': 0.x + + '@firebase/storage-types@0.8.4': + resolution: {integrity: sha512-BT7cwxJOx8SWwlQfrlC+bD/Sk3Cw+1odCi8UZNFNWTVZoPsBnA5W+mqtZzVnvsdJpXCFGSGQ7R7vOR6dtM/BRA==} + peerDependencies: + '@firebase/app-types': 0.x + '@firebase/util': 1.x + + '@firebase/storage@0.14.4': + resolution: {integrity: sha512-jfzEWZb3Fpsq3FwAB2ifoc8mcSh935qXdDou3TpyjDWa45hhNcZUv8/w28/10njByhfK7snbakKN30nwnzQ3/w==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@firebase/app': 0.x + + '@firebase/util@1.15.2': + resolution: {integrity: sha512-974pWIZVLDMc5GW5YAsj8y0XxULxIy/sPUy7tsxmWbF93KRIyh9xpuHlh0zDL+shUcf5nHDjFOg9YLiQ763eiA==} + engines: {node: '>=20.0.0'} + + '@firebase/webchannel-wrapper@1.0.6': + resolution: {integrity: sha512-Vr/Mqu79dMwGRAyGbJ4uN4+BtXB3/mRTdzetD1daWNeG8QaWuzhhbG77GltO5c0yYmYls8i250iX73624GJd7Q==} + + '@grpc/grpc-js@1.9.16': + resolution: {integrity: sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ==} + engines: {node: ^8.13.0 || >=10.10.0} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -914,6 +1160,33 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + '@radix-ui/primitive@1.1.7': resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} @@ -1151,6 +1424,35 @@ packages: '@types/react': optional: true + '@react-native-firebase/app@26.1.0': + resolution: {integrity: sha512-MymCFTvg/vvwJ+qsex6+5dhFGBq+czCUsXvG5a1eMw19puLrlzk6sh9+J/DnwBIRIj9m+5J9NLBi2I4S9iFrXg==} + peerDependencies: + expo: '>=47.0.0' + react: '*' + react-native: '*' + peerDependenciesMeta: + expo: + optional: true + + '@react-native-firebase/auth@26.1.0': + resolution: {integrity: sha512-o55i+A1qvifnSk8nybUzp7KgjJHoOMSCpraY4MHEGKuapAHeIJjXJxz7nnzimGHOG03dh5Cv1nGc1vd7eb7Y7w==} + peerDependencies: + '@react-native-firebase/app': 26.1.0 + expo: '>=47.0.0' + peerDependenciesMeta: + expo: + optional: true + + '@react-native-google-signin/google-signin@16.1.4': + resolution: {integrity: sha512-Lt4vjihJ+Nk9I4HjScWHzch3ebtIlP3QIea0XEHVN0PJumwUwiK/J2tU8qs7H9DIqNblC0qlXkR0Ts4ELvq7yA==} + peerDependencies: + expo: '>=52.0.40' + react: '*' + react-native: '*' + peerDependenciesMeta: + expo: + optional: true + '@react-native/assets-registry@0.81.5': resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==} engines: {node: '>= 20.19.4'} @@ -2510,6 +2812,10 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -2552,6 +2858,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + firebase@12.17.0: + resolution: {integrity: sha512-8iENFg2/k7asnybijN3ZrmTag0BuyiihUXrRZkX+yCW+YocknpzPZ4DUkdbyA/rdrdaCssBqHH8zpdDliv+4ng==} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -2725,6 +3034,9 @@ packages: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -2732,6 +3044,9 @@ packages: hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3102,6 +3417,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -3115,6 +3433,9 @@ packages: resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} engines: {node: '>=4'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -3663,6 +3984,10 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + protobufjs@7.6.5: + resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} + engines: {node: '>=12.0.0'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3686,6 +4011,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + re2js@2.8.6: + resolution: {integrity: sha512-xLgQil4kIUCrAzVk9fRSkxkFNwmygLFjVxXrLc65aE1F0+Zsb8rxumFBy4XKyvgMCTL6kilDq3EZ0piE2dP/Dg==} + engines: {node: '>=18.0.0'} + react-devtools-core@6.1.5: resolution: {integrity: sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==} @@ -4383,6 +4712,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -4390,6 +4722,14 @@ packages: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} engines: {node: '>=8'} + websocket-driver@0.7.5: + resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} @@ -5520,6 +5860,336 @@ snapshots: chalk: 4.1.2 js-yaml: 4.3.0 + '@firebase/ai@2.14.0(@firebase/app-types@0.9.5)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-check-interop-types': 0.3.4 + '@firebase/app-types': 0.9.5 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/analytics-compat@0.2.29(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/analytics': 0.10.23(@firebase/app@0.16.0) + '@firebase/analytics-types': 0.8.4 + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/analytics-types@0.8.4': {} + + '@firebase/analytics@0.10.23(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/app-check-compat@0.4.6(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-check': 0.13.0(@firebase/app@0.16.0) + '@firebase/app-check-types': 0.5.4 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/app-check-interop-types@0.3.4': {} + + '@firebase/app-check-types@0.5.4': {} + + '@firebase/app-check@0.13.0(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/app-compat@0.5.16': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/app-types@0.9.5': + dependencies: + '@firebase/logger': 0.5.1 + + '@firebase/app@0.16.0': + dependencies: + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + idb: 7.1.1 + tslib: 2.8.1 + + '@firebase/auth-compat@0.6.9(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/auth': 1.13.4(@firebase/app@0.16.0) + '@firebase/auth-types': 0.13.1(@firebase/app-types@0.9.5)(@firebase/util@1.15.2) + '@firebase/component': 0.7.4 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@firebase/app-types' + - '@react-native-async-storage/async-storage' + + '@firebase/auth-interop-types@0.2.5': {} + + '@firebase/auth-types@0.13.1(@firebase/app-types@0.9.5)(@firebase/util@1.15.2)': + dependencies: + '@firebase/app-types': 0.9.5 + '@firebase/util': 1.15.2 + + '@firebase/auth@1.13.4(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/component@0.7.4': + dependencies: + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/data-connect@0.7.2(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/auth-interop-types': 0.2.5 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/database-compat@2.1.5(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/component': 0.7.4 + '@firebase/database': 1.1.4 + '@firebase/database-types': 1.0.21 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + optionalDependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + + '@firebase/database-types@1.0.21': + dependencies: + '@firebase/app-types': 0.9.5 + '@firebase/util': 1.15.2 + + '@firebase/database@1.1.4': + dependencies: + '@firebase/app-check-interop-types': 0.3.4 + '@firebase/auth-interop-types': 0.2.5 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + faye-websocket: 0.11.4 + tslib: 2.8.1 + + '@firebase/firestore-compat@0.4.12(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/firestore': 4.17.0(@firebase/app@0.16.0) + '@firebase/firestore-types': 3.0.4(@firebase/app-types@0.9.5)(@firebase/util@1.15.2) + '@firebase/util': 1.15.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@firebase/app-types' + + '@firebase/firestore-types@3.0.4(@firebase/app-types@0.9.5)(@firebase/util@1.15.2)': + dependencies: + '@firebase/app-types': 0.9.5 + '@firebase/util': 1.15.2 + + '@firebase/firestore@4.17.0(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + '@firebase/webchannel-wrapper': 1.0.6 + '@grpc/grpc-js': 1.9.16 + '@grpc/proto-loader': 0.7.15 + re2js: 2.8.6 + tslib: 2.8.1 + + '@firebase/functions-compat@0.4.6(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/functions': 0.13.6(@firebase/app@0.16.0) + '@firebase/functions-types': 0.6.4 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/functions-types@0.6.4': {} + + '@firebase/functions@0.13.6(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-check-interop-types': 0.3.4 + '@firebase/auth-interop-types': 0.2.5 + '@firebase/component': 0.7.4 + '@firebase/messaging-interop-types': 0.2.5 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/installations-compat@0.2.23(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/installations-types': 0.5.4(@firebase/app-types@0.9.5) + '@firebase/util': 1.15.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@firebase/app-types' + + '@firebase/installations-types@0.5.4(@firebase/app-types@0.9.5)': + dependencies: + '@firebase/app-types': 0.9.5 + + '@firebase/installations@0.6.23(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/util': 1.15.2 + idb: 7.1.1 + tslib: 2.8.1 + + '@firebase/logger@0.5.1': + dependencies: + tslib: 2.8.1 + + '@firebase/messaging-compat@0.2.28(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/messaging': 0.13.1(@firebase/app@0.16.0) + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/messaging-interop-types@0.2.5': {} + + '@firebase/messaging@0.13.1(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/messaging-interop-types': 0.2.5 + '@firebase/util': 1.15.2 + idb: 7.1.1 + tslib: 2.8.1 + + '@firebase/performance-compat@0.2.26(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/performance': 0.7.13(@firebase/app@0.16.0) + '@firebase/performance-types': 0.2.4 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/performance-types@0.2.4': {} + + '@firebase/performance@0.7.13(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + web-vitals: 4.2.4 + + '@firebase/remote-config-compat@0.2.28(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/logger': 0.5.1 + '@firebase/remote-config': 0.9.1(@firebase/app@0.16.0) + '@firebase/remote-config-types': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/remote-config-types@0.5.1': {} + + '@firebase/remote-config@0.9.1(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/logger': 0.5.1 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/storage-compat@0.4.4(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/app-compat': 0.5.16 + '@firebase/component': 0.7.4 + '@firebase/storage': 0.14.4(@firebase/app@0.16.0) + '@firebase/storage-types': 0.8.4(@firebase/app-types@0.9.5)(@firebase/util@1.15.2) + '@firebase/util': 1.15.2 + tslib: 2.8.1 + transitivePeerDependencies: + - '@firebase/app-types' + + '@firebase/storage-types@0.8.4(@firebase/app-types@0.9.5)(@firebase/util@1.15.2)': + dependencies: + '@firebase/app-types': 0.9.5 + '@firebase/util': 1.15.2 + + '@firebase/storage@0.14.4(@firebase/app@0.16.0)': + dependencies: + '@firebase/app': 0.16.0 + '@firebase/component': 0.7.4 + '@firebase/util': 1.15.2 + tslib: 2.8.1 + + '@firebase/util@1.15.2': + dependencies: + tslib: 2.8.1 + + '@firebase/webchannel-wrapper@1.0.6': {} + + '@grpc/grpc-js@1.9.16': + dependencies: + '@grpc/proto-loader': 0.7.15 + '@types/node': 26.1.1 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.5 + yargs: 17.7.3 + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -5638,6 +6308,26 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.2': {} + '@radix-ui/primitive@1.1.7': {} '@radix-ui/react-collection@1.1.15(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': @@ -5838,6 +6528,30 @@ snapshots: optionalDependencies: '@types/react': 19.1.17 + '@react-native-firebase/app@26.1.0(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + dependencies: + firebase: 12.17.0 + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0) + optionalDependencies: + expo: 54.0.36(@babel/core@7.29.7)(@expo/metro-runtime@6.1.2)(expo-router@6.0.24)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3) + transitivePeerDependencies: + - '@react-native-async-storage/async-storage' + + '@react-native-firebase/auth@26.1.0(@react-native-firebase/app@26.1.0(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo@54.0.36)': + dependencies: + '@react-native-firebase/app': 26.1.0(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + plist: 3.1.1 + optionalDependencies: + expo: 54.0.36(@babel/core@7.29.7)(@expo/metro-runtime@6.1.2)(expo-router@6.0.24)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3) + + '@react-native-google-signin/google-signin@16.1.4(expo@54.0.36)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + dependencies: + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0) + optionalDependencies: + expo: 54.0.36(@babel/core@7.29.7)(@expo/metro-runtime@6.1.2)(expo-router@6.0.24)(react-native@0.81.5(@babel/core@7.29.7)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3) + '@react-native/assets-registry@0.81.5': {} '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.29.7)': @@ -7476,6 +8190,10 @@ snapshots: fast-levenshtein@2.0.6: {} + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.5 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -7530,6 +8248,39 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + firebase@12.17.0: + dependencies: + '@firebase/ai': 2.14.0(@firebase/app-types@0.9.5)(@firebase/app@0.16.0) + '@firebase/analytics': 0.10.23(@firebase/app@0.16.0) + '@firebase/analytics-compat': 0.2.29(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/app': 0.16.0 + '@firebase/app-check': 0.13.0(@firebase/app@0.16.0) + '@firebase/app-check-compat': 0.4.6(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/app-compat': 0.5.16 + '@firebase/app-types': 0.9.5 + '@firebase/auth': 1.13.4(@firebase/app@0.16.0) + '@firebase/auth-compat': 0.6.9(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0) + '@firebase/data-connect': 0.7.2(@firebase/app@0.16.0) + '@firebase/database': 1.1.4 + '@firebase/database-compat': 2.1.5(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/firestore': 4.17.0(@firebase/app@0.16.0) + '@firebase/firestore-compat': 0.4.12(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0) + '@firebase/functions': 0.13.6(@firebase/app@0.16.0) + '@firebase/functions-compat': 0.4.6(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/installations': 0.6.23(@firebase/app@0.16.0) + '@firebase/installations-compat': 0.2.23(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0) + '@firebase/messaging': 0.13.1(@firebase/app@0.16.0) + '@firebase/messaging-compat': 0.2.28(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/performance': 0.7.13(@firebase/app@0.16.0) + '@firebase/performance-compat': 0.2.26(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/remote-config': 0.9.1(@firebase/app@0.16.0) + '@firebase/remote-config-compat': 0.2.28(@firebase/app-compat@0.5.16)(@firebase/app@0.16.0) + '@firebase/storage': 0.14.4(@firebase/app@0.16.0) + '@firebase/storage-compat': 0.4.4(@firebase/app-compat@0.5.16)(@firebase/app-types@0.9.5)(@firebase/app@0.16.0) + '@firebase/util': 1.15.2 + transitivePeerDependencies: + - '@react-native-async-storage/async-storage' + flat-cache@4.0.1: dependencies: flatted: 3.4.3 @@ -7700,6 +8451,8 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 + http-parser-js@0.5.10: {} + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -7709,6 +8462,8 @@ snapshots: hyphenate-style-name@1.1.0: {} + idb@7.1.1: {} + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -8087,6 +8842,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} lodash.merge@4.6.2: {} @@ -8097,6 +8854,8 @@ snapshots: dependencies: chalk: 2.4.2 + long@5.3.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -8793,6 +9552,20 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + protobufjs@7.6.5: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 26.1.1 + long: 5.3.2 + punycode@2.3.1: {} qrcode-terminal@0.11.0: {} @@ -8817,6 +9590,8 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + re2js@2.8.6: {} + react-devtools-core@6.1.5: dependencies: shell-quote: 1.10.0 @@ -9620,10 +10395,20 @@ snapshots: dependencies: defaults: 1.0.4 + web-vitals@4.2.4: {} + webidl-conversions@3.0.1: {} webidl-conversions@5.0.0: {} + websocket-driver@0.7.5: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-fetch@3.6.20: {} whatwg-url-without-unicode@8.0.0-3: