I am running a react native app v0.72 and am using an emulator for ios.
I am having some issues from cold start of my app gathering currentUser. Any reading online is indicating this is not available on cold start. I simply tried cheating this by implementing a 15 second delay on my app start potentially to listen for auth state changes, presuming it would only take a few seconds to generate a new token. I am unable to get the user object to have a .currentUser value after x period of time. Not really sure what the issue is here. I see auths hitting my firebase console so I know i’m signed in. I have a concern maybe the persistence isn’t’ working properly for firebase specifically. I actually store things like an email after sign in in async storage.
Here is my code that i’m using and the flow its following: My user launches the app, they navigate to my “SplashScreen.js” if they don’t have an ‘auth’ session already after x amount of time. I am signing in via apple, i get my apple oauth token back, and a firebase token as well indicating i’m logged in, it navigates me to my “HomeScreen.js”. I force close the apple app and relaunch. Then I wait again 15 seconds and i’m seeing NULL in the currentUser and i’m back at the “SplashScreen.js” because i did not authenticate in time.
Here all the major files involved (altered some files that may have sensitive like information in it so its not my true sensitive data in those spots):
App.tsx (see this referred to as app.js as well or index.js, also wondering if its worth updating this name of this file because this is what react native provides my default on init)???
import { auth } from './configs/Firestore';
import 'react-native-gesture-handler';
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { View, ActivityIndicator } from 'react-native';
// Import auth object from Firestore configuration
import { getAuth, onAuthStateChanged, onIdTokenChanged } from 'firebase/auth';
// Screens
import SplashScreen from './screens/SplashScreen';
import SignupScreen from './screens/CreateAccountScreen';
import LoginScreen from './screens/LoginScreen';
import InitialAccountConfigurationScreen from './screens/InitialAccountConfigurationScreen';
import ForgotPasswordScreen from './screens/ForgotPasswordScreen';
import TermsAndConditionsScreen from './screens/TermsAndConditionsScreen';
import PrivacyScreen from './screens/PrivacyPolicyScreen';
import HomeScreen from './screens/HomeScreen';
import ExerciseHomeScreen from './screens/ExerciseHomeScreen';
import CreateCustomWorkoutScreen from './screens/CreateCustomWorkoutScreen';
import FinalizeCreateCustomWorkoutScreen from './screens/FinalizeCreateCustomWorkoutScreen';
import EditCustomWorkoutNamesScreen from './screens/EditCustomWorkoutNamesScreen';
import EditCustomWorkoutDetailsScreen from './screens/EditCustomWorkoutDetailsScreen';
import AddOneExerciseScreen from './screens/AddOneExerciseScreen';
import StartExerciseNamesScreen from './screens/StartExerciseNamesScreen';
import StartExerciseWorkoutScreen from './screens/StartExerciseWorkoutScreen';
import PreviousCompletedWorkoutNamesScreen from './screens/PreviousCompletedWorkoutNamesScreen';
import PreviousCompletedWorkoutDetailsScreen from './screens/PreviousCompletedWorkoutDetailsScreen';
import HelpHomeScreen from './screens/HelpHomeScreen';
import SendFeedbackScreen from './screens/SendFeedbackScreen';
import SettingsScreen from './screens/SettingsScreen';
import EditProfileScreen from './screens/EditProfileScreen';
import ChangePasswordScreen from './screens/ChangePasswordScreen';
import DeleteAccountScreen from './screens/DeleteAccountScreen';
const Stack = createStackNavigator();
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [initialLoadingComplete, setInitialLoadingComplete] = useState(false);
// Function to create a delay
const delay = ms => new Promise(res => setTimeout(res, ms));
useEffect(() => {
console.log("App started, setting up auth listener");
const unsubscribe = onIdTokenChanged(auth, async (user) => {
console.log("Auth State Changed");
console.log("User from Listener:", user);
console.log("CurrentUser from Auth:", auth.currentUser);
if (user) {
setIsAuthenticated(true);
const idToken = await user.getIdToken();
console.log("Firebase ID Token:", idToken);
} else {
setIsAuthenticated(false);
console.log("No user found, retrying in 5 seconds");
setTimeout(() => {
console.log("Retry CurrentUser from Auth:", auth.currentUser);
}, 5000);
}
setIsLoading(false);
});
// Start the forced 15 seconds delay
delay(10000).then(() => setInitialLoadingComplete(true));
// Cleanup function
return () => unsubscribe();
}, []);
if (isLoading || !initialLoadingComplete) {
return (
<View style={{ flex: 1, backgroundColor: '#123456', justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#FFFFFF" />
</View>
);
}
return (
<View style={{ flex: 1, backgroundColor: '#323232' }}>
<NavigationContainer>
<Stack.Navigator
initialRouteName={isAuthenticated ? 'Home' : 'Splash'}
screenOptions={{ cardStyle: { backgroundColor: '#323232' } }}
>
<Stack.Screen name="Splash" component={SplashScreen} />
<Stack.Screen name="Signup" component={SignupScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="InitialAccountConfiguration" component={InitialAccountConfigurationScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
<Stack.Screen name="TermsAndConditions" component={TermsAndConditionsScreen} />
<Stack.Screen name="Privacy" component={PrivacyScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="ExerciseHome" component={ExerciseHomeScreen} />
<Stack.Screen name="CreateCustomWorkout" component={CreateCustomWorkoutScreen} />
<Stack.Screen name="FinalizeCreateCustomWorkout" component={FinalizeCreateCustomWorkoutScreen} />
<Stack.Screen name="EditCustomWorkoutNames" component={EditCustomWorkoutNamesScreen} />
<Stack.Screen name="EditCustomWorkoutDetails" component={EditCustomWorkoutDetailsScreen} />
<Stack.Screen name="AddOneExercise" component={AddOneExerciseScreen} />
<Stack.Screen name="StartExerciseNames" component={StartExerciseNamesScreen} />
<Stack.Screen name="StartExerciseWorkout" component={StartExerciseWorkoutScreen} />
<Stack.Screen name="PreviousCompletedWorkouts" component={PreviousCompletedWorkoutNamesScreen} />
<Stack.Screen name="PreviousCompletedWorkoutDetails" component={PreviousCompletedWorkoutDetailsScreen} />
<Stack.Screen name="HelpHome" component={HelpHomeScreen} />
<Stack.Screen name="SendFeedback" component={SendFeedbackScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
<Stack.Screen name="EditProfile" component={EditProfileScreen} />
<Stack.Screen name="ChangePassword" component={ChangePasswordScreen} />
<Stack.Screen name="DeleteAccount" component={DeleteAccountScreen} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
};
export default App;
Firestore.js
import { initializeApp } from 'firebase/app';
import { getApp } from 'firebase/app';
import { getAnalytics } from "firebase/analytics";
import { getFirestore } from 'firebase/firestore';
import { getAuth, setPersistence, browserSessionPersistence } from 'firebase/auth';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { getReactNativePersistence } from 'firebase/auth/react-native';
import ReactNativeAsyncStorage from '@react-native-async-storage/async-storage';
const storage = getReactNativePersistence(ReactNativeAsyncStorage)
//const app = getApp();
const firebaseConfig = {
apiKey: "AIzaSyCu2EX4S7_123_sOo8",
authDomain: "xaza-health.firebaseapp.com",
projectId: "xaza-health",
storageBucket: "xaza-health.appspot.com",
messagingSenderId: "116553309985",
appId: "1:116553308885:web:cce248380cc9e89a3e05c",
measurementId: "G-HV50L9NTPE",
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
console.log(auth);
// Set persistence
setPersistence(auth, getReactNativePersistence(AsyncStorage))
.then(() => {
console.log('Persistence set to React Native AsyncStorage');
})
.catch((error) => {
console.error('Error setting persistence:', error);
});
export { auth };
SplashScreen.js
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, Platform, Animated, SafeAreaView, Image, TouchableOpacity, StyleSheet, Button } from 'react-native';
import { Path, Svg } from 'react-native-svg';
import DeviceInfo from 'react-native-device-info';
import AsyncStorage from '@react-native-async-storage/async-storage';
import EncryptedStorage from 'react-native-encrypted-storage';
// Security
import CheckVersion from '../security/CheckVersion';
import CheckSessionLoggedInAlready from '../security/CheckSessionLoggedInAlready';
import CheckSessionNotLoggedIn from '../security/CheckSessionNotLoggedIn'; // Adjust the path as necessary
// Custom Headers
import BlankHeader from '../headers/HeaderBlank';
// Custom Styles
import stylesSplashScreen from '../styles/StylesSplashScreen';
// Custom Buttons
import CustomButtonEnabled from '../buttons/CustomButtonEnabled';
import AppleSignInButton from '../buttons/AppleSignIn';
import GoogleSignInButton from '../buttons/GoogleSignIn';
// Custom Modals
import GeneralMessageModal from '../modals/GeneralMessageModal';
import GeneralMessageNoButtonsModal from '../modals/GeneralMessageNoButtonsModal';
// Firebase / OAuth
import { auth } from '../configs/Firestore';
import { getAuth, GoogleAuthProvider, signInWithCredential, OAuthProvider } from 'firebase/auth';
import { GoogleSignin } from '@react-native-google-signin/google-signin';
import { appleAuth } from '@invertase/react-native-apple-authentication';
const AnimatedPath = Animated.createAnimatedComponent(Path);
const SplashScreen = ({ navigation }) => {
const { shouldUpdateApp, errorMessage, setErrorMessage } = CheckVersion();
//const { sessionUserProvider, sessionUserEmail, sessionUserUsername, sessionUserFirstName, sessionUserFirebaseUID, sessionDataIsLoaded } = CheckSessionLoggedInAlready(navigation);
const [generalMessageModalVisible, setGeneralMessageModalVisible] = useState(false);
const [generalMessageModalData, setGeneralMessageModalData] = useState({ title: '', message: '' });
const [loggedIn, setloggedIn] = useState(false);
const [userInfo, setuserInfo] = useState([]);
const [isSigninInProgress, setIsSigninInProgress] = useState(false);
const handleAppleSignIn = async () => {
console.log("Signing in with Apple");
try {
// Start Apple Sign-In Request
const appleAuthResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
});
// Check credential state
const credentialState = await appleAuth.getCredentialStateForUser(appleAuthResponse.user);
if (credentialState === appleAuth.State.AUTHORIZED && appleAuthResponse.identityToken) {
// User is authenticated and token received
console.log('Apple SignIn Success:', appleAuthResponse);
// Create Firebase credential
const provider = new OAuthProvider('apple.com');
const credential = provider.credential({
idToken: appleAuthResponse.identityToken,
rawNonce: appleAuthResponse.nonce
});
// Sign in to Firebase with the Apple credential
const firebaseUserCredential = await signInWithCredential(auth, credential);
if (firebaseUserCredential) {
console.log('Firebase user', firebaseUserCredential.user);
console.log("Firebase User after Sign-in:", auth.currentUser);
const firebaseUser = firebaseUserCredential.user;
// Log the providerData array to inspect if the email is there
console.log('Provider Data:', firebaseUser.providerData);
// Extract the provider ID
const providerId = firebaseUser.providerData.find((data) => data.providerId === 'apple.com')?.providerId || 'apple.com';
// If firebaseUser.email is undefined, try to get the email from providerData
const emailFromProviderData = firebaseUser.providerData
.filter((pd) => pd.providerId === 'apple.com' && pd.email)
.map((pd) => pd.email)[0];
// Use email from providerData if available
const emailToUse = firebaseUser.email || emailFromProviderData;
// Call postSignIn with the defined email, uid and providerId.
await postSignIn({
email: emailToUse, // Use the potentially fallback email
firebaseuid: firebaseUser.uid,
provider: providerId,
});
}
} else {
console.log("User not authorized or Identity token missing");
}
} catch (error) {
console.log('Error with Apple Sign-In or Firebase:', error);
}
};
useEffect(() => {
// Configure Google Sign-In
GoogleSignin.configure({
scopes: ['https://www.googleapis.com/auth/userinfo.email'],
iosClientId: '1:116553308885:ios:3c93a4a78b49b868a3e05c',
webClientId: '116553308885-mvqbb5ku69pgggjl69ul2i75uhffq4b8.apps.googleusercontent.com',
});
}, []);
const handleGoogleSignIn = async () => {
try {
await GoogleSignin.signOut(); // Add this line to sign the user out from Google first
await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
const { idToken } = await GoogleSignin.signIn();
console.log('Google ID Token obtained:', idToken);
const firebaseAuth = getAuth();
const googleCredential = GoogleAuthProvider.credential(idToken);
const firebaseUserCredential = await signInWithCredential(firebaseAuth, googleCredential);
const firebaseUser = firebaseUserCredential.user;
console.log('Firebase user signed in with Google:', firebaseUser);
// Log the providerData array to inspect if the email is there
console.log('Provider Data:', firebaseUser.providerData);
// Extract the provider ID
const providerId = firebaseUser.providerData.find((data) => data.providerId === 'google.com')?.providerId || 'google.com';
// If firebaseUser.email is undefined, try to get the email from providerData
const emailFromProviderData = firebaseUser.providerData
.filter((pd) => pd.providerId === 'google.com' && pd.email)
.map((pd) => pd.email)[0];
// Use email from providerData if available
const emailToUse = firebaseUser.email || emailFromProviderData;
// Call postSignIn with the defined email, uid and providerId.
await postSignIn({
email: emailToUse, // Use the potentially fallback email
firebaseuid: firebaseUser.uid,
provider: providerId,
});
} catch (error) {
console.error('Google Sign-In error:', error);
return null;
}
};
async function postSignIn({ email, firebaseuid, provider }) {
// Your logic to handle the sign in after firebase authentication
console.log('Post sign-in data:', { email, firebaseuid, provider });
const payload = {
email: email,
firebaseuid: firebaseuid,
provider: provider,
};
console.log(payload);
try {
// Start the fetch call
const response = await fetch('https://api.xazahealth.com/loginAlt', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
// Parse the JSON response
const data = await response.json();
console.log(data);
if (data.success) {
if (data.message === 'Login Successful! Navigating to initial account configuration.') {
console.log("Initial account configuration needed")
// Navigate to the InitialAccountConfigurationScreen
navigation.navigate('InitialAccountConfiguration', {
email: email,
firebaseuid: firebaseuid,
provider: provider,
});
} else {
try {
// Save data to AsyncStorage and navigate
await AsyncStorage.setItem('sessionUserProvider', data.providerid);
await AsyncStorage.setItem('sessionUserEmail', data.email);
await AsyncStorage.setItem('sessionUserUsername', data.username);
await AsyncStorage.setItem('sessionUserFirstName', data.firstname || ''); // use empty string if null
await AsyncStorage.setItem('sessionUserLastName', data.lastname || ''); // use empty string if null
await EncryptedStorage.setItem('sessionUserFirebaseUID', data.firebaseuid);
// Navigate to the Home screen
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
});
} catch (error) {
// Handle errors saving data
console.log('Error saving user data:', error);
setGeneralMessageModalData({
success: false,
visible: true,
title: 'Error',
message: data.message || 'Login failed.'
});
}
}
} else {
// Handle login failed
setGeneralMessageModalData({
success: false,
visible: true,
title: 'Error',
message: data.message || 'Login failed.'
});
setGeneralMessageModalVisible(true);
}
} catch (error) {
// Handle API call error
console.log('API call error:', error);
setGeneralMessageModalData({
success: false,
visible: true,
title: 'Service Unavailable',
message: 'We apologize for the inconvenience! XAZA Health is offline at this time. Please try again later.'
});
setGeneralMessageModalVisible(true);
}
};
const handleLogin = () => {
navigation.navigate('Login');
};
const handleSignup = () => {
navigation.navigate('Signup');
};
const animation = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(animation, {
toValue: 1,
duration: 5000, // Increasing the duration for a smoother animation
useNativeDriver: true,
}),
Animated.delay(300)
])
).start();
}, []);
const d = animation.interpolate({
inputRange: [
0, 0.0625, 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375,
0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 1
],
outputRange: [
'M0,50 L40,50 L60,50 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // flat line
'M0,50 L40,50 L60,45 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // slight rise
'M0,50 L40,50 L60,40 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // P wave
'M0,50 L40,50 L60,50 L80,53 L100,35 L120,55 L140,50 L160,50 L200,50', // Q wave
'M0,50 L40,50 L60,50 L80,60 L100,25 L120,60 L140,50 L160,50 L200,50', // R wave
'M0,50 L40,50 L60,50 L80,53 L100,45 L120,53 L140,50 L160,50 L200,50', // S wave
'M0,50 L40,50 L60,50 L80,55 L100,50 L120,55 L140,55 L160,50 L200,50', // T wave
'M0,50 L40,50 L60,50 L80,50 L100,50 L120,50 L140,50 L160,52 L200,50', // flat line
'M0,50 L40,50 L60,55 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // slight dip
'M0,50 L40,50 L60,60 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // inverse P wave
'M0,50 L40,50 L60,50 L80,47 L100,65 L120,45 L140,50 L160,50 L200,50', // inverse Q wave
'M0,50 L40,50 L60,50 L80,40 L100,75 L120,40 L140,50 L160,50 L200,50', // inverse R wave
'M0,50 L40,50 L60,50 L80,47 L100,55 L120,47 L140,50 L160,50 L200,50', // inverse S wave
'M0,50 L40,50 L60,50 L80,45 L100,50 L120,45 L140,45 L160,50 L200,50', // inverse T wave
'M0,50 L40,50 L60,50 L80,50 L100,50 L120,50 L140,50 L160,50 L200,50', // back to flat line
],
});
/*
useEffect(() => {
const unsubscribe = auth().onAuthStateChanged((user) => {
if (user) {
console.log('User is signed in:', user);
} else {
console.log('User is signed out');
}
});
return () => unsubscribe(); // Unsubscribe on component unmount
}, []);
*/
BlankHeader(navigation);
return (
<View style={stylesSplashScreen.container}>
<View style={stylesSplashScreen.centeredContent}>
<Text style={stylesSplashScreen.text}>
XAZA Health
</Text>
</View>
<Svg style={{ marginTop: -70 }} width="100%" height="100" viewBox="0 0 200 100">
<AnimatedPath
d={d}
fill="none"
stroke="#F9C400"
strokeWidth="2"
/>
</Svg>
<View style={{ flex: 2, justifyContent: 'center' }}>
{Platform.OS === 'android' && (
<>
<GoogleSignInButton onGoogleButtonPress={handleGoogleSignIn} />
<CustomButtonEnabled
title="Create Account"
onPress={handleSignup}
/>
<CustomButtonEnabled
title="Login"
onPress={handleLogin}
/>
</>
)}
{Platform.OS === 'ios' && (
<>
<AppleSignInButton onAppleButtonPress={handleAppleSignIn} />
<CustomButtonEnabled
title="Create Account"
onPress={handleSignup}
/>
<CustomButtonEnabled
title="Login"
onPress={handleLogin}
/>
</>
)}
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>
Signing in to this app implies continued acceptance of our{" "}
<Text
style={styles.hyperlink}
onPress={() => navigation.navigate('TermsAndConditions')}
>
Terms and Conditions
</Text>
{" "}and{" "}
<Text
style={styles.hyperlink}
onPress={() => navigation.navigate('PrivacyPolicy')}
>
Privacy Policy
</Text>.
</Text>
</View>
<GeneralMessageModal
generalMessageModalVisible={generalMessageModalVisible}
generalMessageModalData={generalMessageModalData}
handlePressingOK={() => setGeneralMessageModalVisible(false)}
/>
<GeneralMessageNoButtonsModal
modalData={{
visible: shouldUpdateApp,
title: 'Update Required',
message: 'You need to update the app to the latest version to continue.',
}}
/>
<GeneralMessageNoButtonsModal
modalData={{
visible: !!errorMessage,
title: 'Error',
message: errorMessage,
}}
/>
</View>
);
};
const styles = StyleSheet.create({
// ...other styles,
signInImage: {
width: 200,
height: 200,
resizeMode: 'contain', // This ensures the aspect ratio is maintained
},
footer: {
padding: 1,
alignItems: 'center',
justifyContent: 'center',
},
footerText: {
fontSize: 10,
color: '#F5F5F5', // Adjust the color as needed
textAlign: 'center',
},
hyperlink: {
color: '#0CCAFF', // Hyperlink color
textDecorationLine: 'underline',
},
});
export default SplashScreen;
I have tried quite a bit of things (different devices, file modifcations, etc) Been working and sending this into GPT4 full files over the last few days and this thing is pretty much telling me to just ask the community, it is ‘puzzled’ as am I.
EDIT 1: Can’t include HomeScreen.js i’ve exceeded stacks limits. Also can’t include logs i’m seeing :/




