@umituz/react-native-auth 3.6.71 → 3.6.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/infrastructure/adapters/StorageProviderAdapter.ts +1 -4
- package/src/infrastructure/providers/FirebaseAuthProvider.ts +5 -32
- package/src/infrastructure/repositories/AuthRepository.ts +0 -5
- package/src/infrastructure/services/AnonymousModeService.ts +6 -20
- package/src/infrastructure/services/AuthEventService.ts +2 -4
- package/src/infrastructure/services/initializeAuth.ts +2 -11
- package/src/infrastructure/utils/AuthErrorMapper.ts +0 -4
- package/src/infrastructure/utils/authStateHandler.ts +2 -4
- package/src/infrastructure/utils/listener/anonymousSignInHandler.ts +2 -22
- package/src/infrastructure/utils/listener/listenerLifecycle.util.ts +144 -0
- package/src/infrastructure/utils/listener/listenerState.util.ts +125 -0
- package/src/init/createAuthInitModule.ts +3 -22
- package/src/presentation/components/AccountActions.tsx +2 -4
- package/src/presentation/components/AuthBottomSheet.tsx +1 -17
- package/src/presentation/hooks/useAccountManagement.ts +0 -7
- package/src/presentation/hooks/useAuth.ts +2 -4
- package/src/presentation/hooks/useAuthBottomSheet.ts +23 -79
- package/src/presentation/hooks/useGoogleAuth.ts +0 -3
- package/src/presentation/hooks/useLoginForm.ts +26 -30
- package/src/presentation/hooks/useProfileEdit.ts +56 -64
- package/src/presentation/hooks/useRegisterForm.ts +41 -44
- package/src/presentation/providers/AuthProvider.tsx +2 -7
- package/src/presentation/stores/authModalStore.ts +0 -7
- package/src/presentation/stores/authStore.ts +1 -28
- package/src/presentation/stores/initializeAuthListener.ts +30 -160
- package/src/presentation/utils/accountDeleteHandler.util.ts +0 -51
- package/src/presentation/utils/authTransition.util.ts +72 -0
- package/src/presentation/utils/form/formFieldState.util.ts +82 -0
- package/src/presentation/utils/form/formValidation.util.ts +173 -0
- package/src/presentation/utils/socialAuthHandler.util.ts +106 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-auth",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.73",
|
|
4
4
|
"description": "Authentication service for React Native apps - Secure, type-safe, and production-ready. Provider-agnostic design with dependency injection, configurable validation, and comprehensive error handling.",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
package/src/index.ts
CHANGED
|
@@ -37,10 +37,7 @@ export class StorageProviderAdapter implements IStorageProvider {
|
|
|
37
37
|
} else {
|
|
38
38
|
throw new Error("Unsupported storage implementation");
|
|
39
39
|
}
|
|
40
|
-
} catch
|
|
41
|
-
if (__DEV__) {
|
|
42
|
-
console.warn("[StorageProviderAdapter] Failed to get value for key:", key, error);
|
|
43
|
-
}
|
|
40
|
+
} catch {
|
|
44
41
|
return null;
|
|
45
42
|
}
|
|
46
43
|
}
|
|
@@ -81,22 +81,11 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
81
81
|
|
|
82
82
|
// Convert anonymous user to permanent account
|
|
83
83
|
if (currentUser && isAnonymous) {
|
|
84
|
-
if (__DEV__) {
|
|
85
|
-
console.log("[FirebaseAuthProvider] Converting anonymous user to authenticated:", {
|
|
86
|
-
anonymousId: currentUser.uid.slice(0, 8),
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
84
|
// Reload user to refresh token before linking (prevents token-expired errors)
|
|
91
85
|
try {
|
|
92
86
|
await currentUser.reload();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
} catch (reloadError) {
|
|
97
|
-
if (__DEV__) {
|
|
98
|
-
console.log("[FirebaseAuthProvider] Reload failed, proceeding with link:", reloadError);
|
|
99
|
-
}
|
|
87
|
+
} catch {
|
|
88
|
+
// Reload failed, proceed with link anyway
|
|
100
89
|
}
|
|
101
90
|
|
|
102
91
|
const credential = EmailAuthProvider.credential(
|
|
@@ -105,19 +94,8 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
105
94
|
);
|
|
106
95
|
|
|
107
96
|
userCredential = await linkWithCredential(currentUser, credential);
|
|
108
|
-
|
|
109
|
-
if (__DEV__) {
|
|
110
|
-
console.log("[FirebaseAuthProvider] Anonymous user converted successfully:", {
|
|
111
|
-
userId: userCredential.user.uid.slice(0, 8),
|
|
112
|
-
sameUser: currentUser.uid === userCredential.user.uid,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
97
|
} else {
|
|
116
98
|
// Create new user
|
|
117
|
-
if (__DEV__) {
|
|
118
|
-
console.log("[FirebaseAuthProvider] Creating new user account");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
99
|
userCredential = await createUserWithEmailAndPassword(
|
|
122
100
|
this.auth,
|
|
123
101
|
credentials.email.trim(),
|
|
@@ -130,14 +108,9 @@ export class FirebaseAuthProvider implements IAuthProvider {
|
|
|
130
108
|
await updateProfile(userCredential.user, {
|
|
131
109
|
displayName: credentials.displayName.trim(),
|
|
132
110
|
});
|
|
133
|
-
} catch
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
137
|
-
console.warn(
|
|
138
|
-
`[FirebaseAuthProvider] Account created but display name update failed: ${errorMessage}. ` +
|
|
139
|
-
"User can update their display name later from profile settings."
|
|
140
|
-
);
|
|
111
|
+
} catch {
|
|
112
|
+
// Silently fail - the account was created successfully,
|
|
113
|
+
// only the display name update failed. User can update it later.
|
|
141
114
|
}
|
|
142
115
|
}
|
|
143
116
|
|
|
@@ -36,11 +36,6 @@ export class AuthRepository implements IAuthRepository {
|
|
|
36
36
|
const password = sanitizePassword(params.password);
|
|
37
37
|
const displayName = params.displayName ? sanitizeName(params.displayName) : undefined;
|
|
38
38
|
|
|
39
|
-
// Log if display name was sanitized
|
|
40
|
-
if (__DEV__ && params.displayName && displayName && params.displayName !== displayName) {
|
|
41
|
-
console.warn("[AuthRepository] Display name was sanitized during sign up. Original:", params.displayName, "Sanitized:", displayName);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
39
|
// Validate email
|
|
45
40
|
const emailResult = validateEmail(email);
|
|
46
41
|
if (!emailResult.isValid) {
|
|
@@ -21,10 +21,7 @@ export class AnonymousModeService {
|
|
|
21
21
|
const value = await storageProvider.get(this.storageKey);
|
|
22
22
|
this.isAnonymousMode = value === "true";
|
|
23
23
|
return this.isAnonymousMode;
|
|
24
|
-
} catch
|
|
25
|
-
if (__DEV__) {
|
|
26
|
-
console.warn("[AnonymousModeService] Storage load failed:", error);
|
|
27
|
-
}
|
|
24
|
+
} catch {
|
|
28
25
|
return false;
|
|
29
26
|
}
|
|
30
27
|
}
|
|
@@ -33,10 +30,7 @@ export class AnonymousModeService {
|
|
|
33
30
|
try {
|
|
34
31
|
await storageProvider.set(this.storageKey, this.isAnonymousMode.toString());
|
|
35
32
|
return true;
|
|
36
|
-
} catch
|
|
37
|
-
if (__DEV__) {
|
|
38
|
-
console.error("[AnonymousModeService] Storage save failed:", err);
|
|
39
|
-
}
|
|
33
|
+
} catch {
|
|
40
34
|
return false;
|
|
41
35
|
}
|
|
42
36
|
}
|
|
@@ -46,10 +40,7 @@ export class AnonymousModeService {
|
|
|
46
40
|
await storageProvider.remove(this.storageKey);
|
|
47
41
|
this.isAnonymousMode = false;
|
|
48
42
|
return true;
|
|
49
|
-
} catch
|
|
50
|
-
if (__DEV__) {
|
|
51
|
-
console.error("[AnonymousModeService] Storage clear failed:", err);
|
|
52
|
-
}
|
|
43
|
+
} catch {
|
|
53
44
|
this.isAnonymousMode = false;
|
|
54
45
|
return false;
|
|
55
46
|
}
|
|
@@ -60,18 +51,13 @@ export class AnonymousModeService {
|
|
|
60
51
|
if (provider?.getCurrentUser()) {
|
|
61
52
|
try {
|
|
62
53
|
await provider.signOut();
|
|
63
|
-
} catch
|
|
64
|
-
|
|
65
|
-
console.warn("[AnonymousModeService] Sign out failed during mode switch:", error);
|
|
66
|
-
}
|
|
54
|
+
} catch {
|
|
55
|
+
// Silently fail - sign out errors are handled elsewhere
|
|
67
56
|
}
|
|
68
57
|
}
|
|
69
58
|
|
|
70
59
|
this.isAnonymousMode = true;
|
|
71
|
-
|
|
72
|
-
if (!saved && __DEV__) {
|
|
73
|
-
console.warn("[AnonymousModeService] Anonymous mode enabled but not persisted to storage. Mode will be lost on app restart.");
|
|
74
|
-
}
|
|
60
|
+
await this.save(storageProvider);
|
|
75
61
|
emitAnonymousModeEnabled();
|
|
76
62
|
}
|
|
77
63
|
|
|
@@ -92,10 +92,8 @@ export class AuthEventService {
|
|
|
92
92
|
eventListeners.forEach((listener) => {
|
|
93
93
|
try {
|
|
94
94
|
listener(payload);
|
|
95
|
-
} catch
|
|
96
|
-
|
|
97
|
-
console.error("[AuthEventService] Listener error:", err);
|
|
98
|
-
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Silently fail - listener errors should not break the event system
|
|
99
97
|
}
|
|
100
98
|
});
|
|
101
99
|
}
|
|
@@ -65,9 +65,8 @@ async function doInitializeAuth(
|
|
|
65
65
|
let authServiceInitFailed = false;
|
|
66
66
|
try {
|
|
67
67
|
await initializeAuthService(auth, authConfig, storageProvider);
|
|
68
|
-
} catch
|
|
68
|
+
} catch {
|
|
69
69
|
authServiceInitFailed = true;
|
|
70
|
-
console.warn("[initializeAuth] Auth service init failed, continuing:", error);
|
|
71
70
|
}
|
|
72
71
|
|
|
73
72
|
const handleAuthStateChange = createAuthStateHandler(conversionState, {
|
|
@@ -78,18 +77,10 @@ async function doInitializeAuth(
|
|
|
78
77
|
initializeAuthListener({
|
|
79
78
|
autoAnonymousSignIn,
|
|
80
79
|
onAuthStateChange: (user) => {
|
|
81
|
-
handleAuthStateChange(user)
|
|
82
|
-
if (__DEV__) {
|
|
83
|
-
console.error("[initializeAuth] Auth state change handler error:", err);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
80
|
+
void handleAuthStateChange(user);
|
|
86
81
|
},
|
|
87
82
|
});
|
|
88
83
|
|
|
89
|
-
if (authServiceInitFailed) {
|
|
90
|
-
console.warn("[initializeAuth] Auth service initialization failed. Some auth features may not work.");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
84
|
isInitialized = true;
|
|
94
85
|
return { success: !authServiceInitFailed, auth };
|
|
95
86
|
}
|
|
@@ -122,10 +122,6 @@ export function mapFirebaseAuthError(error: unknown): Error {
|
|
|
122
122
|
);
|
|
123
123
|
|
|
124
124
|
default:
|
|
125
|
-
// Log unknown errors in development for debugging
|
|
126
|
-
if (__DEV__ && code) {
|
|
127
|
-
console.warn(`[AuthErrorMapper] Unknown Firebase Auth code: ${code}`, error);
|
|
128
|
-
}
|
|
129
125
|
return new AuthError(message, code || "AUTH_UNKNOWN_ERROR");
|
|
130
126
|
}
|
|
131
127
|
}
|
|
@@ -35,10 +35,8 @@ export function createAuthStateHandler(
|
|
|
35
35
|
if (conversion.isConversion && onUserConverted && state.current.previousUserId) {
|
|
36
36
|
try {
|
|
37
37
|
await onUserConverted(state.current.previousUserId, currentUserId);
|
|
38
|
-
} catch
|
|
39
|
-
|
|
40
|
-
console.warn("[AuthStateHandler] User conversion callback failed:", error);
|
|
41
|
-
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Silently fail - conversion callback errors are handled elsewhere
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
|
|
@@ -72,17 +72,8 @@ async function performAnonymousSignIn(
|
|
|
72
72
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
73
73
|
try {
|
|
74
74
|
await anonymousAuthService.signInAnonymously(auth);
|
|
75
|
-
|
|
76
|
-
if (__DEV__) {
|
|
77
|
-
console.log("[AnonymousSignInHandler] Anonymous sign-in successful");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
75
|
return;
|
|
81
76
|
} catch (error) {
|
|
82
|
-
if (__DEV__) {
|
|
83
|
-
console.warn(`[AnonymousSignInHandler] Attempt ${attempt + 1}/${maxRetries} failed:`, error);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
77
|
// If not last attempt, wait and retry
|
|
87
78
|
if (attempt < maxRetries - 1) {
|
|
88
79
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
@@ -110,9 +101,6 @@ export function createAnonymousSignInHandler(
|
|
|
110
101
|
): () => Promise<void> {
|
|
111
102
|
return async () => {
|
|
112
103
|
if (!auth) {
|
|
113
|
-
if (__DEV__) {
|
|
114
|
-
console.warn("[AnonymousSignInHandler] No auth instance");
|
|
115
|
-
}
|
|
116
104
|
store.setFirebaseUser(null);
|
|
117
105
|
store.setLoading(false);
|
|
118
106
|
store.setInitialized(true);
|
|
@@ -125,21 +113,13 @@ export function createAnonymousSignInHandler(
|
|
|
125
113
|
auth,
|
|
126
114
|
{
|
|
127
115
|
onSignInStart: () => {
|
|
128
|
-
|
|
129
|
-
console.log("[AnonymousSignInHandler] Starting anonymous sign-in");
|
|
130
|
-
}
|
|
116
|
+
// Sign-in starting
|
|
131
117
|
},
|
|
132
118
|
onSignInSuccess: () => {
|
|
133
|
-
if (__DEV__) {
|
|
134
|
-
console.log("[AnonymousSignInHandler] Anonymous sign-in successful");
|
|
135
|
-
}
|
|
136
119
|
// Listener will be triggered again with the new user
|
|
137
120
|
store.setFirebaseUser(null);
|
|
138
121
|
},
|
|
139
|
-
onSignInFailure: (
|
|
140
|
-
if (__DEV__) {
|
|
141
|
-
console.error("[AnonymousSignInHandler] All attempts failed:", error);
|
|
142
|
-
}
|
|
122
|
+
onSignInFailure: () => {
|
|
143
123
|
store.setFirebaseUser(null);
|
|
144
124
|
store.setLoading(false);
|
|
145
125
|
store.setInitialized(true);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Listener Lifecycle Utilities
|
|
3
|
+
* Handles subscription and cleanup logic for auth listener
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Auth, User } from "firebase/auth";
|
|
7
|
+
import type { AuthActions } from "../../../types/auth-store.types";
|
|
8
|
+
import { createAnonymousSignInHandler } from "./anonymousSignInHandler";
|
|
9
|
+
import {
|
|
10
|
+
isListenerInitialized,
|
|
11
|
+
completeInitialization,
|
|
12
|
+
setUnsubscribe,
|
|
13
|
+
incrementRefCount,
|
|
14
|
+
decrementRefCount,
|
|
15
|
+
startAnonymousSignIn,
|
|
16
|
+
completeAnonymousSignIn,
|
|
17
|
+
resetListenerState,
|
|
18
|
+
} from "./listenerState.util";
|
|
19
|
+
import { onIdTokenChanged } from "firebase/auth";
|
|
20
|
+
import { getAuthService } from "../../../infrastructure/services/AuthService";
|
|
21
|
+
|
|
22
|
+
type Store = AuthActions & { isAnonymous: boolean };
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create unsubscribe function that decrements ref count
|
|
26
|
+
*/
|
|
27
|
+
export function createUnsubscribeHandler(): () => void {
|
|
28
|
+
return () => {
|
|
29
|
+
const { shouldCleanup } = decrementRefCount();
|
|
30
|
+
|
|
31
|
+
if (shouldCleanup) {
|
|
32
|
+
resetListenerState();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Return existing unsubscribe if already initialized
|
|
39
|
+
* Returns null if initialization is in progress
|
|
40
|
+
*/
|
|
41
|
+
export function handleExistingInitialization(): (() => void) | null {
|
|
42
|
+
if (!isListenerInitialized()) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
incrementRefCount();
|
|
47
|
+
return createUnsubscribeHandler();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Return no-op if initialization is in progress
|
|
52
|
+
*/
|
|
53
|
+
export function handleInitializationInProgress(): () => void {
|
|
54
|
+
return () => {
|
|
55
|
+
// No-op - handled by initial initialization
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Setup Firebase auth listener
|
|
61
|
+
*/
|
|
62
|
+
export function setupAuthListener(
|
|
63
|
+
auth: Auth,
|
|
64
|
+
store: Store,
|
|
65
|
+
autoAnonymousSignIn: boolean,
|
|
66
|
+
onAuthStateChange?: (user: User | null) => void
|
|
67
|
+
): void {
|
|
68
|
+
const service = getAuthService();
|
|
69
|
+
|
|
70
|
+
if (service) {
|
|
71
|
+
const isAnonymous = service.getIsAnonymousMode();
|
|
72
|
+
if (isAnonymous) {
|
|
73
|
+
store.setIsAnonymous(true);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const unsubscribe = onIdTokenChanged(auth, (user) => {
|
|
78
|
+
handleAuthStateChange(user, store, auth, autoAnonymousSignIn, onAuthStateChange);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
setUnsubscribe(unsubscribe);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Handle auth state change from Firebase
|
|
86
|
+
*/
|
|
87
|
+
function handleAuthStateChange(
|
|
88
|
+
user: User | null,
|
|
89
|
+
store: Store,
|
|
90
|
+
auth: Auth,
|
|
91
|
+
autoAnonymousSignIn: boolean,
|
|
92
|
+
onAuthStateChange?: (user: User | null) => void
|
|
93
|
+
): void {
|
|
94
|
+
if (!user && autoAnonymousSignIn) {
|
|
95
|
+
handleAnonymousMode(store, auth);
|
|
96
|
+
store.setFirebaseUser(null);
|
|
97
|
+
completeInitialization();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
store.setFirebaseUser(user);
|
|
102
|
+
store.setInitialized(true);
|
|
103
|
+
|
|
104
|
+
// Handle conversion from anonymous
|
|
105
|
+
if (user && !user.isAnonymous && store.isAnonymous) {
|
|
106
|
+
store.setIsAnonymous(false);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onAuthStateChange?.(user);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Handle anonymous mode sign-in
|
|
114
|
+
*/
|
|
115
|
+
function handleAnonymousMode(store: Store, auth: Auth): void {
|
|
116
|
+
if (!startAnonymousSignIn()) {
|
|
117
|
+
return; // Already signing in
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const handleAnonymousSignIn = createAnonymousSignInHandler(auth, store);
|
|
121
|
+
|
|
122
|
+
// Start sign-in without blocking
|
|
123
|
+
void (async () => {
|
|
124
|
+
try {
|
|
125
|
+
await handleAnonymousSignIn();
|
|
126
|
+
} finally {
|
|
127
|
+
completeAnonymousSignIn();
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle case where Firebase auth is not available
|
|
134
|
+
*/
|
|
135
|
+
export function handleNoFirebaseAuth(store: Store): () => void {
|
|
136
|
+
completeInitialization();
|
|
137
|
+
store.setLoading(false);
|
|
138
|
+
store.setInitialized(true);
|
|
139
|
+
return () => {};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function completeListenerSetup() {
|
|
143
|
+
completeInitialization();
|
|
144
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Listener State Management
|
|
3
|
+
* Manages the state of Firebase auth listener initialization and lifecycle
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ListenerState {
|
|
7
|
+
initialized: boolean;
|
|
8
|
+
refCount: number;
|
|
9
|
+
initializationInProgress: boolean;
|
|
10
|
+
anonymousSignInInProgress: boolean;
|
|
11
|
+
unsubscribe: (() => void) | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const state: ListenerState = {
|
|
15
|
+
initialized: false,
|
|
16
|
+
refCount: 0,
|
|
17
|
+
initializationInProgress: false,
|
|
18
|
+
anonymousSignInInProgress: false,
|
|
19
|
+
unsubscribe: null,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if listener is initialized
|
|
24
|
+
*/
|
|
25
|
+
export function isListenerInitialized(): boolean {
|
|
26
|
+
return state.initialized;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if initialization is in progress
|
|
31
|
+
*/
|
|
32
|
+
export function isInitializationInProgress(): boolean {
|
|
33
|
+
return state.initializationInProgress;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if anonymous sign-in is in progress
|
|
38
|
+
*/
|
|
39
|
+
export function isAnonymousSignInInProgress(): boolean {
|
|
40
|
+
return state.anonymousSignInInProgress;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get current reference count
|
|
45
|
+
*/
|
|
46
|
+
export function getRefCount(): number {
|
|
47
|
+
return state.refCount;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Mark initialization as started
|
|
52
|
+
*/
|
|
53
|
+
export function startInitialization(): boolean {
|
|
54
|
+
if (state.initializationInProgress) {
|
|
55
|
+
return false; // Already initializing
|
|
56
|
+
}
|
|
57
|
+
state.initializationInProgress = true;
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Complete initialization
|
|
63
|
+
*/
|
|
64
|
+
export function completeInitialization(): void {
|
|
65
|
+
state.initializationInProgress = false;
|
|
66
|
+
state.initialized = true;
|
|
67
|
+
state.refCount = 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Set the unsubscribe function
|
|
72
|
+
*/
|
|
73
|
+
export function setUnsubscribe(fn: (() => void) | null): void {
|
|
74
|
+
state.unsubscribe = fn;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Increment reference count when a new subscriber joins
|
|
79
|
+
*/
|
|
80
|
+
export function incrementRefCount(): number {
|
|
81
|
+
state.refCount++;
|
|
82
|
+
return state.refCount;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Decrement reference count when a subscriber leaves
|
|
87
|
+
* Returns true if cleanup should be performed
|
|
88
|
+
*/
|
|
89
|
+
export function decrementRefCount(): { shouldCleanup: boolean; count: number } {
|
|
90
|
+
state.refCount--;
|
|
91
|
+
const shouldCleanup = state.refCount <= 0 && state.unsubscribe !== null;
|
|
92
|
+
return { shouldCleanup, count: state.refCount };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Mark anonymous sign-in as in progress
|
|
97
|
+
*/
|
|
98
|
+
export function startAnonymousSignIn(): boolean {
|
|
99
|
+
if (state.anonymousSignInInProgress) {
|
|
100
|
+
return false; // Already signing in
|
|
101
|
+
}
|
|
102
|
+
state.anonymousSignInInProgress = true;
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Mark anonymous sign-in as complete
|
|
108
|
+
*/
|
|
109
|
+
export function completeAnonymousSignIn(): void {
|
|
110
|
+
state.anonymousSignInInProgress = false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Reset all state (for testing)
|
|
115
|
+
*/
|
|
116
|
+
export function resetListenerState(): void {
|
|
117
|
+
if (state.unsubscribe) {
|
|
118
|
+
state.unsubscribe();
|
|
119
|
+
}
|
|
120
|
+
state.initialized = false;
|
|
121
|
+
state.refCount = 0;
|
|
122
|
+
state.initializationInProgress = false;
|
|
123
|
+
state.anonymousSignInInProgress = false;
|
|
124
|
+
state.unsubscribe = null;
|
|
125
|
+
}
|
|
@@ -87,24 +87,12 @@ export function createAuthInitModule(
|
|
|
87
87
|
autoAnonymousSignIn,
|
|
88
88
|
storageProvider,
|
|
89
89
|
onUserConverted: async (anonymousId: string, authenticatedId: string) => {
|
|
90
|
-
if (__DEV__) {
|
|
91
|
-
console.log('[createAuthInitModule] User converted:', {
|
|
92
|
-
anonymousId: anonymousId.slice(0, 8),
|
|
93
|
-
authenticatedId: authenticatedId.slice(0, 8),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
90
|
// Restore purchases after conversion (if callback provided)
|
|
98
91
|
if (onRestorePurchases) {
|
|
99
92
|
try {
|
|
100
93
|
await onRestorePurchases();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (__DEV__) {
|
|
106
|
-
console.error('[createAuthInitModule] Restore failed:', error);
|
|
107
|
-
}
|
|
94
|
+
} catch {
|
|
95
|
+
// Silently fail - purchase restoration errors are handled elsewhere
|
|
108
96
|
}
|
|
109
97
|
}
|
|
110
98
|
|
|
@@ -115,15 +103,8 @@ export function createAuthInitModule(
|
|
|
115
103
|
},
|
|
116
104
|
});
|
|
117
105
|
|
|
118
|
-
if (__DEV__) {
|
|
119
|
-
console.log('[createAuthInitModule] Auth initialized');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
106
|
return true;
|
|
123
|
-
} catch
|
|
124
|
-
if (__DEV__) {
|
|
125
|
-
console.error('[createAuthInitModule] Error:', error);
|
|
126
|
-
}
|
|
107
|
+
} catch {
|
|
127
108
|
return false;
|
|
128
109
|
}
|
|
129
110
|
},
|
|
@@ -60,10 +60,8 @@ export const AccountActions: React.FC<AccountActionsProps> = ({ config }) => {
|
|
|
60
60
|
onPress: async () => {
|
|
61
61
|
try {
|
|
62
62
|
await onLogout();
|
|
63
|
-
} catch
|
|
64
|
-
|
|
65
|
-
console.error("[AccountActions] Logout failed:", error);
|
|
66
|
-
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Silently fail - logout error handling is managed elsewhere
|
|
67
65
|
}
|
|
68
66
|
},
|
|
69
67
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
2
|
import { View, TouchableOpacity, ScrollView } from "react-native";
|
|
3
3
|
import {
|
|
4
4
|
useAppDesignTokens,
|
|
@@ -63,22 +63,6 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
63
63
|
handleAppleSignIn,
|
|
64
64
|
} = useAuthBottomSheet({ socialConfig, onGoogleSignIn, onAppleSignIn, onAuthSuccess });
|
|
65
65
|
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
if (__DEV__) {
|
|
68
|
-
console.log("[AuthBottomSheet] Rendered with:", {
|
|
69
|
-
mode,
|
|
70
|
-
providersCount: providers.length,
|
|
71
|
-
hasModalRef: !!modalRef.current,
|
|
72
|
-
hasTermsUrl: !!termsUrl,
|
|
73
|
-
hasPrivacyUrl: !!privacyUrl,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}, [mode, providers.length, termsUrl, privacyUrl, modalRef]);
|
|
77
|
-
|
|
78
|
-
if (__DEV__) {
|
|
79
|
-
console.log("[AuthBottomSheet] Rendering...");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
66
|
return (
|
|
83
67
|
<BottomSheetModal
|
|
84
68
|
ref={modalRef}
|
|
@@ -78,13 +78,6 @@ export const useAccountManagement = (
|
|
|
78
78
|
throw new Error("Cannot delete anonymous account");
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
if (__DEV__) {
|
|
82
|
-
console.log("[useAccountManagement] Starting delete account", {
|
|
83
|
-
userId: user.uid,
|
|
84
|
-
provider: user.provider,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
81
|
setIsDeletingAccount(true);
|
|
89
82
|
|
|
90
83
|
try {
|
|
@@ -146,10 +146,8 @@ export function useAuth(): UseAuthResult {
|
|
|
146
146
|
setLoading(true);
|
|
147
147
|
await anonymousModeMutation.mutateAsync();
|
|
148
148
|
setIsAnonymous(true);
|
|
149
|
-
} catch
|
|
150
|
-
|
|
151
|
-
console.warn("[useAuth] continueAnonymously failed:", error);
|
|
152
|
-
}
|
|
149
|
+
} catch {
|
|
150
|
+
// Silently fail - anonymous mode is optional
|
|
153
151
|
setIsAnonymous(true);
|
|
154
152
|
} finally {
|
|
155
153
|
setLoading(false);
|