@umituz/react-native-subscription 3.1.10 → 3.1.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.
Files changed (40) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/presentation/useCreditsRealTime.ts +10 -5
  3. package/src/domains/credits/utils/creditValidation.ts +5 -26
  4. package/src/domains/paywall/hooks/usePaywallActions.ts +21 -133
  5. package/src/domains/paywall/hooks/usePaywallActions.types.ts +16 -0
  6. package/src/domains/paywall/hooks/usePaywallPurchase.ts +78 -0
  7. package/src/domains/paywall/hooks/usePaywallRestore.ts +66 -0
  8. package/src/domains/revenuecat/infrastructure/services/userSwitchCore.ts +116 -0
  9. package/src/domains/revenuecat/infrastructure/services/userSwitchHandler.ts +19 -237
  10. package/src/domains/revenuecat/infrastructure/services/userSwitchHelpers.ts +55 -0
  11. package/src/domains/revenuecat/infrastructure/services/userSwitchInitializer.ts +143 -0
  12. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +6 -3
  13. package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +2 -2
  14. package/src/domains/subscription/infrastructure/managers/packageHandlerFactory.ts +2 -2
  15. package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +2 -2
  16. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.logic.ts +52 -0
  17. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.tsx +15 -89
  18. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.types.ts +59 -0
  19. package/src/domains/subscription/presentation/components/details/CreditRow.tsx +9 -0
  20. package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.tsx +23 -0
  21. package/src/domains/subscription/presentation/components/states/FeedbackState.tsx +36 -0
  22. package/src/domains/subscription/presentation/components/states/InitializingState.tsx +47 -0
  23. package/src/domains/subscription/presentation/components/states/OnboardingState.tsx +27 -0
  24. package/src/domains/subscription/presentation/components/states/PaywallState.tsx +66 -0
  25. package/src/domains/subscription/presentation/components/states/ReadyState.tsx +51 -0
  26. package/src/domains/subscription/presentation/providers/SubscriptionFlowProvider.tsx +8 -2
  27. package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx +119 -103
  28. package/src/domains/wallet/presentation/components/BalanceCard.tsx +7 -0
  29. package/src/domains/wallet/presentation/components/TransactionItem.tsx +11 -0
  30. package/src/index.components.ts +1 -1
  31. package/src/shared/infrastructure/SubscriptionEventBus.ts +4 -2
  32. package/src/shared/presentation/hooks/useFirestoreRealTime.ts +22 -6
  33. package/src/shared/utils/errors/errorAssertions.ts +35 -0
  34. package/src/shared/utils/errors/errorConversion.ts +73 -0
  35. package/src/shared/utils/errors/errorTypeGuards.ts +27 -0
  36. package/src/shared/utils/errors/errorWrappers.ts +54 -0
  37. package/src/shared/utils/errors/index.ts +19 -0
  38. package/src/shared/utils/errors/serviceErrors.ts +36 -0
  39. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.states.tsx +0 -187
  40. package/src/shared/utils/errorUtils.ts +0 -195
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "3.1.10",
3
+ "version": "3.1.12",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -49,15 +49,20 @@ export function useCreditsRealTime(userId: string | null | undefined) {
49
49
  /**
50
50
  * Hook to get derived credit values with real-time sync.
51
51
  * This is the real-time equivalent of the computed values in useCredits.
52
+ *
53
+ * PERFORMANCE: Uses useMemo to avoid recalculating on every render
52
54
  */
53
55
  export function useCreditsRealTimeDerived(userId: string | null | undefined) {
54
56
  const { credits, isLoading } = useCreditsRealTime(userId);
55
57
 
56
- const hasCredits = (credits?.credits ?? 0) > 0;
57
- const creditsPercent = credits ? Math.min(
58
- (credits.credits / credits.creditLimit) * 100,
59
- 100
60
- ) : 0;
58
+ const { hasCredits, creditsPercent } = useMemo(() => {
59
+ const hasCredits = (credits?.credits ?? 0) > 0;
60
+ const creditsPercent = credits ? Math.min(
61
+ (credits.credits / credits.creditLimit) * 100,
62
+ 100
63
+ ) : 0;
64
+ return { hasCredits, creditsPercent };
65
+ }, [credits]); // Include full credits object to catch all changes
61
66
 
62
67
  return {
63
68
  hasCredits,
@@ -1,27 +1,6 @@
1
- import { isValidNumber, isNonNegativeNumber } from "../../../shared/utils/validators";
1
+ /**
2
+ * Credit validation utilities
3
+ * Re-exports validation functions from creditCalculations for backwards compatibility
4
+ */
2
5
 
3
- const isValidBalance = (balance: number | null | undefined): balance is number => {
4
- return isValidNumber(balance) && isNonNegativeNumber(balance);
5
- };
6
-
7
- const isValidCost = (cost: number): boolean => {
8
- return isValidNumber(cost) && isNonNegativeNumber(cost);
9
- };
10
-
11
- const isValidMaxCredits = (max: number): boolean => {
12
- return isValidNumber(max) && max > 0;
13
- };
14
-
15
- export const canAffordAmount = (balance: number | null | undefined, cost: number): boolean => {
16
- if (!isValidBalance(balance) || !isValidCost(cost)) return false;
17
- return balance >= cost;
18
- };
19
-
20
- export const calculateSafePercentage = (
21
- current: number | null | undefined,
22
- max: number
23
- ): number => {
24
- if (!isValidNumber(current) || !isValidMaxCredits(max)) return 0;
25
- const percentage = (current / max) * 100;
26
- return Math.min(Math.max(percentage, 0), 100);
27
- };
6
+ export { canAffordAmount, calculateSafePercentage } from './creditCalculations';
@@ -1,24 +1,14 @@
1
1
  /**
2
2
  * Paywall Actions Hook
3
3
  *
4
- * Handles purchase and restore operations with premium verification.
5
- * Ref management and success checking extracted to utilities.
4
+ * Main entry point that combines purchase and restore handlers.
5
+ * Handlers extracted to separate modules for better maintainability.
6
6
  */
7
7
 
8
- import { useState, useCallback, useRef, useMemo } from "react";
9
- import type { PurchasesPackage } from "react-native-purchases";
10
- import { usePremiumVerification } from "./usePaywallActions.utils";
11
-
12
- interface UsePaywallActionsParams {
13
- packages?: PurchasesPackage[];
14
- purchasePackage: (pkg: PurchasesPackage) => Promise<boolean>;
15
- restorePurchase: () => Promise<boolean>;
16
- source?: string; // PurchaseSource
17
- onPurchaseSuccess?: () => void;
18
- onPurchaseError?: (error: Error | string) => void;
19
- onAuthRequired?: () => void;
20
- onClose?: () => void;
21
- }
8
+ import { useState, useRef, useMemo } from "react";
9
+ import type { UsePaywallActionsParams } from "./usePaywallActions.types";
10
+ import { usePurchaseHandler } from "./usePaywallPurchase";
11
+ import { useRestoreHandler } from "./usePaywallRestore";
22
12
 
23
13
  export function usePaywallActions({
24
14
  packages = [],
@@ -35,8 +25,6 @@ export function usePaywallActions({
35
25
  const isProcessingRef = useRef(isProcessing);
36
26
  isProcessingRef.current = isProcessing;
37
27
 
38
- const { verifyPremiumStatus } = usePremiumVerification();
39
-
40
28
  // Ref management
41
29
  const callbacksRef = useRef({
42
30
  purchasePackage,
@@ -59,128 +47,28 @@ export function usePaywallActions({
59
47
  packages,
60
48
  };
61
49
 
62
- // ─────────────────────────────────────────────────────────────
63
- // PURCHASE HANDLER
64
- // ─────────────────────────────────────────────────────────────
65
-
66
- const handlePurchase = useCallback(async () => {
67
- const currentSelectedId = selectedPlanId;
68
- if (!currentSelectedId) return;
69
- if (isProcessingRef.current) return;
70
-
71
- const pkg = callbacksRef.current.packages.find((p) => p.product.identifier === currentSelectedId);
72
- if (!pkg) {
73
- callbacksRef.current.onPurchaseError?.(new Error(`Package not found: ${currentSelectedId}`));
74
- return;
75
- }
76
-
77
- if (__DEV__) {
78
- console.log('[usePaywallActions] 🛒 Starting purchase', {
79
- productId: pkg.product.identifier,
80
- });
81
- }
82
-
83
- setIsProcessing(true);
84
-
85
- try {
86
- const success = await callbacksRef.current.purchasePackage(pkg);
87
-
88
- if (__DEV__) {
89
- console.log('[usePaywallActions] ✅ Purchase completed', { success });
90
- }
91
-
92
- let isActuallySuccessful = success === true;
93
-
94
- // Fallback verification if success is undefined
95
- if (success === undefined) {
96
- isActuallySuccessful = await verifyPremiumStatus();
97
- }
98
-
99
- if (isActuallySuccessful) {
100
- if (__DEV__) {
101
- console.log('[usePaywallActions] 🎉 Purchase successful, closing paywall');
102
- }
103
- callbacksRef.current.onPurchaseSuccess?.();
104
- callbacksRef.current.onClose?.();
105
- } else {
106
- if (__DEV__) {
107
- console.warn('[usePaywallActions] ⚠️ Purchase did not indicate success');
108
- }
109
- }
110
- } catch (error) {
111
- if (__DEV__) {
112
- console.error('[usePaywallActions] ❌ Purchase error:', error);
113
- }
114
- callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
115
- } finally {
116
- setIsProcessing(false);
117
- }
118
- }, [selectedPlanId, verifyPremiumStatus]);
119
-
120
- // ─────────────────────────────────────────────────────────────
121
- // RESTORE HANDLER
122
- // ─────────────────────────────────────────────────────────────
123
-
124
- const handleRestore = useCallback(async () => {
125
- if (isProcessingRef.current) return;
126
-
127
- if (__DEV__) {
128
- console.log('[usePaywallActions] 🔄 Starting restore');
129
- }
130
-
131
- setIsProcessing(true);
132
-
133
- try {
134
- const success = await callbacksRef.current.restorePurchase();
135
-
136
- if (__DEV__) {
137
- console.log('[usePaywallActions] ✅ Restore completed', { success });
138
- }
139
-
140
- let isActuallySuccessful = success === true;
141
-
142
- // Fallback verification if success is undefined
143
- if (success === undefined) {
144
- isActuallySuccessful = await verifyPremiumStatus();
145
- }
146
-
147
- if (isActuallySuccessful) {
148
- if (__DEV__) {
149
- console.log('[usePaywallActions] 🎉 Restore successful, closing paywall');
150
- }
151
- callbacksRef.current.onPurchaseSuccess?.();
152
- callbacksRef.current.onClose?.();
153
- } else {
154
- if (__DEV__) {
155
- console.warn('[usePaywallActions] ⚠️ Restore did not indicate success');
156
- }
157
- }
158
- } catch (error) {
159
- if (__DEV__) {
160
- console.error('[usePaywallActions] ❌ Restore error:', error);
161
- }
162
- callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
163
- } finally {
164
- setIsProcessing(false);
165
- }
166
- }, [verifyPremiumStatus]);
50
+ // Extracted handlers
51
+ const handlePurchase = usePurchaseHandler({
52
+ selectedPlanId,
53
+ isProcessingRef,
54
+ callbacksRef,
55
+ });
167
56
 
168
- // ─────────────────────────────────────────────────────────────
169
- // RESET
170
- // ─────────────────────────────────────────────────────────────
57
+ const handleRestore = useRestoreHandler({
58
+ isProcessingRef,
59
+ callbacksRef,
60
+ });
171
61
 
172
- const resetState = useCallback(() => {
62
+ // Reset state
63
+ const resetState = () => {
173
64
  if (__DEV__) {
174
65
  console.log('[usePaywallActions] 🧹 Resetting state');
175
66
  }
176
67
  setSelectedPlanId(null);
177
68
  setIsProcessing(false);
178
- }, []);
179
-
180
- // ─────────────────────────────────────────────────────────────
181
- // RETURN
182
- // ─────────────────────────────────────────────────────────────
69
+ };
183
70
 
71
+ // Return API
184
72
  return useMemo(() => ({
185
73
  selectedPlanId,
186
74
  setSelectedPlanId,
@@ -188,5 +76,5 @@ export function usePaywallActions({
188
76
  handlePurchase,
189
77
  handleRestore,
190
78
  resetState,
191
- }), [selectedPlanId, isProcessing, handlePurchase, handleRestore, resetState]);
79
+ }), [selectedPlanId, isProcessing, handlePurchase, handleRestore]);
192
80
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Paywall Actions Types
3
+ */
4
+
5
+ import type { PurchasesPackage } from "react-native-purchases";
6
+
7
+ export interface UsePaywallActionsParams {
8
+ packages?: PurchasesPackage[];
9
+ purchasePackage: (pkg: PurchasesPackage) => Promise<boolean>;
10
+ restorePurchase: () => Promise<boolean>;
11
+ source?: string;
12
+ onPurchaseSuccess?: () => void;
13
+ onPurchaseError?: (error: Error | string) => void;
14
+ onAuthRequired?: () => void;
15
+ onClose?: () => void;
16
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Paywall Purchase Handler
3
+ *
4
+ * Extracted purchase logic from usePaywallActions for better modularity.
5
+ */
6
+
7
+ import { useCallback } from "react";
8
+ import type { UsePaywallActionsParams } from "./usePaywallActions.types";
9
+ import { usePremiumVerification } from "./usePaywallActions.utils";
10
+
11
+ interface UsePurchaseHandlerParams {
12
+ selectedPlanId: string | null;
13
+ isProcessingRef: React.MutableRefObject<boolean>;
14
+ callbacksRef: React.MutableRefObject<UsePaywallActionsParams>;
15
+ }
16
+
17
+ /**
18
+ * Hook to handle purchase operations.
19
+ * Extracted for better testability and modularity.
20
+ */
21
+ export function usePurchaseHandler({
22
+ selectedPlanId,
23
+ isProcessingRef,
24
+ callbacksRef,
25
+ }: UsePurchaseHandlerParams) {
26
+ const { verifyPremiumStatus } = usePremiumVerification();
27
+
28
+ const handlePurchase = useCallback(async () => {
29
+ const currentSelectedId = selectedPlanId;
30
+ if (!currentSelectedId) return;
31
+ if (isProcessingRef.current) return;
32
+
33
+ const pkg = callbacksRef.current.packages?.find((p) => p.product.identifier === currentSelectedId);
34
+ if (!pkg) {
35
+ callbacksRef.current.onPurchaseError?.(new Error(`Package not found: ${currentSelectedId}`));
36
+ return;
37
+ }
38
+
39
+ if (__DEV__) {
40
+ console.log('[usePurchaseHandler] 🛒 Starting purchase', {
41
+ productId: pkg.product.identifier,
42
+ });
43
+ }
44
+
45
+ try {
46
+ const success = await callbacksRef.current.purchasePackage(pkg);
47
+
48
+ if (__DEV__) {
49
+ console.log('[usePurchaseHandler] ✅ Purchase completed', { success });
50
+ }
51
+
52
+ let isActuallySuccessful = success === true;
53
+
54
+ if (success === undefined) {
55
+ isActuallySuccessful = await verifyPremiumStatus();
56
+ }
57
+
58
+ if (isActuallySuccessful) {
59
+ if (__DEV__) {
60
+ console.log('[usePurchaseHandler] 🎉 Purchase successful, closing paywall');
61
+ }
62
+ callbacksRef.current.onPurchaseSuccess?.();
63
+ callbacksRef.current.onClose?.();
64
+ } else {
65
+ if (__DEV__) {
66
+ console.warn('[usePurchaseHandler] ⚠️ Purchase did not indicate success');
67
+ }
68
+ }
69
+ } catch (error) {
70
+ if (__DEV__) {
71
+ console.error('[usePurchaseHandler] ❌ Purchase error:', error);
72
+ }
73
+ callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
74
+ }
75
+ }, [selectedPlanId, verifyPremiumStatus, callbacksRef, isProcessingRef]);
76
+
77
+ return handlePurchase;
78
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Paywall Restore Handler
3
+ *
4
+ * Extracted restore logic from usePaywallActions for better modularity.
5
+ */
6
+
7
+ import { useCallback } from "react";
8
+ import type { UsePaywallActionsParams } from "./usePaywallActions.types";
9
+ import { usePremiumVerification } from "./usePaywallActions.utils";
10
+
11
+ interface UseRestoreHandlerParams {
12
+ isProcessingRef: React.MutableRefObject<boolean>;
13
+ callbacksRef: React.MutableRefObject<UsePaywallActionsParams>;
14
+ }
15
+
16
+ /**
17
+ * Hook to handle restore operations.
18
+ * Extracted for better testability and modularity.
19
+ */
20
+ export function useRestoreHandler({
21
+ isProcessingRef,
22
+ callbacksRef,
23
+ }: UseRestoreHandlerParams) {
24
+ const { verifyPremiumStatus } = usePremiumVerification();
25
+
26
+ const handleRestore = useCallback(async () => {
27
+ if (isProcessingRef.current) return;
28
+
29
+ if (__DEV__) {
30
+ console.log('[useRestoreHandler] 🔄 Starting restore');
31
+ }
32
+
33
+ try {
34
+ const success = await callbacksRef.current.restorePurchase();
35
+
36
+ if (__DEV__) {
37
+ console.log('[useRestoreHandler] ✅ Restore completed', { success });
38
+ }
39
+
40
+ let isActuallySuccessful = success === true;
41
+
42
+ if (success === undefined) {
43
+ isActuallySuccessful = await verifyPremiumStatus();
44
+ }
45
+
46
+ if (isActuallySuccessful) {
47
+ if (__DEV__) {
48
+ console.log('[useRestoreHandler] 🎉 Restore successful, closing paywall');
49
+ }
50
+ callbacksRef.current.onPurchaseSuccess?.();
51
+ callbacksRef.current.onClose?.();
52
+ } else {
53
+ if (__DEV__) {
54
+ console.warn('[useRestoreHandler] ⚠️ Restore did not indicate success');
55
+ }
56
+ }
57
+ } catch (error) {
58
+ if (__DEV__) {
59
+ console.error('[useRestoreHandler] ❌ Restore error:', error);
60
+ }
61
+ callbacksRef.current.onPurchaseError?.(error instanceof Error ? error : new Error(String(error)));
62
+ }
63
+ }, [verifyPremiumStatus, callbacksRef, isProcessingRef]);
64
+
65
+ return handleRestore;
66
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * User Switch Core Handler
3
+ *
4
+ * Core user switch logic. Handles switching between anonymous and authenticated users.
5
+ */
6
+
7
+ import Purchases, { type CustomerInfo } from "react-native-purchases";
8
+ import type { InitializeResult } from "../../../../shared/application/ports/IRevenueCatService";
9
+ import type { InitializerDeps } from "./RevenueCatInitializer.types";
10
+ import { FAILED_INITIALIZATION_RESULT } from "./initializerConstants";
11
+ import { UserSwitchMutex } from "./UserSwitchMutex";
12
+ import { ANONYMOUS_CACHE_KEY } from "../../../subscription/core/SubscriptionConstants";
13
+ import {
14
+ normalizeUserId,
15
+ isAnonymousId,
16
+ buildSuccessResult,
17
+ fetchOfferingsSafe,
18
+ } from "./userSwitchHelpers";
19
+
20
+ declare const __DEV__: boolean;
21
+
22
+ /**
23
+ * Handle user switch operation with mutex protection.
24
+ */
25
+ export async function handleUserSwitch(
26
+ deps: InitializerDeps,
27
+ userId: string
28
+ ): Promise<InitializeResult> {
29
+ const mutexKey = userId || ANONYMOUS_CACHE_KEY;
30
+
31
+ const { shouldProceed, existingPromise } = await UserSwitchMutex.acquire(mutexKey);
32
+
33
+ if (!shouldProceed && existingPromise) {
34
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
35
+ console.log('[UserSwitchCore] Using result from active switch operation');
36
+ }
37
+ return existingPromise as Promise<InitializeResult>;
38
+ }
39
+
40
+ const switchOperation = performUserSwitch(deps, userId);
41
+ UserSwitchMutex.setPromise(switchOperation);
42
+ return switchOperation;
43
+ }
44
+
45
+ /**
46
+ * Perform the actual user switch operation.
47
+ */
48
+ async function performUserSwitch(
49
+ deps: InitializerDeps,
50
+ userId: string
51
+ ): Promise<InitializeResult> {
52
+ try {
53
+ const currentAppUserId = await Purchases.getAppUserID();
54
+ const normalizedUserId = normalizeUserId(userId);
55
+ const normalizedCurrentUserId = isAnonymousId(currentAppUserId) ? null : currentAppUserId;
56
+
57
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
58
+ console.log('[UserSwitchCore] performUserSwitch:', {
59
+ providedUserId: userId,
60
+ normalizedUserId: normalizedUserId || '(null - anonymous)',
61
+ currentAppUserId,
62
+ normalizedCurrentUserId: normalizedCurrentUserId || '(null - anonymous)',
63
+ needsSwitch: normalizedCurrentUserId !== normalizedUserId,
64
+ });
65
+ }
66
+
67
+ let customerInfo: CustomerInfo;
68
+
69
+ if (normalizedCurrentUserId !== normalizedUserId) {
70
+ if (normalizedUserId) {
71
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
72
+ console.log('[UserSwitchCore] Calling Purchases.logIn() to switch from anonymous to:', normalizedUserId);
73
+ }
74
+ const result = await Purchases.logIn(normalizedUserId!);
75
+ customerInfo = result.customerInfo;
76
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
77
+ console.log('[UserSwitchCore] Purchases.logIn() successful, created:', result.created);
78
+ }
79
+ } else {
80
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
81
+ console.log('[UserSwitchCore] User is anonymous, fetching customer info');
82
+ }
83
+ customerInfo = await Purchases.getCustomerInfo();
84
+ }
85
+ } else {
86
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
87
+ console.log('[UserSwitchCore] No user switch needed, fetching current customer info');
88
+ }
89
+ customerInfo = await Purchases.getCustomerInfo();
90
+ }
91
+
92
+ deps.setInitialized(true);
93
+ deps.setCurrentUserId(normalizedUserId || undefined);
94
+ const offerings = await fetchOfferingsSafe();
95
+
96
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
97
+ console.log('[UserSwitchCore] User switch completed successfully');
98
+ }
99
+
100
+ return buildSuccessResult(deps, customerInfo, offerings);
101
+ } catch (error) {
102
+ let currentAppUserId = 'unknown';
103
+ try {
104
+ currentAppUserId = await Purchases.getAppUserID();
105
+ } catch {
106
+ // Ignore error in error handler
107
+ }
108
+
109
+ console.error('[UserSwitchCore] Failed during user switch or fetch', {
110
+ userId,
111
+ currentAppUserId,
112
+ error
113
+ });
114
+ return FAILED_INITIALIZATION_RESULT;
115
+ }
116
+ }