@umituz/react-native-auth 3.2.11 → 3.2.12
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/__tests__/services/AnonymousModeService.test.ts +197 -0
- package/src/__tests__/services/AuthPackage.test.ts +18 -18
- package/src/application/ports/IAuthService.ts +4 -4
- package/src/domain/utils/{guestNameGenerator.ts → anonymousNameGenerator.ts} +11 -11
- package/src/index.ts +3 -4
- package/src/infrastructure/services/{GuestModeService.ts → AnonymousModeService.ts} +19 -19
- package/src/infrastructure/services/AuthEventService.ts +6 -6
- package/src/infrastructure/services/AuthPackage.ts +4 -4
- package/src/infrastructure/services/AuthService.ts +19 -30
- package/src/infrastructure/storage/{GuestModeStorage.ts → AnonymousModeStorage.ts} +9 -9
- package/src/infrastructure/utils/AuthEventEmitter.ts +2 -2
- package/src/presentation/components/AuthBottomSheet.tsx +3 -3
- package/src/presentation/hooks/mutations/useAuthMutations.ts +2 -2
- package/src/presentation/hooks/useAuth.ts +17 -26
- package/src/presentation/hooks/useAuthRequired.ts +1 -1
- package/src/presentation/hooks/useLoginForm.ts +7 -7
- package/src/presentation/hooks/useUserProfile.ts +8 -8
- package/src/presentation/stores/auth.selectors.ts +6 -6
- package/src/presentation/stores/authStore.ts +12 -18
- package/src/presentation/stores/initializeAuthListener.ts +5 -5
- package/src/types/auth-store.types.ts +5 -5
- package/src/__tests__/services/GuestModeService.test.ts +0 -194
|
@@ -11,14 +11,14 @@ import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
|
11
11
|
import type { AuthConfig } from "../../domain/value-objects/AuthConfig";
|
|
12
12
|
import { DEFAULT_AUTH_CONFIG } from "../../domain/value-objects/AuthConfig";
|
|
13
13
|
import { AuthRepository } from "../repositories/AuthRepository";
|
|
14
|
-
import {
|
|
14
|
+
import { AnonymousModeService } from "./AnonymousModeService";
|
|
15
15
|
import { authEventService } from "./AuthEventService";
|
|
16
16
|
import { authTracker } from "../utils/auth-tracker.util";
|
|
17
17
|
import type { IStorageProvider } from "./AuthPackage";
|
|
18
18
|
|
|
19
19
|
export class AuthService implements IAuthService {
|
|
20
20
|
private repository!: AuthRepository;
|
|
21
|
-
private
|
|
21
|
+
private anonymousModeService: AnonymousModeService;
|
|
22
22
|
private storageProvider?: IStorageProvider;
|
|
23
23
|
private initialized: boolean = false;
|
|
24
24
|
private config: AuthConfig;
|
|
@@ -29,19 +29,9 @@ export class AuthService implements IAuthService {
|
|
|
29
29
|
...config,
|
|
30
30
|
password: { ...DEFAULT_AUTH_CONFIG.password, ...config.password },
|
|
31
31
|
};
|
|
32
|
-
// Initialize with a dummy provider effectively, or null?
|
|
33
|
-
// AuthRepository needs a provider. We can't init it without one.
|
|
34
|
-
// We'll initialize it properly in initialize()
|
|
35
|
-
// For now we can cast null or strict init check.
|
|
36
|
-
// Better: Allow repository to be nullable or initialize with a dummy/proxy.
|
|
37
|
-
// To satisfy strict TS, let's delay repository creation or create a NotInitializedProvider.
|
|
38
|
-
// But since initialize() sets it up, we can use a ! or optional.
|
|
39
|
-
|
|
40
|
-
this.guestModeService = new GuestModeService();
|
|
41
|
-
this.storageProvider = storageProvider;
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
this.anonymousModeService = new AnonymousModeService();
|
|
34
|
+
this.storageProvider = storageProvider;
|
|
45
35
|
}
|
|
46
36
|
|
|
47
37
|
private get repositoryInstance(): AuthRepository {
|
|
@@ -54,7 +44,6 @@ export class AuthService implements IAuthService {
|
|
|
54
44
|
|
|
55
45
|
let provider: IAuthProvider;
|
|
56
46
|
|
|
57
|
-
// Check if it's a Firebase Auth instance (has currentUser property)
|
|
58
47
|
if ("currentUser" in providerOrAuth) {
|
|
59
48
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
60
49
|
const firebaseProvider = new FirebaseAuthProvider(providerOrAuth as any);
|
|
@@ -68,7 +57,7 @@ export class AuthService implements IAuthService {
|
|
|
68
57
|
this.repository = new AuthRepository(provider, this.config);
|
|
69
58
|
|
|
70
59
|
if (this.storageProvider) {
|
|
71
|
-
await this.
|
|
60
|
+
await this.anonymousModeService.load(this.storageProvider);
|
|
72
61
|
}
|
|
73
62
|
this.initialized = true;
|
|
74
63
|
}
|
|
@@ -81,7 +70,7 @@ export class AuthService implements IAuthService {
|
|
|
81
70
|
authTracker.logOperationStarted("Sign up", { email: params.email });
|
|
82
71
|
try {
|
|
83
72
|
const user = await this.repositoryInstance.signUp(params);
|
|
84
|
-
await this.
|
|
73
|
+
await this.clearAnonymousModeIfNeeded();
|
|
85
74
|
authTracker.logOperationSuccess("Sign up", { userId: user.uid });
|
|
86
75
|
authEventService.emitUserAuthenticated(user.uid);
|
|
87
76
|
return user;
|
|
@@ -95,7 +84,7 @@ export class AuthService implements IAuthService {
|
|
|
95
84
|
authTracker.logOperationStarted("Sign in", { email: params.email });
|
|
96
85
|
try {
|
|
97
86
|
const user = await this.repositoryInstance.signIn(params);
|
|
98
|
-
await this.
|
|
87
|
+
await this.clearAnonymousModeIfNeeded();
|
|
99
88
|
authTracker.logOperationSuccess("Sign in", { userId: user.uid });
|
|
100
89
|
authEventService.emitUserAuthenticated(user.uid);
|
|
101
90
|
return user;
|
|
@@ -109,7 +98,7 @@ export class AuthService implements IAuthService {
|
|
|
109
98
|
authTracker.logOperationStarted("Sign out");
|
|
110
99
|
try {
|
|
111
100
|
await this.repositoryInstance.signOut();
|
|
112
|
-
await this.
|
|
101
|
+
await this.clearAnonymousModeIfNeeded();
|
|
113
102
|
authTracker.logOperationSuccess("Sign out");
|
|
114
103
|
} catch (error) {
|
|
115
104
|
authTracker.logOperationError("sign-out", error);
|
|
@@ -117,35 +106,35 @@ export class AuthService implements IAuthService {
|
|
|
117
106
|
}
|
|
118
107
|
}
|
|
119
108
|
|
|
120
|
-
private async
|
|
121
|
-
if (this.
|
|
122
|
-
await this.
|
|
109
|
+
private async clearAnonymousModeIfNeeded(): Promise<void> {
|
|
110
|
+
if (this.anonymousModeService.getIsAnonymousMode() && this.storageProvider) {
|
|
111
|
+
await this.anonymousModeService.clear(this.storageProvider);
|
|
123
112
|
}
|
|
124
113
|
}
|
|
125
114
|
|
|
126
|
-
async
|
|
115
|
+
async setAnonymousMode(): Promise<void> {
|
|
127
116
|
if (!this.storageProvider) {
|
|
128
|
-
throw new Error("Storage provider is required for
|
|
117
|
+
throw new Error("Storage provider is required for anonymous mode");
|
|
129
118
|
}
|
|
130
|
-
await this.
|
|
119
|
+
await this.anonymousModeService.enable(this.storageProvider);
|
|
131
120
|
}
|
|
132
121
|
|
|
133
122
|
getCurrentUser(): AuthUser | null {
|
|
134
123
|
if (!this.initialized) return null;
|
|
135
|
-
return this.
|
|
124
|
+
return this.anonymousModeService.getIsAnonymousMode() ? null : this.repositoryInstance.getCurrentUser();
|
|
136
125
|
}
|
|
137
126
|
|
|
138
|
-
|
|
139
|
-
return this.
|
|
127
|
+
getIsAnonymousMode(): boolean {
|
|
128
|
+
return this.anonymousModeService.getIsAnonymousMode();
|
|
140
129
|
}
|
|
141
130
|
|
|
142
131
|
onAuthStateChange(callback: (user: AuthUser | null) => void): () => void {
|
|
143
|
-
const wrappedCallback = this.
|
|
132
|
+
const wrappedCallback = this.anonymousModeService.wrapAuthStateCallback(callback);
|
|
144
133
|
return this.repositoryInstance.onAuthStateChange(wrappedCallback);
|
|
145
134
|
}
|
|
146
135
|
|
|
147
136
|
getConfig(): AuthConfig { return this.config; }
|
|
148
|
-
|
|
137
|
+
getAnonymousModeService(): AnonymousModeService { return this.anonymousModeService; }
|
|
149
138
|
}
|
|
150
139
|
|
|
151
140
|
let authServiceInstance: AuthService | null = null;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Single Responsibility: Manage
|
|
2
|
+
* Anonymous Mode Storage
|
|
3
|
+
* Single Responsibility: Manage anonymous mode persistence
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { getAuthPackage } from "../services/AuthPackage";
|
|
7
7
|
|
|
8
|
-
export async function
|
|
8
|
+
export async function loadAnonymousMode(storageKey?: string): Promise<boolean> {
|
|
9
9
|
try {
|
|
10
10
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
11
|
-
const key = storageKey ?? packageConfig?.storageKeys.
|
|
11
|
+
const key = storageKey ?? packageConfig?.storageKeys.anonymousMode ?? "@auth_anonymous_mode";
|
|
12
12
|
|
|
13
13
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
14
14
|
if (!storageProvider) return false;
|
|
@@ -20,15 +20,15 @@ export async function loadGuestMode(storageKey?: string): Promise<boolean> {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export async function
|
|
23
|
+
export async function saveAnonymousMode(isAnonymous: boolean, storageKey?: string): Promise<void> {
|
|
24
24
|
try {
|
|
25
25
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
26
|
-
const key = storageKey ?? packageConfig?.storageKeys.
|
|
26
|
+
const key = storageKey ?? packageConfig?.storageKeys.anonymousMode ?? "@auth_anonymous_mode";
|
|
27
27
|
|
|
28
28
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
29
29
|
if (!storageProvider) return;
|
|
30
30
|
|
|
31
|
-
if (
|
|
31
|
+
if (isAnonymous) {
|
|
32
32
|
await storageProvider.set(key, "true");
|
|
33
33
|
} else {
|
|
34
34
|
await storageProvider.remove(key);
|
|
@@ -38,10 +38,10 @@ export async function saveGuestMode(isGuest: boolean, storageKey?: string): Prom
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export async function
|
|
41
|
+
export async function clearAnonymousMode(storageKey?: string): Promise<void> {
|
|
42
42
|
try {
|
|
43
43
|
const packageConfig = getAuthPackage()?.getConfig();
|
|
44
|
-
const key = storageKey ?? packageConfig?.storageKeys.
|
|
44
|
+
const key = storageKey ?? packageConfig?.storageKeys.anonymousMode ?? "@auth_anonymous_mode";
|
|
45
45
|
|
|
46
46
|
const storageProvider = getAuthPackage()?.getStorageProvider();
|
|
47
47
|
if (!storageProvider) return;
|
|
@@ -9,6 +9,6 @@ export function emitUserAuthenticated(userId: string): void {
|
|
|
9
9
|
DeviceEventEmitter.emit("user-authenticated", { userId });
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function
|
|
13
|
-
DeviceEventEmitter.emit("
|
|
12
|
+
export function emitAnonymousModeEnabled(): void {
|
|
13
|
+
DeviceEventEmitter.emit("anonymous-mode-enabled");
|
|
14
14
|
}
|
|
@@ -52,7 +52,7 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
52
52
|
|
|
53
53
|
const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
|
|
54
54
|
useAuthModalStore();
|
|
55
|
-
const { isAuthenticated,
|
|
55
|
+
const { isAuthenticated, isAnonymous } = useAuth();
|
|
56
56
|
|
|
57
57
|
useEffect(() => {
|
|
58
58
|
if (isVisible) {
|
|
@@ -63,11 +63,11 @@ export const AuthBottomSheet: React.FC<AuthBottomSheetProps> = ({
|
|
|
63
63
|
}, [isVisible]);
|
|
64
64
|
|
|
65
65
|
useEffect(() => {
|
|
66
|
-
if (isAuthenticated && !
|
|
66
|
+
if (isAuthenticated && !isAnonymous && isVisible) {
|
|
67
67
|
hideAuthModal();
|
|
68
68
|
executePendingCallback();
|
|
69
69
|
}
|
|
70
|
-
}, [isAuthenticated,
|
|
70
|
+
}, [isAuthenticated, isAnonymous, isVisible, hideAuthModal, executePendingCallback]);
|
|
71
71
|
|
|
72
72
|
const handleDismiss = useCallback(() => {
|
|
73
73
|
hideAuthModal();
|
|
@@ -38,12 +38,12 @@ export const useSignOutMutation = () => {
|
|
|
38
38
|
});
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
export const
|
|
41
|
+
export const useAnonymousModeMutation = () => {
|
|
42
42
|
return useMutation({
|
|
43
43
|
mutationFn: async (): Promise<void> => {
|
|
44
44
|
const service = getAuthService();
|
|
45
45
|
if (!service) throw new Error("Auth Service not initialized");
|
|
46
|
-
return service.
|
|
46
|
+
return service.setAnonymousMode();
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
49
|
};
|
|
@@ -11,11 +11,10 @@ import {
|
|
|
11
11
|
useAuthStore,
|
|
12
12
|
selectUser,
|
|
13
13
|
selectLoading,
|
|
14
|
-
selectIsGuest,
|
|
15
14
|
selectError,
|
|
16
15
|
selectSetLoading,
|
|
17
16
|
selectSetError,
|
|
18
|
-
|
|
17
|
+
selectSetIsAnonymous,
|
|
19
18
|
selectIsAuthenticated,
|
|
20
19
|
selectUserId,
|
|
21
20
|
selectUserType,
|
|
@@ -27,7 +26,7 @@ import {
|
|
|
27
26
|
useSignInMutation,
|
|
28
27
|
useSignUpMutation,
|
|
29
28
|
useSignOutMutation,
|
|
30
|
-
|
|
29
|
+
useAnonymousModeMutation,
|
|
31
30
|
} from "./mutations/useAuthMutations";
|
|
32
31
|
import type { AuthUser } from "../../domain/entities/AuthUser";
|
|
33
32
|
|
|
@@ -42,11 +41,9 @@ export interface UseAuthResult {
|
|
|
42
41
|
loading: boolean;
|
|
43
42
|
/** Whether auth is ready (initialized and not loading) */
|
|
44
43
|
isAuthReady: boolean;
|
|
45
|
-
/** Whether user is in guest mode */
|
|
46
|
-
isGuest: boolean;
|
|
47
44
|
/** Whether user is anonymous */
|
|
48
45
|
isAnonymous: boolean;
|
|
49
|
-
/** Whether user is authenticated (not
|
|
46
|
+
/** Whether user is authenticated (not anonymous) */
|
|
50
47
|
isAuthenticated: boolean;
|
|
51
48
|
/** Current error message */
|
|
52
49
|
error: string | null;
|
|
@@ -56,8 +53,8 @@ export interface UseAuthResult {
|
|
|
56
53
|
signIn: (email: string, password: string) => Promise<void>;
|
|
57
54
|
/** Sign out function */
|
|
58
55
|
signOut: () => Promise<void>;
|
|
59
|
-
/** Continue
|
|
60
|
-
|
|
56
|
+
/** Continue anonymously function */
|
|
57
|
+
continueAnonymously: () => Promise<void>;
|
|
61
58
|
/** Set error manually (for form validation, etc.) */
|
|
62
59
|
setError: (error: string | null) => void;
|
|
63
60
|
}
|
|
@@ -72,7 +69,6 @@ export function useAuth(): UseAuthResult {
|
|
|
72
69
|
// State from store - using typed selectors
|
|
73
70
|
const user = useAuthStore(selectUser);
|
|
74
71
|
const loading = useAuthStore(selectLoading);
|
|
75
|
-
const isGuest = useAuthStore(selectIsGuest);
|
|
76
72
|
const error = useAuthStore(selectError);
|
|
77
73
|
const isAuthenticated = useAuthStore(selectIsAuthenticated);
|
|
78
74
|
const userId = useAuthStore(selectUserId);
|
|
@@ -83,13 +79,13 @@ export function useAuth(): UseAuthResult {
|
|
|
83
79
|
// Actions from store - using typed selectors
|
|
84
80
|
const setLoading = useAuthStore(selectSetLoading);
|
|
85
81
|
const setError = useAuthStore(selectSetError);
|
|
86
|
-
const
|
|
82
|
+
const setIsAnonymous = useAuthStore(selectSetIsAnonymous);
|
|
87
83
|
|
|
88
84
|
// Mutations
|
|
89
85
|
const signInMutation = useSignInMutation();
|
|
90
86
|
const signUpMutation = useSignUpMutation();
|
|
91
87
|
const signOutMutation = useSignOutMutation();
|
|
92
|
-
const
|
|
88
|
+
const anonymousModeMutation = useAnonymousModeMutation();
|
|
93
89
|
|
|
94
90
|
const signUp = useCallback(
|
|
95
91
|
async (email: string, password: string, displayName?: string) => {
|
|
@@ -97,9 +93,7 @@ export function useAuth(): UseAuthResult {
|
|
|
97
93
|
setLoading(true);
|
|
98
94
|
setError(null);
|
|
99
95
|
await signUpMutation.mutateAsync({ email, password, displayName });
|
|
100
|
-
|
|
101
|
-
setIsGuest(false);
|
|
102
|
-
}
|
|
96
|
+
setIsAnonymous(false);
|
|
103
97
|
} catch (err: unknown) {
|
|
104
98
|
const errorMessage = err instanceof Error ? err.message : "Sign up failed";
|
|
105
99
|
setError(errorMessage);
|
|
@@ -108,7 +102,7 @@ export function useAuth(): UseAuthResult {
|
|
|
108
102
|
setLoading(false);
|
|
109
103
|
}
|
|
110
104
|
},
|
|
111
|
-
[
|
|
105
|
+
[setIsAnonymous, setLoading, setError, signUpMutation]
|
|
112
106
|
);
|
|
113
107
|
|
|
114
108
|
const signIn = useCallback(
|
|
@@ -117,9 +111,7 @@ export function useAuth(): UseAuthResult {
|
|
|
117
111
|
setLoading(true);
|
|
118
112
|
setError(null);
|
|
119
113
|
await signInMutation.mutateAsync({ email, password });
|
|
120
|
-
|
|
121
|
-
setIsGuest(false);
|
|
122
|
-
}
|
|
114
|
+
setIsAnonymous(false);
|
|
123
115
|
} catch (err: unknown) {
|
|
124
116
|
const errorMessage = err instanceof Error ? err.message : "Sign in failed";
|
|
125
117
|
setError(errorMessage);
|
|
@@ -128,7 +120,7 @@ export function useAuth(): UseAuthResult {
|
|
|
128
120
|
setLoading(false);
|
|
129
121
|
}
|
|
130
122
|
},
|
|
131
|
-
[
|
|
123
|
+
[setIsAnonymous, setLoading, setError, signInMutation]
|
|
132
124
|
);
|
|
133
125
|
|
|
134
126
|
const signOut = useCallback(async () => {
|
|
@@ -140,17 +132,17 @@ export function useAuth(): UseAuthResult {
|
|
|
140
132
|
}
|
|
141
133
|
}, [setLoading, signOutMutation]);
|
|
142
134
|
|
|
143
|
-
const
|
|
135
|
+
const continueAnonymously = useCallback(async () => {
|
|
144
136
|
try {
|
|
145
137
|
setLoading(true);
|
|
146
|
-
await
|
|
147
|
-
|
|
138
|
+
await anonymousModeMutation.mutateAsync();
|
|
139
|
+
setIsAnonymous(true);
|
|
148
140
|
} catch {
|
|
149
|
-
|
|
141
|
+
setIsAnonymous(true);
|
|
150
142
|
} finally {
|
|
151
143
|
setLoading(false);
|
|
152
144
|
}
|
|
153
|
-
}, [
|
|
145
|
+
}, [setIsAnonymous, setLoading, anonymousModeMutation]);
|
|
154
146
|
|
|
155
147
|
return {
|
|
156
148
|
user,
|
|
@@ -158,14 +150,13 @@ export function useAuth(): UseAuthResult {
|
|
|
158
150
|
userType,
|
|
159
151
|
loading,
|
|
160
152
|
isAuthReady,
|
|
161
|
-
isGuest,
|
|
162
153
|
isAnonymous,
|
|
163
154
|
isAuthenticated,
|
|
164
155
|
error,
|
|
165
156
|
signUp,
|
|
166
157
|
signIn,
|
|
167
158
|
signOut,
|
|
168
|
-
|
|
159
|
+
continueAnonymously,
|
|
169
160
|
setError,
|
|
170
161
|
};
|
|
171
162
|
}
|
|
@@ -22,7 +22,7 @@ import { useAuthModalStore } from "../stores/authModalStore";
|
|
|
22
22
|
import { selectShowAuthModal } from "../stores/auth.selectors";
|
|
23
23
|
|
|
24
24
|
export interface UseAuthRequiredResult {
|
|
25
|
-
/** Whether user is authenticated (not
|
|
25
|
+
/** Whether user is authenticated (not anonymous) */
|
|
26
26
|
isAllowed: boolean;
|
|
27
27
|
/** Whether auth is still loading */
|
|
28
28
|
isLoading: boolean;
|
|
@@ -19,13 +19,13 @@ export interface UseLoginFormResult {
|
|
|
19
19
|
handleEmailChange: (text: string) => void;
|
|
20
20
|
handlePasswordChange: (text: string) => void;
|
|
21
21
|
handleSignIn: () => Promise<void>;
|
|
22
|
-
|
|
22
|
+
handleContinueAnonymously: () => Promise<void>;
|
|
23
23
|
displayError: string | null;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function useLoginForm(): UseLoginFormResult {
|
|
27
27
|
const { t } = useLocalization();
|
|
28
|
-
const { signIn, loading, error,
|
|
28
|
+
const { signIn, loading, error, continueAnonymously } = useAuth();
|
|
29
29
|
|
|
30
30
|
const [email, setEmail] = useState("");
|
|
31
31
|
const [password, setPassword] = useState("");
|
|
@@ -81,13 +81,13 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
81
81
|
}
|
|
82
82
|
}, [email, password, t, signIn]);
|
|
83
83
|
|
|
84
|
-
const
|
|
84
|
+
const handleContinueAnonymously = useCallback(async () => {
|
|
85
85
|
try {
|
|
86
|
-
await
|
|
86
|
+
await continueAnonymously();
|
|
87
87
|
} catch {
|
|
88
|
-
// Silent fail -
|
|
88
|
+
// Silent fail - anonymous mode should always work
|
|
89
89
|
}
|
|
90
|
-
}, [
|
|
90
|
+
}, [continueAnonymously]);
|
|
91
91
|
|
|
92
92
|
const displayError = localError || error;
|
|
93
93
|
|
|
@@ -101,7 +101,7 @@ export function useLoginForm(): UseLoginFormResult {
|
|
|
101
101
|
handleEmailChange,
|
|
102
102
|
handlePasswordChange,
|
|
103
103
|
handleSignIn,
|
|
104
|
-
|
|
104
|
+
handleContinueAnonymously,
|
|
105
105
|
displayError,
|
|
106
106
|
};
|
|
107
107
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { useAuth } from "./useAuth";
|
|
9
|
-
import {
|
|
9
|
+
import { generateAnonymousName, type AnonymousNameConfig } from "../../domain/utils/anonymousNameGenerator";
|
|
10
10
|
|
|
11
11
|
export interface UserProfileData {
|
|
12
12
|
displayName?: string;
|
|
@@ -17,9 +17,9 @@ export interface UserProfileData {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface UseUserProfileParams {
|
|
20
|
-
|
|
20
|
+
anonymousDisplayName?: string;
|
|
21
21
|
accountRoute?: string;
|
|
22
|
-
|
|
22
|
+
anonymousNameConfig?: AnonymousNameConfig;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export const useUserProfile = (
|
|
@@ -27,9 +27,9 @@ export const useUserProfile = (
|
|
|
27
27
|
): UserProfileData | undefined => {
|
|
28
28
|
const { user } = useAuth();
|
|
29
29
|
|
|
30
|
-
const
|
|
30
|
+
const anonymousName = params?.anonymousDisplayName;
|
|
31
31
|
const accountRoute = params?.accountRoute;
|
|
32
|
-
const nameConfig = params?.
|
|
32
|
+
const nameConfig = params?.anonymousNameConfig;
|
|
33
33
|
|
|
34
34
|
return useMemo(() => {
|
|
35
35
|
if (!user) {
|
|
@@ -40,7 +40,7 @@ export const useUserProfile = (
|
|
|
40
40
|
|
|
41
41
|
if (isAnonymous) {
|
|
42
42
|
return {
|
|
43
|
-
displayName:
|
|
43
|
+
displayName: generateAnonymousName(user.uid, nameConfig),
|
|
44
44
|
userId: user.uid,
|
|
45
45
|
isAnonymous: true,
|
|
46
46
|
accountSettingsRoute: accountRoute,
|
|
@@ -49,10 +49,10 @@ export const useUserProfile = (
|
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
accountSettingsRoute: accountRoute,
|
|
52
|
-
displayName: user.displayName || user.email ||
|
|
52
|
+
displayName: user.displayName || user.email || anonymousName,
|
|
53
53
|
userId: user.uid,
|
|
54
54
|
isAnonymous: false,
|
|
55
55
|
avatarUrl: user.photoURL || undefined,
|
|
56
56
|
};
|
|
57
|
-
}, [user,
|
|
57
|
+
}, [user, anonymousName, accountRoute, nameConfig]);
|
|
58
58
|
};
|
|
@@ -24,9 +24,9 @@ export const selectUser = (state: AuthStore): AuthUser | null => state.user;
|
|
|
24
24
|
export const selectLoading = (state: AuthStore): boolean => state.loading;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Select
|
|
27
|
+
* Select anonymous mode (state flag)
|
|
28
28
|
*/
|
|
29
|
-
export const
|
|
29
|
+
export const selectIsAnonymousState = (state: AuthStore): boolean => state.isAnonymous;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Select error
|
|
@@ -54,9 +54,9 @@ export const selectSetLoading = (state: AuthStore) => state.setLoading;
|
|
|
54
54
|
export const selectSetError = (state: AuthStore) => state.setError;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Select
|
|
57
|
+
* Select setIsAnonymous action
|
|
58
58
|
*/
|
|
59
|
-
export const
|
|
59
|
+
export const selectSetIsAnonymous = (state: AuthStore) => state.setIsAnonymous;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Select showAuthModal action (from authModalStore)
|
|
@@ -77,10 +77,10 @@ export const selectUserId = (state: AuthStore): string | null => {
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Check if user is authenticated (not
|
|
80
|
+
* Check if user is authenticated (not anonymous)
|
|
81
81
|
*/
|
|
82
82
|
export const selectIsAuthenticated = (state: AuthStore): boolean => {
|
|
83
|
-
return !!state.user && !state.
|
|
83
|
+
return !!state.user && !state.isAnonymous && !state.user.isAnonymous;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
/**
|
|
@@ -14,12 +14,12 @@ import { initialAuthState } from "../../types/auth-store.types";
|
|
|
14
14
|
import {
|
|
15
15
|
selectUser,
|
|
16
16
|
selectLoading,
|
|
17
|
-
|
|
17
|
+
selectIsAnonymousState,
|
|
18
18
|
selectError,
|
|
19
19
|
selectFirebaseUserId,
|
|
20
20
|
selectSetLoading,
|
|
21
21
|
selectSetError,
|
|
22
|
-
|
|
22
|
+
selectSetIsAnonymous,
|
|
23
23
|
selectUserId,
|
|
24
24
|
selectIsAuthenticated,
|
|
25
25
|
selectIsAnonymous,
|
|
@@ -34,12 +34,12 @@ export type { AuthState, AuthActions, UserType };
|
|
|
34
34
|
export {
|
|
35
35
|
selectUser,
|
|
36
36
|
selectLoading,
|
|
37
|
-
|
|
37
|
+
selectIsAnonymousState,
|
|
38
38
|
selectError,
|
|
39
39
|
selectFirebaseUserId,
|
|
40
40
|
selectSetLoading,
|
|
41
41
|
selectSetError,
|
|
42
|
-
|
|
42
|
+
selectSetIsAnonymous,
|
|
43
43
|
selectUserId,
|
|
44
44
|
selectIsAuthenticated,
|
|
45
45
|
selectIsAnonymous,
|
|
@@ -62,21 +62,21 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
|
|
|
62
62
|
name: "auth-store",
|
|
63
63
|
initialState: initialAuthState,
|
|
64
64
|
persist: true,
|
|
65
|
-
version:
|
|
65
|
+
version: 2,
|
|
66
66
|
partialize: (state) => ({
|
|
67
|
-
|
|
67
|
+
isAnonymous: state.isAnonymous,
|
|
68
68
|
initialized: state.initialized,
|
|
69
69
|
}),
|
|
70
70
|
actions: (set, get) => ({
|
|
71
71
|
setFirebaseUser: (firebaseUser) => {
|
|
72
|
-
const {
|
|
72
|
+
const { isAnonymous } = get();
|
|
73
73
|
|
|
74
74
|
let user: AuthUser | null = null;
|
|
75
75
|
|
|
76
76
|
if (firebaseUser) {
|
|
77
77
|
if (!firebaseUser.isAnonymous) {
|
|
78
78
|
user = mapToAuthUser(firebaseUser);
|
|
79
|
-
} else if (!
|
|
79
|
+
} else if (!isAnonymous) {
|
|
80
80
|
user = mapToAuthUser(firebaseUser);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -85,24 +85,25 @@ export const useAuthStore = createStore<AuthState, AuthActions>({
|
|
|
85
85
|
firebaseUser,
|
|
86
86
|
user,
|
|
87
87
|
loading: false,
|
|
88
|
+
isAnonymous: firebaseUser?.isAnonymous ?? false,
|
|
88
89
|
});
|
|
89
90
|
},
|
|
90
91
|
|
|
91
92
|
setLoading: (loading) => set({ loading }),
|
|
92
93
|
|
|
93
|
-
|
|
94
|
+
setIsAnonymous: (isAnonymous) => {
|
|
94
95
|
const { firebaseUser } = get();
|
|
95
96
|
|
|
96
97
|
let user: AuthUser | null = null;
|
|
97
98
|
if (firebaseUser) {
|
|
98
99
|
if (!firebaseUser.isAnonymous) {
|
|
99
100
|
user = mapToAuthUser(firebaseUser);
|
|
100
|
-
} else if (!
|
|
101
|
+
} else if (!isAnonymous) {
|
|
101
102
|
user = mapToAuthUser(firebaseUser);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
set({
|
|
106
|
+
set({ isAnonymous, user });
|
|
106
107
|
},
|
|
107
108
|
|
|
108
109
|
setError: (error) => set({ error }),
|
|
@@ -138,13 +139,6 @@ export function getIsAuthenticated(): boolean {
|
|
|
138
139
|
return selectIsAuthenticated(useAuthStore.getState());
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
/**
|
|
142
|
-
* Check if guest without hook
|
|
143
|
-
*/
|
|
144
|
-
export function getIsGuest(): boolean {
|
|
145
|
-
return useAuthStore.getState().isGuest;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
142
|
/**
|
|
149
143
|
* Check if anonymous without hook
|
|
150
144
|
*/
|
|
@@ -44,9 +44,9 @@ export function initializeAuthListener(
|
|
|
44
44
|
|
|
45
45
|
const service = getAuthService();
|
|
46
46
|
if (service) {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
store.
|
|
47
|
+
const isAnonymous = service.getIsAnonymousMode();
|
|
48
|
+
if (isAnonymous) {
|
|
49
|
+
store.setIsAnonymous(true);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -84,8 +84,8 @@ export function initializeAuthListener(
|
|
|
84
84
|
store.setFirebaseUser(user);
|
|
85
85
|
store.setInitialized(true);
|
|
86
86
|
|
|
87
|
-
if (user && !user.isAnonymous && store.
|
|
88
|
-
store.
|
|
87
|
+
if (user && !user.isAnonymous && store.isAnonymous) {
|
|
88
|
+
store.setIsAnonymous(false);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// Call optional callback
|
|
@@ -21,8 +21,8 @@ export interface AuthState {
|
|
|
21
21
|
firebaseUser: User | null;
|
|
22
22
|
/** Loading state during auth operations */
|
|
23
23
|
loading: boolean;
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/** Anonymous mode (user using anonymous auth) */
|
|
25
|
+
isAnonymous: boolean;
|
|
26
26
|
/** Error message from last auth operation */
|
|
27
27
|
error: string | null;
|
|
28
28
|
/** Whether auth listener has initialized */
|
|
@@ -37,8 +37,8 @@ export interface AuthActions {
|
|
|
37
37
|
setFirebaseUser: (user: User | null) => void;
|
|
38
38
|
/** Set loading state */
|
|
39
39
|
setLoading: (loading: boolean) => void;
|
|
40
|
-
/** Set
|
|
41
|
-
|
|
40
|
+
/** Set anonymous mode */
|
|
41
|
+
setIsAnonymous: (isAnonymous: boolean) => void;
|
|
42
42
|
/** Set error message */
|
|
43
43
|
setError: (error: string | null) => void;
|
|
44
44
|
/** Mark as initialized */
|
|
@@ -54,7 +54,7 @@ export const initialAuthState: AuthState = {
|
|
|
54
54
|
user: null,
|
|
55
55
|
firebaseUser: null,
|
|
56
56
|
loading: true,
|
|
57
|
-
|
|
57
|
+
isAnonymous: false,
|
|
58
58
|
error: null,
|
|
59
59
|
initialized: false,
|
|
60
60
|
};
|