@umituz/react-native-subscription 2.14.47 → 2.14.48
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 +2 -2
- package/src/domains/paywall/components/PaywallHeader.tsx +1 -0
- package/src/domains/paywall/components/PaywallModal.styles.ts +49 -0
- package/src/domains/paywall/components/PaywallModal.tsx +2 -37
- package/src/domains/wallet/domain/entities/CreditCost.ts +3 -3
- package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +11 -20
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +4 -3
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +4 -3
- package/src/domains/wallet/presentation/components/TransactionList.tsx +7 -5
- package/src/domains/wallet/presentation/hooks/useWallet.ts +6 -4
- package/src/domains/wallet/presentation/screens/WalletScreen.tsx +11 -8
- package/src/infrastructure/repositories/CreditsRepository.ts +7 -42
- package/src/infrastructure/services/CreditsInitializer.ts +1 -52
- package/src/presentation/components/feedback/PaywallFeedbackModal.tsx +1 -0
- package/src/presentation/components/sections/SubscriptionSection.tsx +4 -3
- package/src/presentation/hooks/useCreditChecker.ts +3 -2
- package/src/presentation/hooks/useCredits.ts +4 -3
- package/src/presentation/hooks/usePremium.ts +4 -3
- package/src/presentation/hooks/useSubscriptionDetails.ts +3 -2
- package/src/presentation/hooks/useSubscriptionSettingsConfig.ts +4 -3
- package/src/presentation/screens/SubscriptionDetailScreen.tsx +3 -2
- package/src/presentation/screens/components/UpgradePrompt.tsx +4 -3
- package/src/revenuecat/infrastructure/services/CustomerInfoListenerManager.ts +60 -158
- package/src/revenuecat/infrastructure/services/OfferingsFetcher.ts +13 -29
- package/src/revenuecat/infrastructure/services/PurchaseHandler.ts +64 -88
- package/src/revenuecat/infrastructure/services/RestoreHandler.ts +32 -48
- package/src/revenuecat/infrastructure/services/RevenueCatInitializer.ts +90 -219
- package/src/revenuecat/infrastructure/services/RevenueCatService.ts +121 -126
- package/src/revenuecat/infrastructure/utils/InitializationCache.ts +25 -29
- package/src/revenuecat/infrastructure/utils/PremiumStatusSyncer.ts +52 -100
- package/src/revenuecat/infrastructure/utils/UserIdProvider.ts +17 -25
- package/src/revenuecat/presentation/hooks/usePaywallFlow.ts +9 -8
|
@@ -3,41 +3,37 @@
|
|
|
3
3
|
* Manages promise caching and user state for initialization
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
export class InitializationCache {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
private initPromise: Promise<boolean> | null = null;
|
|
8
|
+
private currentUserId: string | null = null;
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
shouldReinitialize(userId: string): boolean {
|
|
11
|
+
if (!this.initPromise) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
});
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
15
|
+
if (this.currentUserId !== userId) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
getExistingPromise(): Promise<boolean> | null {
|
|
23
|
+
return this.initPromise;
|
|
24
|
+
}
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
setPromise(promise: Promise<boolean>, userId: string): void {
|
|
27
|
+
this.initPromise = promise;
|
|
28
|
+
this.currentUserId = userId;
|
|
29
|
+
}
|
|
34
30
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
getCurrentUserId(): string | null {
|
|
32
|
+
return this.currentUserId;
|
|
33
|
+
}
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
reset(): void {
|
|
36
|
+
this.initPromise = null;
|
|
37
|
+
this.currentUserId = null;
|
|
38
|
+
}
|
|
43
39
|
}
|
|
@@ -4,121 +4,73 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { CustomerInfo } from "react-native-purchases";
|
|
7
|
-
import type { RevenueCatConfig } from
|
|
8
|
-
import { getPremiumEntitlement } from
|
|
7
|
+
import type { RevenueCatConfig } from "../../domain/value-objects/RevenueCatConfig";
|
|
8
|
+
import { getPremiumEntitlement } from "../../domain/types/RevenueCatTypes";
|
|
9
9
|
import { getExpirationDate } from "./ExpirationDateCalculator";
|
|
10
10
|
|
|
11
11
|
export async function syncPremiumStatus(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
config: RevenueCatConfig,
|
|
13
|
+
userId: string,
|
|
14
|
+
customerInfo: CustomerInfo
|
|
15
15
|
): Promise<void> {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const entitlementIdentifier = config.entitlementIdentifier;
|
|
21
|
-
const premiumEntitlement = getPremiumEntitlement(
|
|
22
|
-
customerInfo,
|
|
23
|
-
entitlementIdentifier
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const isPremium = !!premiumEntitlement;
|
|
27
|
-
|
|
28
|
-
userId,
|
|
29
|
-
isPremium,
|
|
30
|
-
productId: premiumEntitlement?.productIdentifier,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
if (premiumEntitlement) {
|
|
35
|
-
const productId = premiumEntitlement.productIdentifier;
|
|
36
|
-
const expiresAt = getExpirationDate(premiumEntitlement);
|
|
37
|
-
await config.onPremiumStatusChanged(
|
|
38
|
-
userId,
|
|
39
|
-
true,
|
|
40
|
-
productId,
|
|
41
|
-
expiresAt || undefined
|
|
42
|
-
);
|
|
43
|
-
} else {
|
|
44
|
-
await config.onPremiumStatusChanged(userId, false);
|
|
16
|
+
if (!config.onPremiumStatusChanged) {
|
|
17
|
+
return;
|
|
45
18
|
}
|
|
46
19
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
52
|
-
{
|
|
53
|
-
packageName: "subscription",
|
|
54
|
-
operation: "sync_premium_status",
|
|
55
|
-
userId,
|
|
56
|
-
isPremium,
|
|
57
|
-
}
|
|
20
|
+
const entitlementIdentifier = config.entitlementIdentifier;
|
|
21
|
+
const premiumEntitlement = getPremiumEntitlement(
|
|
22
|
+
customerInfo,
|
|
23
|
+
entitlementIdentifier
|
|
58
24
|
);
|
|
59
|
-
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
if (premiumEntitlement) {
|
|
28
|
+
const productId = premiumEntitlement.productIdentifier;
|
|
29
|
+
const expiresAt = getExpirationDate(premiumEntitlement);
|
|
30
|
+
await config.onPremiumStatusChanged(
|
|
31
|
+
userId,
|
|
32
|
+
true,
|
|
33
|
+
productId,
|
|
34
|
+
expiresAt || undefined
|
|
35
|
+
);
|
|
36
|
+
} else {
|
|
37
|
+
await config.onPremiumStatusChanged(userId, false);
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// Silent error handling
|
|
41
|
+
}
|
|
60
42
|
}
|
|
61
43
|
|
|
62
44
|
export async function notifyPurchaseCompleted(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
45
|
+
config: RevenueCatConfig,
|
|
46
|
+
userId: string,
|
|
47
|
+
productId: string,
|
|
48
|
+
customerInfo: CustomerInfo
|
|
67
49
|
): Promise<void> {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
userId,
|
|
73
|
-
productId,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
await config.onPurchaseCompleted(userId, productId, customerInfo);
|
|
50
|
+
if (!config.onPurchaseCompleted) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
78
53
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
packageName: "subscription",
|
|
86
|
-
operation: "purchase_callback",
|
|
87
|
-
userId,
|
|
88
|
-
productId,
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
}
|
|
54
|
+
try {
|
|
55
|
+
await config.onPurchaseCompleted(userId, productId, customerInfo);
|
|
56
|
+
} catch {
|
|
57
|
+
// Silent error handling
|
|
58
|
+
}
|
|
92
59
|
}
|
|
93
60
|
|
|
94
61
|
export async function notifyRestoreCompleted(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
62
|
+
config: RevenueCatConfig,
|
|
63
|
+
userId: string,
|
|
64
|
+
isPremium: boolean,
|
|
65
|
+
customerInfo: CustomerInfo
|
|
99
66
|
): Promise<void> {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
userId,
|
|
105
|
-
isPremium,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
await config.onRestoreCompleted(userId, isPremium, customerInfo);
|
|
67
|
+
if (!config.onRestoreCompleted) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
110
70
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
{
|
|
117
|
-
packageName: "subscription",
|
|
118
|
-
operation: "restore_callback",
|
|
119
|
-
userId,
|
|
120
|
-
isPremium,
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
}
|
|
71
|
+
try {
|
|
72
|
+
await config.onRestoreCompleted(userId, isPremium, customerInfo);
|
|
73
|
+
} catch {
|
|
74
|
+
// Silent error handling
|
|
75
|
+
}
|
|
124
76
|
}
|
|
@@ -3,36 +3,28 @@
|
|
|
3
3
|
* Manages user ID retrieval (anonymous or authenticated)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
export class UserIdProvider {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
configure(getAnonymousUserId: () => Promise<string>): void {
|
|
12
|
-
this.getAnonymousUserIdFn = getAnonymousUserId;
|
|
13
|
-
}
|
|
7
|
+
private cachedAnonUserId: string | null = null;
|
|
8
|
+
private getAnonymousUserIdFn: (() => Promise<string>) | null = null;
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return this.cachedAnonUserId;
|
|
10
|
+
configure(getAnonymousUserId: () => Promise<string>): void {
|
|
11
|
+
this.getAnonymousUserIdFn = getAnonymousUserId;
|
|
18
12
|
}
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
});
|
|
25
|
-
throw error;
|
|
26
|
-
}
|
|
14
|
+
async getOrCreateAnonymousUserId(): Promise<string> {
|
|
15
|
+
if (this.cachedAnonUserId) {
|
|
16
|
+
return this.cachedAnonUserId;
|
|
17
|
+
}
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
if (!this.getAnonymousUserIdFn) {
|
|
20
|
+
throw new Error("Anonymous user ID provider not configured");
|
|
21
|
+
}
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
this.cachedAnonUserId = await this.getAnonymousUserIdFn();
|
|
24
|
+
return this.cachedAnonUserId;
|
|
25
|
+
}
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
reset(): void {
|
|
28
|
+
this.cachedAnonUserId = null;
|
|
29
|
+
}
|
|
38
30
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useState, useEffect, useCallback } from 'react';
|
|
7
|
-
import
|
|
7
|
+
import { useStorage } from '@umituz/react-native-storage';
|
|
8
8
|
|
|
9
9
|
const PAYWALL_SHOWN_KEY = 'post_onboarding_paywall_shown';
|
|
10
10
|
|
|
@@ -23,33 +23,34 @@ export interface UsePaywallFlowResult {
|
|
|
23
23
|
|
|
24
24
|
export const usePaywallFlow = (options: UsePaywallFlowOptions = {}): UsePaywallFlowResult => {
|
|
25
25
|
const { showAfterOnboarding = false } = options;
|
|
26
|
+
const { getString, setString } = useStorage();
|
|
26
27
|
const [showPostOnboardingPaywall, setShowPostOnboardingPaywall] = useState(showAfterOnboarding);
|
|
27
28
|
const [paywallShown, setPaywallShown] = useState(false);
|
|
28
29
|
|
|
29
30
|
// Load persisted state
|
|
30
31
|
useEffect(() => {
|
|
31
32
|
const loadPersistedState = async () => {
|
|
32
|
-
const value = await
|
|
33
|
+
const value = await getString(PAYWALL_SHOWN_KEY, '');
|
|
33
34
|
setPaywallShown(value === 'true');
|
|
34
35
|
};
|
|
35
36
|
|
|
36
37
|
loadPersistedState();
|
|
37
|
-
}, []);
|
|
38
|
+
}, [getString]);
|
|
38
39
|
|
|
39
|
-
const closePostOnboardingPaywall = useCallback(async (
|
|
40
|
-
await
|
|
40
|
+
const closePostOnboardingPaywall = useCallback(async (_isPremium: boolean) => {
|
|
41
|
+
await setString(PAYWALL_SHOWN_KEY, 'true');
|
|
41
42
|
setShowPostOnboardingPaywall(false);
|
|
42
43
|
setPaywallShown(true);
|
|
43
|
-
}, []);
|
|
44
|
+
}, [setString]);
|
|
44
45
|
|
|
45
46
|
const hidePostOnboardingPaywall = useCallback(() => {
|
|
46
47
|
setShowPostOnboardingPaywall(false);
|
|
47
48
|
}, []);
|
|
48
49
|
|
|
49
50
|
const markPaywallShown = useCallback(async () => {
|
|
50
|
-
await
|
|
51
|
+
await setString(PAYWALL_SHOWN_KEY, 'true');
|
|
51
52
|
setPaywallShown(true);
|
|
52
|
-
}, []);
|
|
53
|
+
}, [setString]);
|
|
53
54
|
|
|
54
55
|
return {
|
|
55
56
|
showPostOnboardingPaywall,
|