Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 1x 9x 9x 2x 2x 2x 2x 9x 10x 8x 8x 8x 9x 7x 7x 7x 7x 9x 10x 10x 10x 1x 1x 9x 7x 7x 7x 6x 7x 2x 1x 1x 9x 1x | import { useCallback, useRef } from 'react';
import type { FirebaseAuthTypes } from '@react-native-firebase/auth';
import { getAuth, reload } from '@react-native-firebase/auth';
import { FIREBASE_ERROR_CODES } from '@repo/constants/error';
import { CoreAuthProvider, useCoreAuth } from '@repo/core/auth/CoreAuthProvider';
import { safeLaunchDarklyOperation } from '@/utils/launchdarklyHelpers';
import { launchdarklyService } from '@/services/launchdarklyService';
import { resetTokenRegistrationCache } from '@/services/notification/notificationApi';
import { notificationService } from '@/services/notification/notificationService';
import { queryClient } from '@/services/queryClient';
import { sentryService } from '@/services/sentryService';
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const currentUidRef = useRef<string | null>(null);
const clearUserData = useCallback(async () => {
notificationService.clearTokenCache();
resetTokenRegistrationCache();
sentryService.clearAuthenticatedUser();
await safeLaunchDarklyOperation(() => launchdarklyService.clearUser(), 'clear_user');
}, []);
// Query keys are not scoped by uid, so anything cached for the previous
// principal would be served to the next one on the same device.
const clearCachedServerState = useCallback(async (nextUid: string | null) => {
if (currentUidRef.current === nextUid) return;
currentUidRef.current = nextUid;
await queryClient.cancelQueries().catch(() => {});
queryClient.clear();
}, []);
const identifyLaunchDarklyUser = useCallback((firebaseUser: FirebaseAuthTypes.User) => {
const providerData = firebaseUser.providerData[0];
const provider = providerData?.providerId?.replace('.com', '') || 'unknown';
safeLaunchDarklyOperation(
() =>
launchdarklyService.identify({
key: firebaseUser.uid,
email: firebaseUser.email || undefined,
name: firebaseUser.displayName || undefined,
custom: {
provider: provider === 'google' ? 'google' : provider,
emailVerified: firebaseUser.emailVerified ? 'true' : 'false',
},
}),
'identify',
);
}, []);
const handleAuthStateChange = useCallback(
async (firebaseUser: FirebaseAuthTypes.User | null) => {
try {
await clearCachedServerState(firebaseUser?.uid ?? null);
if (!firebaseUser) {
await clearUserData();
return;
}
await reload(firebaseUser);
// Set Sentry user context
const providerData = firebaseUser.providerData[0];
const provider = providerData?.providerId?.replace('.com', '') || 'unknown';
sentryService.setAuthenticatedUser({
id: firebaseUser.uid,
email: firebaseUser.email,
username: firebaseUser.displayName,
provider: provider === 'google' ? 'google' : provider,
feature: 'auth_state_change',
additionalContext: {
google: {
photoUrl: firebaseUser.photoURL || null,
emailVerified: firebaseUser.emailVerified,
providerData: firebaseUser.providerData.map(p => ({
providerId: p.providerId,
uid: p.uid,
})),
},
user: {
creationTime: firebaseUser.metadata.creationTime,
lastSignInTime: firebaseUser.metadata.lastSignInTime,
isEmailVerified: firebaseUser.emailVerified,
},
},
});
// Identify user with LaunchDarkly
identifyLaunchDarklyUser(firebaseUser);
} catch (error: any) {
if (error.code === FIREBASE_ERROR_CODES.AUTH_DISABLED) {
await getAuth().signOut();
await clearUserData();
}
}
},
[clearCachedServerState, clearUserData, identifyLaunchDarklyUser],
);
return <CoreAuthProvider onAuthStateChange={handleAuthStateChange}>{children}</CoreAuthProvider>;
};
export const useAuth = () => useCoreAuth();
|