@umituz/react-native-subscription 2.37.38 → 2.37.40
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/domains/credits/application/CreditLimitCalculator.ts +1 -9
- package/src/domains/credits/application/CreditsInitializer.ts +5 -20
- package/src/domains/credits/application/DeductCreditsCommand.ts +13 -6
- package/src/domains/credits/application/RefundCreditsCommand.ts +1 -5
- package/src/domains/credits/application/credit-strategies/CreditAllocationOrchestrator.ts +1 -9
- package/src/domains/credits/application/credit-strategies/ICreditStrategy.ts +1 -5
- package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +1 -5
- package/src/domains/credits/application/creditDocumentHelpers.ts +2 -9
- package/src/domains/credits/application/creditOperationUtils.ts +1 -43
- package/src/domains/credits/core/Credits.ts +0 -23
- package/src/domains/credits/core/CreditsConstants.ts +0 -11
- package/src/domains/credits/core/CreditsMapper.ts +0 -6
- package/src/domains/credits/core/UserCreditsDocument.ts +0 -12
- package/src/domains/credits/infrastructure/CreditsRepository.ts +6 -1
- package/src/domains/credits/infrastructure/CreditsRepositoryManager.ts +0 -21
- package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +52 -1
- package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +2 -2
- package/src/domains/credits/presentation/useCredits.ts +10 -9
- package/src/domains/paywall/components/PaywallContainer.types.ts +0 -28
- package/src/domains/paywall/components/PaywallModal.styles.ts +0 -4
- package/src/domains/paywall/entities/types.ts +0 -5
- package/src/domains/paywall/hooks/usePaywallActions.ts +1 -15
- package/src/domains/revenuecat/core/errors/RevenueCatError.ts +0 -6
- package/src/domains/revenuecat/core/errors/RevenueCatErrorHandler.ts +0 -24
- package/src/domains/revenuecat/core/errors/RevenueCatErrorMessages.ts +0 -18
- package/src/domains/revenuecat/core/errors/index.ts +0 -4
- package/src/domains/revenuecat/core/types/RevenueCatConfig.ts +3 -7
- package/src/domains/revenuecat/core/types/RevenueCatData.ts +4 -9
- package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +5 -65
- package/src/domains/revenuecat/core/types/index.ts +0 -4
- package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +1 -24
- package/src/domains/subscription/application/SubscriptionAuthListener.ts +5 -21
- package/src/domains/subscription/application/SubscriptionInitializerTypes.ts +1 -5
- package/src/domains/subscription/application/SubscriptionSyncProcessor.ts +0 -4
- package/src/domains/subscription/application/SubscriptionSyncService.ts +4 -8
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
- package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +15 -2
- package/src/domains/subscription/application/initializer/ServiceConfigurator.ts +9 -2
- package/src/domains/subscription/application/statusChangeHandlers.ts +14 -27
- package/src/domains/subscription/application/syncIdGenerators.ts +0 -4
- package/src/domains/subscription/constants/thresholds.ts +0 -9
- package/src/domains/subscription/core/SubscriptionConstants.ts +0 -4
- package/src/domains/subscription/core/SubscriptionStatus.ts +11 -21
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +4 -7
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/subscriptionQueryKeys.ts +0 -13
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +0 -18
- package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +3 -17
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +0 -17
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionPackages.ts +0 -19
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -6
- package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +0 -17
- package/src/domains/subscription/infrastructure/state/initializationState.ts +0 -25
- package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +0 -21
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -17
- package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +0 -5
- package/src/domains/subscription/infrastructure/utils/renewal/PackageTierComparator.ts +1 -0
- package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +0 -18
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.styles.ts +0 -5
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCardTypes.ts +0 -5
- package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -5
- package/src/domains/subscription/presentation/stores/index.ts +0 -4
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +0 -13
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +30 -21
- package/src/domains/subscription/presentation/usePaywallVisibility.ts +0 -9
- package/src/domains/subscription/presentation/useSubscriptionStatus.ts +8 -11
- package/src/domains/subscription/utils/authGuards.ts +3 -0
- package/src/domains/trial/application/TrialService.ts +0 -9
- package/src/domains/trial/core/TrialTypes.ts +0 -8
- package/src/domains/wallet/domain/mappers/TransactionMapper.ts +0 -5
- package/src/domains/wallet/domain/types/transaction.types.ts +0 -7
- package/src/domains/wallet/index.ts +0 -7
- package/src/domains/wallet/infrastructure/config/walletConfig.ts +0 -11
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +6 -3
- package/src/domains/wallet/presentation/hooks/useWallet.ts +0 -7
- package/src/domains/wallet/utils/transactionIconMap.ts +0 -10
- package/src/global.d.ts +0 -6
- package/src/index.ts +1 -4
- package/src/init/createSubscriptionInitModule.ts +12 -2
- package/src/init/index.ts +1 -5
- package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -11
- package/src/shared/application/FeedbackService.ts +3 -21
- package/src/shared/application/ports/ISubscriptionRepository.ts +0 -4
- package/src/shared/infrastructure/SubscriptionEventBus.ts +0 -13
- package/src/shared/infrastructure/firestore/collectionUtils.ts +1 -17
- package/src/shared/infrastructure/firestore/index.ts +0 -4
- package/src/shared/infrastructure/firestore/resultUtils.ts +0 -12
- package/src/shared/infrastructure/react-query/hooks/usePreviousUserCleanup.ts +0 -17
- package/src/shared/infrastructure/react-query/queryConfig.ts +0 -15
- package/src/shared/utils/BaseError.ts +0 -5
- package/src/shared/utils/Result.ts +0 -20
- package/src/shared/utils/dateConverter.ts +6 -46
- package/src/utils/appUtils.ts +0 -16
- package/src/utils/creditMapper.ts +0 -7
- package/src/utils/dateUtils.compare.ts +0 -24
- package/src/utils/dateUtils.core.ts +0 -39
- package/src/utils/dateUtils.format.ts +0 -41
- package/src/utils/dateUtils.math.ts +0 -41
- package/src/utils/dateUtils.ts +0 -5
- package/src/utils/packagePeriodUtils.ts +0 -20
- package/src/utils/packageTypeDetector.ts +1 -21
- package/src/utils/premiumStatusUtils.ts +1 -14
- package/src/utils/priceUtils.ts +0 -35
- package/src/utils/tierUtils.ts +1 -8
- package/src/utils/types.ts +1 -25
- package/src/utils/validation.ts +1 -7
- package/src/domains/README.md +0 -52
- package/src/domains/config/domain/README.md +0 -37
- package/src/domains/config/domain/entities/README.md +0 -41
- package/src/domains/credits/application/credit-strategies/SyncCreditStrategy.ts +0 -24
- package/src/domains/paywall/README.md +0 -101
- package/src/domains/paywall/entities/README.md +0 -40
- package/src/domains/paywall/hooks/README.md +0 -41
- package/src/domains/subscription/application/syncConstants.ts +0 -1
- package/src/domains/subscription/infrastructure/README.md +0 -41
- package/src/domains/subscription/infrastructure/config/README.md +0 -49
- package/src/domains/subscription/infrastructure/handlers/README.md +0 -41
- package/src/domains/subscription/infrastructure/hooks/README.md +0 -50
- package/src/domains/subscription/infrastructure/managers/README.md +0 -41
- package/src/domains/subscription/infrastructure/services/README.md +0 -42
- package/src/domains/subscription/infrastructure/utils/README.md +0 -41
- package/src/domains/subscription/presentation/components/README.md +0 -155
- package/src/domains/subscription/presentation/components/details/CreditRow.md +0 -92
- package/src/domains/subscription/presentation/components/details/DetailRow.md +0 -91
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.md +0 -93
- package/src/domains/subscription/presentation/components/details/PremiumStatusBadge.md +0 -91
- package/src/domains/subscription/presentation/components/details/README.md +0 -99
- package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.md +0 -90
- package/src/domains/subscription/presentation/components/feedback/README.md +0 -99
- package/src/domains/subscription/presentation/components/paywall/PaywallModal.md +0 -94
- package/src/domains/subscription/presentation/components/paywall/README.md +0 -54
- package/src/domains/subscription/presentation/components/sections/README.md +0 -99
- package/src/domains/subscription/presentation/components/sections/SubscriptionSection.md +0 -94
- package/src/domains/subscription/presentation/utils/README.md +0 -31
- package/src/domains/wallet/README.md +0 -51
- package/src/domains/wallet/domain/README.md +0 -41
- package/src/domains/wallet/infrastructure/README.md +0 -41
- package/src/domains/wallet/presentation/components/README.md +0 -41
- package/src/domains/wallet/presentation/hooks/README.md +0 -41
- package/src/shared/application/ports/README.md +0 -48
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Premium Status Syncer
|
|
3
|
-
* Syncs premium status to database via callbacks
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import type { CustomerInfo } from "react-native-purchases";
|
|
7
2
|
import type { RevenueCatConfig, PackageType } from "../../../revenuecat/core/types";
|
|
8
3
|
import type { PurchaseSource } from "../../../subscription/core/SubscriptionConstants";
|
|
@@ -70,16 +65,7 @@ export async function notifyPurchaseCompleted(
|
|
|
70
65
|
return;
|
|
71
66
|
}
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
await config.onPurchaseCompleted(userId, productId, customerInfo, source, packageType);
|
|
75
|
-
} catch (error) {
|
|
76
|
-
// Silently fail callback notifications to prevent crashing the main flow
|
|
77
|
-
console.error('[PremiumStatusSyncer] Purchase completed callback failed:', {
|
|
78
|
-
userId,
|
|
79
|
-
productId,
|
|
80
|
-
error: error instanceof Error ? error.message : String(error)
|
|
81
|
-
});
|
|
82
|
-
}
|
|
68
|
+
await config.onPurchaseCompleted(userId, productId, customerInfo, source, packageType);
|
|
83
69
|
}
|
|
84
70
|
|
|
85
71
|
export async function notifyRestoreCompleted(
|
|
@@ -94,7 +80,7 @@ export async function notifyRestoreCompleted(
|
|
|
94
80
|
|
|
95
81
|
try {
|
|
96
82
|
await config.onRestoreCompleted(userId, isPremium, customerInfo);
|
|
97
|
-
} catch (
|
|
98
|
-
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error('[PremiumStatusSyncer] Restore callback failed:', error instanceof Error ? error.message : String(error));
|
|
99
85
|
}
|
|
100
86
|
}
|
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trial Eligibility Utilities
|
|
3
|
-
* Business logic for checking trial eligibility
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import Purchases, {
|
|
7
2
|
type IntroEligibility,
|
|
8
3
|
INTRO_ELIGIBILITY_STATUS,
|
|
9
4
|
} from "react-native-purchases";
|
|
10
5
|
|
|
11
|
-
/** Trial eligibility info for a single product */
|
|
12
6
|
export interface ProductTrialEligibility {
|
|
13
7
|
productId: string;
|
|
14
8
|
eligible: boolean;
|
|
15
9
|
trialDurationDays?: number;
|
|
16
10
|
}
|
|
17
11
|
|
|
18
|
-
/** Map of product ID to eligibility */
|
|
19
12
|
export type TrialEligibilityMap = Record<string, ProductTrialEligibility>;
|
|
20
13
|
|
|
21
|
-
/** Default trial duration in days */
|
|
22
14
|
const DEFAULT_TRIAL_DURATION_DAYS = 7;
|
|
23
15
|
|
|
24
|
-
/**
|
|
25
|
-
* Check trial eligibility for product IDs
|
|
26
|
-
*/
|
|
27
16
|
export async function checkTrialEligibility(
|
|
28
17
|
productIds: string[]
|
|
29
18
|
): Promise<TrialEligibilityMap> {
|
|
@@ -47,10 +36,6 @@ export async function checkTrialEligibility(
|
|
|
47
36
|
return result;
|
|
48
37
|
}
|
|
49
38
|
|
|
50
|
-
/**
|
|
51
|
-
* Create fallback eligibility map (all eligible)
|
|
52
|
-
* Used when eligibility check fails
|
|
53
|
-
*/
|
|
54
39
|
export function createFallbackEligibilityMap(
|
|
55
40
|
productIds: string[]
|
|
56
41
|
): TrialEligibilityMap {
|
|
@@ -67,9 +52,6 @@ export function createFallbackEligibilityMap(
|
|
|
67
52
|
return result;
|
|
68
53
|
}
|
|
69
54
|
|
|
70
|
-
/**
|
|
71
|
-
* Check if any product has eligible trial
|
|
72
|
-
*/
|
|
73
55
|
export function hasAnyEligibleTrial(
|
|
74
56
|
eligibilityMap: TrialEligibilityMap
|
|
75
57
|
): boolean {
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purchase Loading Store
|
|
3
|
-
* Global state for tracking purchase loading across the app
|
|
4
|
-
* Supports concurrent purchases via Map-based tracking
|
|
5
|
-
* Used by both PaywallModal and useSavedPurchaseAutoExecution
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { create } from "zustand";
|
|
9
2
|
|
|
10
3
|
interface PurchaseLoadingState {
|
|
11
|
-
/** Map of product IDs to purchase sources (supports concurrent purchases) */
|
|
12
4
|
activePurchases: Map<string, "manual" | "auto-execution">;
|
|
13
5
|
}
|
|
14
6
|
|
|
15
7
|
interface PurchaseLoadingActions {
|
|
16
|
-
/** Start purchase loading state for a product */
|
|
17
8
|
startPurchase: (productId: string, source: "manual" | "auto-execution") => void;
|
|
18
|
-
/** End purchase loading state for a product */
|
|
19
9
|
endPurchase: (productId: string) => void;
|
|
20
|
-
/** Check if any purchase is in progress, or if a specific product is being purchased */
|
|
21
10
|
isPurchasing: (productId?: string) => boolean;
|
|
22
|
-
/** Reset all state */
|
|
23
11
|
reset: () => void;
|
|
24
12
|
}
|
|
25
13
|
|
|
@@ -59,5 +47,4 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
|
|
|
59
47
|
},
|
|
60
48
|
}));
|
|
61
49
|
|
|
62
|
-
// Selectors for optimized re-renders
|
|
63
50
|
export const selectIsPurchasing = (state: PurchaseLoadingStore) => state.activePurchases.size > 0;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Auth-Aware Purchase Hook
|
|
3
|
-
* Handles purchase flow with authentication requirement
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useCallback } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
7
2
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
8
3
|
import { usePremium } from "./usePremium";
|
|
9
4
|
import type { PurchaseSource } from "../core/SubscriptionConstants";
|
|
@@ -35,10 +30,38 @@ export const useAuthAwarePurchase = (
|
|
|
35
30
|
params?: UseAuthAwarePurchaseParams
|
|
36
31
|
): UseAuthAwarePurchaseResult => {
|
|
37
32
|
const { purchasePackage, restorePurchase } = usePremium();
|
|
33
|
+
const isExecutingSavedRef = useRef(false);
|
|
34
|
+
|
|
35
|
+
const executeSavedPurchase = useCallback(async (): Promise<boolean> => {
|
|
36
|
+
const saved = authPurchaseStateManager.getSavedPurchase();
|
|
37
|
+
if (!saved) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = await purchasePackage(saved.pkg);
|
|
42
|
+
if (result) {
|
|
43
|
+
authPurchaseStateManager.clearSavedPurchase();
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}, [purchasePackage]);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const authProvider = authPurchaseStateManager.getProvider();
|
|
50
|
+
if (!authProvider) return;
|
|
51
|
+
|
|
52
|
+
const isAuth = authProvider.isAuthenticated();
|
|
53
|
+
const hasSavedPurchase = !!authPurchaseStateManager.getSavedPurchase();
|
|
54
|
+
|
|
55
|
+
if (isAuth && hasSavedPurchase && !isExecutingSavedRef.current) {
|
|
56
|
+
isExecutingSavedRef.current = true;
|
|
57
|
+
executeSavedPurchase().finally(() => {
|
|
58
|
+
isExecutingSavedRef.current = false;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
38
62
|
|
|
39
63
|
const handlePurchase = useCallback(
|
|
40
64
|
async (pkg: PurchasesPackage, source?: PurchaseSource): Promise<boolean> => {
|
|
41
|
-
|
|
42
65
|
const authProvider = authPurchaseStateManager.getProvider();
|
|
43
66
|
|
|
44
67
|
if (!authProvider) {
|
|
@@ -61,7 +84,6 @@ export const useAuthAwarePurchase = (
|
|
|
61
84
|
);
|
|
62
85
|
|
|
63
86
|
const handleRestore = useCallback(async (): Promise<boolean> => {
|
|
64
|
-
|
|
65
87
|
const authProvider = authPurchaseStateManager.getProvider();
|
|
66
88
|
|
|
67
89
|
if (!authProvider) {
|
|
@@ -78,19 +100,6 @@ export const useAuthAwarePurchase = (
|
|
|
78
100
|
return result;
|
|
79
101
|
}, [restorePurchase]);
|
|
80
102
|
|
|
81
|
-
const executeSavedPurchase = useCallback(async (): Promise<boolean> => {
|
|
82
|
-
const saved = authPurchaseStateManager.getSavedPurchase();
|
|
83
|
-
if (!saved) {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const result = await purchasePackage(saved.pkg);
|
|
88
|
-
if (result) {
|
|
89
|
-
authPurchaseStateManager.clearSavedPurchase();
|
|
90
|
-
}
|
|
91
|
-
return result;
|
|
92
|
-
}, [purchasePackage]);
|
|
93
|
-
|
|
94
103
|
return {
|
|
95
104
|
handlePurchase,
|
|
96
105
|
handleRestore,
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paywall Visibility Hook
|
|
3
|
-
* Simple global state for paywall visibility using module-level state
|
|
4
|
-
* Generic implementation for 100+ apps
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import { useCallback, useSyncExternalStore } from "react";
|
|
8
2
|
import type { PurchaseSource } from "../core/SubscriptionConstants";
|
|
9
3
|
|
|
@@ -29,9 +23,6 @@ const setPaywallState = (visible: boolean, source?: PurchaseSource): void => {
|
|
|
29
23
|
listeners.forEach((listener) => listener());
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
/**
|
|
33
|
-
* Direct paywall control for non-React services (e.g., appInitializer)
|
|
34
|
-
*/
|
|
35
26
|
export const paywallControl = {
|
|
36
27
|
open: (source?: PurchaseSource) => setPaywallState(true, source),
|
|
37
28
|
close: () => setPaywallState(false, undefined),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useQuery, useQueryClient } from "@umituz/react-native-design-system";
|
|
2
2
|
import { useEffect, useSyncExternalStore } from "react";
|
|
3
|
-
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
3
|
+
import { useAuthStore, selectUserId, selectIsAnonymous } from "@umituz/react-native-auth";
|
|
4
4
|
import { SubscriptionManager } from "../infrastructure/managers/SubscriptionManager";
|
|
5
5
|
import { initializationState } from "../infrastructure/state/initializationState";
|
|
6
6
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
7
7
|
import { SubscriptionStatusResult } from "./useSubscriptionStatus.types";
|
|
8
|
-
import {
|
|
8
|
+
import { isRegisteredUser } from "../utils/authGuards";
|
|
9
9
|
import { NO_CACHE_QUERY_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
|
|
10
10
|
import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
|
|
11
11
|
|
|
@@ -17,32 +17,30 @@ export const subscriptionStatusQueryKeys = {
|
|
|
17
17
|
|
|
18
18
|
export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
19
19
|
const userId = useAuthStore(selectUserId);
|
|
20
|
+
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
20
21
|
const queryClient = useQueryClient();
|
|
21
22
|
const isConfigured = SubscriptionManager.isConfigured();
|
|
23
|
+
const isUserRegistered = isRegisteredUser(userId, isAnonymous);
|
|
22
24
|
|
|
23
|
-
// Reactive initialization state - triggers re-render when BackgroundInitializer completes
|
|
24
25
|
const initState = useSyncExternalStore(
|
|
25
26
|
initializationState.subscribe,
|
|
26
27
|
initializationState.getSnapshot,
|
|
27
28
|
initializationState.getSnapshot,
|
|
28
29
|
);
|
|
29
30
|
|
|
30
|
-
// Check if initialized for this specific user (reactive)
|
|
31
31
|
const isInitialized = userId
|
|
32
32
|
? initState.initialized && initState.userId === userId
|
|
33
33
|
: false;
|
|
34
34
|
|
|
35
|
-
const queryEnabled =
|
|
35
|
+
const queryEnabled = isUserRegistered && isConfigured && isInitialized;
|
|
36
36
|
|
|
37
37
|
const { data, status, error, refetch } = useQuery({
|
|
38
38
|
queryKey: subscriptionStatusQueryKeys.user(userId),
|
|
39
39
|
queryFn: async () => {
|
|
40
|
-
if (!
|
|
40
|
+
if (!isUserRegistered) {
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// No side effects - just check premium status
|
|
45
|
-
// Initialization is handled by BackgroundInitializer
|
|
46
44
|
try {
|
|
47
45
|
const result = await SubscriptionManager.checkPremiumStatus();
|
|
48
46
|
return result;
|
|
@@ -54,11 +52,10 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
54
52
|
...NO_CACHE_QUERY_CONFIG,
|
|
55
53
|
});
|
|
56
54
|
|
|
57
|
-
// Clean up previous user's cache on logout/user switch
|
|
58
55
|
usePreviousUserCleanup(userId, queryClient, subscriptionStatusQueryKeys.user);
|
|
59
56
|
|
|
60
57
|
useEffect(() => {
|
|
61
|
-
if (!
|
|
58
|
+
if (!isUserRegistered) return undefined;
|
|
62
59
|
|
|
63
60
|
const unsubscribe = subscriptionEventBus.on(
|
|
64
61
|
SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED,
|
|
@@ -72,7 +69,7 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
72
69
|
);
|
|
73
70
|
|
|
74
71
|
return unsubscribe;
|
|
75
|
-
}, [userId, queryClient]);
|
|
72
|
+
}, [userId, isUserRegistered, queryClient]);
|
|
76
73
|
|
|
77
74
|
const isLoading = status === "pending";
|
|
78
75
|
|
|
@@ -4,3 +4,6 @@ export function isAuthenticated(userId: string | null | undefined): userId is st
|
|
|
4
4
|
return isDefined(userId) && userId.length > 0;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export function isRegisteredUser(userId: string | null | undefined, isAnonymous: boolean): userId is string {
|
|
8
|
+
return isAuthenticated(userId) && !isAnonymous;
|
|
9
|
+
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trial Service - Facade for device-based trial tracking
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import { arrayUnion, type FieldValue } from "firebase/firestore";
|
|
6
2
|
import { serverTimestamp } from "@umituz/react-native-firebase";
|
|
7
3
|
import { PersistentDeviceIdService } from "@umituz/react-native-design-system";
|
|
@@ -10,7 +6,6 @@ import { TrialEligibilityService } from "./TrialEligibilityService";
|
|
|
10
6
|
import type { TrialEligibilityResult } from "../core/TrialTypes";
|
|
11
7
|
export type { TrialEligibilityResult };
|
|
12
8
|
|
|
13
|
-
// Type for Firestore write operations with FieldValue
|
|
14
9
|
interface TrialRecordWrite {
|
|
15
10
|
deviceId?: string;
|
|
16
11
|
hasUsedTrial?: boolean;
|
|
@@ -26,10 +21,6 @@ const repository = new DeviceTrialRepository();
|
|
|
26
21
|
|
|
27
22
|
export const getDeviceId = () => PersistentDeviceIdService.getDeviceId();
|
|
28
23
|
|
|
29
|
-
/**
|
|
30
|
-
* Ensures a valid device ID is available
|
|
31
|
-
* Uses provided deviceId if non-empty, otherwise fetches from PersistentDeviceIdService
|
|
32
|
-
*/
|
|
33
24
|
async function ensureDeviceId(deviceId?: string): Promise<string> {
|
|
34
25
|
return (deviceId && deviceId.length > 0) ? deviceId : await getDeviceId();
|
|
35
26
|
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trial Types and Constants
|
|
3
|
-
* Device-based trial tracking types
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** Trial constants */
|
|
7
1
|
export const TRIAL_CONFIG = {
|
|
8
2
|
DURATION_DAYS: 3,
|
|
9
3
|
CREDITS: 0,
|
|
10
4
|
} as const;
|
|
11
5
|
|
|
12
|
-
/** Device trial record in Firestore */
|
|
13
6
|
export interface DeviceTrialRecord {
|
|
14
7
|
deviceId: string;
|
|
15
8
|
hasUsedTrial: boolean;
|
|
@@ -23,7 +16,6 @@ export interface DeviceTrialRecord {
|
|
|
23
16
|
updatedAt: Date;
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
/** Trial eligibility result */
|
|
27
19
|
export interface TrialEligibilityResult {
|
|
28
20
|
eligible: boolean;
|
|
29
21
|
reason?: "already_used" | "device_not_found" | "error" | "user_already_used";
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wallet Configuration
|
|
3
|
-
*
|
|
4
|
-
* Global configuration for wallet feature.
|
|
5
|
-
* Set once at app init, used by WalletScreen automatically.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import type { WalletScreenTranslations } from "../../presentation/screens/WalletScreen.types";
|
|
9
2
|
|
|
10
|
-
|
|
11
3
|
interface WalletConfiguration {
|
|
12
4
|
translations: WalletScreenTranslations;
|
|
13
5
|
transactionCollection: string;
|
|
@@ -41,9 +33,6 @@ const DEFAULT_CONFIG: WalletConfiguration = {
|
|
|
41
33
|
|
|
42
34
|
let walletConfig: WalletConfiguration = { ...DEFAULT_CONFIG };
|
|
43
35
|
|
|
44
|
-
/**
|
|
45
|
-
* Get current wallet configuration
|
|
46
|
-
*/
|
|
47
36
|
export function getWalletConfig(): WalletConfiguration {
|
|
48
37
|
return walletConfig;
|
|
49
38
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useQuery } from "@umituz/react-native-design-system";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
3
|
+
import { useAuthStore, selectUserId, selectIsAnonymous } from "@umituz/react-native-auth";
|
|
4
4
|
import { NO_CACHE_QUERY_CONFIG } from "../../../../shared/infrastructure/react-query/queryConfig";
|
|
5
5
|
import type {
|
|
6
6
|
CreditLog,
|
|
@@ -31,16 +31,19 @@ export function useTransactionHistory({
|
|
|
31
31
|
limit = 50,
|
|
32
32
|
}: UseTransactionHistoryParams): UseTransactionHistoryResult {
|
|
33
33
|
const userId = useAuthStore(selectUserId);
|
|
34
|
+
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
34
35
|
|
|
35
36
|
const repository = useMemo(
|
|
36
37
|
() => new TransactionRepository(config),
|
|
37
38
|
[config]
|
|
38
39
|
);
|
|
39
40
|
|
|
41
|
+
const isUserRegistered = !!userId && !isAnonymous;
|
|
42
|
+
|
|
40
43
|
const { data, isLoading, error, refetch } = useQuery({
|
|
41
44
|
queryKey: [...transactionQueryKeys.user(userId ?? ""), limit],
|
|
42
45
|
queryFn: async () => {
|
|
43
|
-
if (!userId) return [];
|
|
46
|
+
if (!userId || isAnonymous) return [];
|
|
44
47
|
|
|
45
48
|
const result = await repository.getTransactions({
|
|
46
49
|
userId,
|
|
@@ -53,7 +56,7 @@ export function useTransactionHistory({
|
|
|
53
56
|
|
|
54
57
|
return result.data ?? [];
|
|
55
58
|
},
|
|
56
|
-
enabled:
|
|
59
|
+
enabled: isUserRegistered,
|
|
57
60
|
...NO_CACHE_QUERY_CONFIG,
|
|
58
61
|
});
|
|
59
62
|
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useWallet Hook
|
|
3
|
-
*
|
|
4
|
-
* Orchestration hook for wallet functionality.
|
|
5
|
-
* Combines balance, transactions, and purchase state.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { useCallback, useMemo } from "react";
|
|
9
2
|
import { useCredits } from "../../../credits/presentation/useCredits";
|
|
10
3
|
import {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction Icon Mapping Utility
|
|
3
|
-
* Maps transaction reasons to their corresponding icons
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import type { TransactionReason } from "../domain/types/transaction.types";
|
|
7
2
|
|
|
8
3
|
const ICON_MAP: Record<TransactionReason, string> = {
|
|
@@ -16,11 +11,6 @@ const ICON_MAP: Record<TransactionReason, string> = {
|
|
|
16
11
|
expired: "clock",
|
|
17
12
|
};
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Get icon name for a transaction reason
|
|
21
|
-
* @param reason - Transaction reason type
|
|
22
|
-
* @returns Icon name for the transaction
|
|
23
|
-
*/
|
|
24
14
|
export function getTransactionIcon(reason: TransactionReason): string {
|
|
25
15
|
return ICON_MAP[reason] || "circle";
|
|
26
16
|
}
|
package/src/global.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global type declarations for React Native environment
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** React Native development flag */
|
|
6
1
|
declare const __DEV__: boolean;
|
|
7
2
|
|
|
8
|
-
/** Extend NodeJS namespace for React Native compatibility */
|
|
9
3
|
declare namespace NodeJS {
|
|
10
4
|
interface Global {
|
|
11
5
|
__DEV__: boolean;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React Native Subscription - Public API
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
// Domain Layer - Constants & Types
|
|
6
2
|
export * from "./domains/subscription/core/SubscriptionConstants";
|
|
7
3
|
export {
|
|
@@ -112,6 +108,7 @@ export type { PurchaseLoadingOverlayProps } from "./domains/subscription/present
|
|
|
112
108
|
// Init Module Factory
|
|
113
109
|
export {
|
|
114
110
|
createSubscriptionInitModule,
|
|
111
|
+
cleanupSubscriptionModule,
|
|
115
112
|
type SubscriptionInitModuleConfig,
|
|
116
113
|
} from './init';
|
|
117
114
|
|
|
@@ -7,6 +7,15 @@ export interface SubscriptionInitModuleConfig extends Omit<SubscriptionInitConfi
|
|
|
7
7
|
dependsOn?: string[];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
let subscriptionCleanup: (() => void) | null = null;
|
|
11
|
+
|
|
12
|
+
export function cleanupSubscriptionModule(): void {
|
|
13
|
+
if (subscriptionCleanup) {
|
|
14
|
+
subscriptionCleanup();
|
|
15
|
+
subscriptionCleanup = null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export function createSubscriptionInitModule(config: SubscriptionInitModuleConfig): InitModule {
|
|
11
20
|
const { getApiKey, critical = false, dependsOn = ['auth'], ...subscriptionConfig } = config;
|
|
12
21
|
|
|
@@ -21,9 +30,10 @@ export function createSubscriptionInitModule(config: SubscriptionInitModuleConfi
|
|
|
21
30
|
return true;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
await initializeSubscription({ apiKey, ...subscriptionConfig });
|
|
33
|
+
subscriptionCleanup = await initializeSubscription({ apiKey, ...subscriptionConfig });
|
|
25
34
|
return true;
|
|
26
|
-
} catch {
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('[SubscriptionInitModule] Initialization failed:', error instanceof Error ? error.message : String(error));
|
|
27
37
|
return false;
|
|
28
38
|
}
|
|
29
39
|
},
|
package/src/init/index.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feedback Submit Hooks
|
|
3
|
-
* React hooks for submitting feedback to Firestore
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { useCallback } from "react";
|
|
7
2
|
import { useAuth } from "@umituz/react-native-auth";
|
|
8
3
|
import {
|
|
@@ -16,9 +11,6 @@ export interface UsePaywallFeedbackSubmitOptions {
|
|
|
16
11
|
onComplete?: () => void;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Hook for submitting paywall decline feedback
|
|
21
|
-
*/
|
|
22
14
|
export function usePaywallFeedbackSubmit(
|
|
23
15
|
options: UsePaywallFeedbackSubmitOptions = {}
|
|
24
16
|
) {
|
|
@@ -63,9 +55,6 @@ export interface UseSettingsFeedbackSubmitOptions {
|
|
|
63
55
|
onError?: (error: Error) => void;
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
/**
|
|
67
|
-
* Hook for submitting general settings feedback
|
|
68
|
-
*/
|
|
69
58
|
export function useSettingsFeedbackSubmit(
|
|
70
59
|
options: UseSettingsFeedbackSubmitOptions = {}
|
|
71
60
|
) {
|