@umituz/react-native-subscription 2.41.13 → 2.43.0
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/paywall/components/PaywallScreen.tsx +15 -26
- package/src/domains/paywall/components/PaywallScreen.types.ts +1 -2
- package/src/domains/paywall/hooks/usePaywallActions.ts +43 -63
- package/src/domains/paywall/hooks/usePaywallOrchestrator.ts +14 -21
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.tsx +8 -10
- package/src/domains/subscription/presentation/usePremium.ts +8 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.43.0",
|
|
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",
|
|
@@ -33,8 +33,14 @@ import { hasItems } from "../../../shared/utils/arrayUtils";
|
|
|
33
33
|
export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) => {
|
|
34
34
|
const navigation = useNavigation();
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if (__DEV__) {
|
|
37
|
+
console.log('[PaywallScreen] 📱 Rendering PaywallScreen', {
|
|
38
|
+
hasPackages: !!props.packages?.length,
|
|
39
|
+
packagesCount: props.packages?.length || 0,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { purchasePackage, restorePurchase } = usePremium();
|
|
38
44
|
|
|
39
45
|
const {
|
|
40
46
|
onClose,
|
|
@@ -46,8 +52,6 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
46
52
|
creditAmounts,
|
|
47
53
|
creditsLabel,
|
|
48
54
|
heroImage,
|
|
49
|
-
onPurchase,
|
|
50
|
-
onRestore,
|
|
51
55
|
onPurchaseSuccess,
|
|
52
56
|
onPurchaseError,
|
|
53
57
|
onAuthRequired,
|
|
@@ -58,23 +62,8 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
58
62
|
const tokens = useAppDesignTokens();
|
|
59
63
|
const insets = useSafeAreaInsets();
|
|
60
64
|
|
|
61
|
-
// Use provided purchase functions or fall back to usePremium
|
|
62
|
-
const handlePurchasePackage = useCallback(async (pkg: any) => {
|
|
63
|
-
if (onPurchase) {
|
|
64
|
-
return await onPurchase(pkg);
|
|
65
|
-
}
|
|
66
|
-
return await premiumPurchase(pkg);
|
|
67
|
-
}, [onPurchase, premiumPurchase]);
|
|
68
|
-
|
|
69
|
-
const handleRestorePackages = useCallback(async () => {
|
|
70
|
-
if (onRestore) {
|
|
71
|
-
return await onRestore();
|
|
72
|
-
}
|
|
73
|
-
return await premiumRestore();
|
|
74
|
-
}, [onRestore, premiumRestore]);
|
|
75
|
-
|
|
76
|
-
// Default close handler using navigation
|
|
77
65
|
const handleClose = useCallback(() => {
|
|
66
|
+
if (__DEV__) console.log('[PaywallScreen] 🔙 Closing paywall');
|
|
78
67
|
if (onClose) {
|
|
79
68
|
onClose();
|
|
80
69
|
} else if (navigation.canGoBack()) {
|
|
@@ -91,8 +80,8 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
91
80
|
resetState
|
|
92
81
|
} = usePaywallActions({
|
|
93
82
|
packages,
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
purchasePackage,
|
|
84
|
+
restorePurchase,
|
|
96
85
|
source,
|
|
97
86
|
onPurchaseSuccess,
|
|
98
87
|
onPurchaseError,
|
|
@@ -100,12 +89,12 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
100
89
|
onClose: handleClose
|
|
101
90
|
});
|
|
102
91
|
|
|
103
|
-
// Reset state when screen is closed to avoid lockups
|
|
104
92
|
useEffect(() => {
|
|
105
93
|
return () => {
|
|
94
|
+
if (__DEV__) console.log('[PaywallScreen] 🧹 Cleanup: resetting state');
|
|
106
95
|
resetState();
|
|
107
96
|
};
|
|
108
|
-
}, [resetState
|
|
97
|
+
}, [resetState]);
|
|
109
98
|
|
|
110
99
|
// Auto-select first package
|
|
111
100
|
useEffect(() => {
|
|
@@ -228,7 +217,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
228
217
|
|
|
229
218
|
|
|
230
219
|
// Performance Optimization: getItemLayout for FlatList
|
|
231
|
-
const getItemLayout = useCallback((_data:
|
|
220
|
+
const getItemLayout = useCallback((_data: ArrayLike<PaywallListItem> | null, index: number) => {
|
|
232
221
|
return calculatePaywallItemLayout(flatData, index);
|
|
233
222
|
}, [flatData]);
|
|
234
223
|
|
|
@@ -322,7 +311,7 @@ export const PaywallScreen: React.FC<PaywallScreenProps> = React.memo((props) =>
|
|
|
322
311
|
translations={translations}
|
|
323
312
|
legalUrls={legalUrls}
|
|
324
313
|
isProcessing={isProcessing}
|
|
325
|
-
onRestore={
|
|
314
|
+
onRestore={handleRestore}
|
|
326
315
|
onLegalClick={handleLegalUrl}
|
|
327
316
|
/>
|
|
328
317
|
</View>
|
|
@@ -13,8 +13,7 @@ export interface PaywallScreenProps {
|
|
|
13
13
|
creditAmounts?: Record<string, number>;
|
|
14
14
|
creditsLabel?: string;
|
|
15
15
|
heroImage?: ImageSourcePropType;
|
|
16
|
-
|
|
17
|
-
onRestore?: () => Promise<void | boolean>;
|
|
16
|
+
// Purchase/restore handlers removed - always use usePremium hook internally
|
|
18
17
|
onPurchaseSuccess?: () => void;
|
|
19
18
|
onPurchaseError?: (error: Error | string) => void;
|
|
20
19
|
onAuthRequired?: () => void;
|
|
@@ -7,8 +7,8 @@ import { useCredits } from "../../credits/presentation/useCredits";
|
|
|
7
7
|
|
|
8
8
|
interface UsePaywallActionsParams {
|
|
9
9
|
packages?: PurchasesPackage[];
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
purchasePackage: (pkg: PurchasesPackage) => Promise<boolean>;
|
|
11
|
+
restorePurchase: () => Promise<boolean>;
|
|
12
12
|
source?: PurchaseSource;
|
|
13
13
|
onPurchaseSuccess?: () => void;
|
|
14
14
|
onPurchaseError?: (error: Error | string) => void;
|
|
@@ -18,8 +18,8 @@ interface UsePaywallActionsParams {
|
|
|
18
18
|
|
|
19
19
|
export function usePaywallActions({
|
|
20
20
|
packages = [],
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
purchasePackage,
|
|
22
|
+
restorePurchase,
|
|
23
23
|
onPurchaseSuccess,
|
|
24
24
|
onPurchaseError,
|
|
25
25
|
onAuthRequired,
|
|
@@ -28,33 +28,26 @@ export function usePaywallActions({
|
|
|
28
28
|
const [selectedPlanId, setSelectedPlanId] = useState<string | null>(null);
|
|
29
29
|
const [isLocalProcessing, setIsLocalProcessing] = useState(false);
|
|
30
30
|
|
|
31
|
-
// Use optimized selector for global purchasing state
|
|
32
31
|
const isGlobalPurchasing = usePurchaseLoadingStore(selectIsPurchasing);
|
|
33
|
-
|
|
34
|
-
// Get premium and credits status for fallback success checks
|
|
35
32
|
const { refetch: refetchStatus } = useSubscriptionStatus();
|
|
36
33
|
const { refetch: refetchCredits } = useCredits();
|
|
37
|
-
|
|
38
|
-
// Combine processing states
|
|
34
|
+
|
|
39
35
|
const isProcessing = isLocalProcessing || isGlobalPurchasing;
|
|
40
|
-
|
|
41
|
-
// Use ref for isProcessing to keep callbacks stable without re-creating them
|
|
42
36
|
const isProcessingRef = useRef(isProcessing);
|
|
43
37
|
isProcessingRef.current = isProcessing;
|
|
44
38
|
|
|
45
39
|
const { startPurchase, endPurchase } = usePurchaseLoadingStore();
|
|
46
40
|
|
|
47
|
-
const
|
|
48
|
-
const
|
|
41
|
+
const purchasePackageRef = useRef(purchasePackage);
|
|
42
|
+
const restorePurchaseRef = useRef(restorePurchase);
|
|
49
43
|
const onPurchaseSuccessRef = useRef(onPurchaseSuccess);
|
|
50
44
|
const onPurchaseErrorRef = useRef(onPurchaseError);
|
|
51
45
|
const onAuthRequiredRef = useRef(onAuthRequired);
|
|
52
46
|
const onCloseRef = useRef(onClose);
|
|
53
47
|
const packagesRef = useRef(packages);
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
onRestoreRef.current = onRestore;
|
|
49
|
+
purchasePackageRef.current = purchasePackage;
|
|
50
|
+
restorePurchaseRef.current = restorePurchase;
|
|
58
51
|
onPurchaseSuccessRef.current = onPurchaseSuccess;
|
|
59
52
|
onPurchaseErrorRef.current = onPurchaseError;
|
|
60
53
|
onAuthRequiredRef.current = onAuthRequired;
|
|
@@ -62,29 +55,20 @@ export function usePaywallActions({
|
|
|
62
55
|
packagesRef.current = packages;
|
|
63
56
|
|
|
64
57
|
const handlePurchase = useCallback(async () => {
|
|
65
|
-
// Access current state via refs to keep callback stable
|
|
66
58
|
const currentSelectedId = selectedPlanId;
|
|
67
59
|
if (!currentSelectedId) return;
|
|
68
60
|
|
|
69
|
-
if (!onPurchaseRef.current) {
|
|
70
|
-
onPurchaseErrorRef.current?.(new Error("Purchase handler not configured"));
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
61
|
if (isProcessingRef.current) return;
|
|
75
62
|
|
|
76
63
|
const pkg = packagesRef.current.find((p) => p.product.identifier === currentSelectedId);
|
|
77
|
-
|
|
78
64
|
if (!pkg) {
|
|
79
65
|
onPurchaseErrorRef.current?.(new Error(`Package not found: ${currentSelectedId}`));
|
|
80
66
|
return;
|
|
81
67
|
}
|
|
82
68
|
|
|
83
69
|
if (__DEV__) {
|
|
84
|
-
console.log('[usePaywallActions] Starting purchase', {
|
|
70
|
+
console.log('[usePaywallActions] 🛒 Starting purchase', {
|
|
85
71
|
productId: pkg.product.identifier,
|
|
86
|
-
hasOnClose: !!onCloseRef.current,
|
|
87
|
-
hasOnSuccess: !!onPurchaseSuccessRef.current,
|
|
88
72
|
});
|
|
89
73
|
}
|
|
90
74
|
|
|
@@ -92,87 +76,77 @@ export function usePaywallActions({
|
|
|
92
76
|
startPurchase(currentSelectedId, "manual");
|
|
93
77
|
|
|
94
78
|
try {
|
|
95
|
-
const success = await
|
|
79
|
+
const success = await purchasePackageRef.current(pkg);
|
|
80
|
+
|
|
96
81
|
if (__DEV__) {
|
|
97
|
-
console.log('[usePaywallActions] Purchase completed', { success });
|
|
82
|
+
console.log('[usePaywallActions] ✅ Purchase completed', { success });
|
|
98
83
|
}
|
|
99
84
|
|
|
100
|
-
// Check if purchase was successful
|
|
101
|
-
// We consider it success if:
|
|
102
|
-
// 1. Handler returns true
|
|
103
|
-
// 2. Handler returns void (undefined) but user is now premium (fallback for misconfigured handlers)
|
|
104
85
|
let isActuallySuccessful = success === true;
|
|
105
|
-
|
|
86
|
+
|
|
106
87
|
if (success === undefined) {
|
|
107
88
|
if (__DEV__) {
|
|
108
|
-
console.log('[usePaywallActions]
|
|
89
|
+
console.log('[usePaywallActions] 🔍 Checking premium status as fallback...');
|
|
109
90
|
}
|
|
110
|
-
|
|
111
|
-
// Use Promise.all to fetch both in parallel
|
|
91
|
+
|
|
112
92
|
const [statusResult, creditsResult] = await Promise.all([
|
|
113
93
|
refetchStatus(),
|
|
114
94
|
refetchCredits()
|
|
115
95
|
]);
|
|
116
|
-
|
|
96
|
+
|
|
117
97
|
const isSubscriptionPremium = statusResult.data?.isPremium ?? false;
|
|
118
98
|
const isCreditsPremium = creditsResult.data?.isPremium ?? false;
|
|
119
|
-
|
|
99
|
+
|
|
120
100
|
isActuallySuccessful = isSubscriptionPremium || isCreditsPremium;
|
|
121
|
-
|
|
101
|
+
|
|
122
102
|
if (__DEV__) {
|
|
123
|
-
console.log('[usePaywallActions] Fallback check result:', {
|
|
124
|
-
isSubscriptionPremium,
|
|
125
|
-
isCreditsPremium,
|
|
126
|
-
isActuallySuccessful
|
|
103
|
+
console.log('[usePaywallActions] 📊 Fallback check result:', {
|
|
104
|
+
isSubscriptionPremium,
|
|
105
|
+
isCreditsPremium,
|
|
106
|
+
isActuallySuccessful
|
|
127
107
|
});
|
|
128
108
|
}
|
|
129
109
|
}
|
|
130
110
|
|
|
131
111
|
if (isActuallySuccessful) {
|
|
132
112
|
if (__DEV__) {
|
|
133
|
-
console.log('[usePaywallActions] Purchase successful,
|
|
113
|
+
console.log('[usePaywallActions] 🎉 Purchase successful, closing paywall');
|
|
134
114
|
}
|
|
135
115
|
onPurchaseSuccessRef.current?.();
|
|
136
|
-
// Always close paywall on successful purchase
|
|
137
116
|
onCloseRef.current?.();
|
|
138
117
|
} else {
|
|
139
118
|
if (__DEV__) {
|
|
140
|
-
console.warn('[usePaywallActions] Purchase did not indicate success
|
|
119
|
+
console.warn('[usePaywallActions] ⚠️ Purchase did not indicate success');
|
|
141
120
|
}
|
|
142
121
|
}
|
|
143
122
|
} catch (error) {
|
|
123
|
+
if (__DEV__) {
|
|
124
|
+
console.error('[usePaywallActions] ❌ Purchase error:', error);
|
|
125
|
+
}
|
|
144
126
|
onPurchaseErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
145
127
|
} finally {
|
|
146
128
|
setIsLocalProcessing(false);
|
|
147
129
|
endPurchase(currentSelectedId);
|
|
148
130
|
}
|
|
149
|
-
}, [selectedPlanId, startPurchase, endPurchase, refetchStatus, refetchCredits]);
|
|
131
|
+
}, [selectedPlanId, startPurchase, endPurchase, refetchStatus, refetchCredits]);
|
|
150
132
|
|
|
151
133
|
const handleRestore = useCallback(async () => {
|
|
152
|
-
if (!onRestoreRef.current) {
|
|
153
|
-
onPurchaseErrorRef.current?.(new Error("Restore handler not configured"));
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
134
|
if (isProcessingRef.current) return;
|
|
158
135
|
|
|
159
136
|
if (__DEV__) {
|
|
160
|
-
console.log('[usePaywallActions] Starting restore'
|
|
161
|
-
hasOnClose: !!onCloseRef.current,
|
|
162
|
-
hasOnSuccess: !!onPurchaseSuccessRef.current,
|
|
163
|
-
});
|
|
137
|
+
console.log('[usePaywallActions] 🔄 Starting restore');
|
|
164
138
|
}
|
|
165
139
|
|
|
166
140
|
setIsLocalProcessing(true);
|
|
167
141
|
try {
|
|
168
|
-
const success = await
|
|
142
|
+
const success = await restorePurchaseRef.current();
|
|
143
|
+
|
|
169
144
|
if (__DEV__) {
|
|
170
|
-
console.log('[usePaywallActions] Restore completed', { success });
|
|
145
|
+
console.log('[usePaywallActions] ✅ Restore completed', { success });
|
|
171
146
|
}
|
|
172
147
|
|
|
173
|
-
// Check success with same fallback as purchase
|
|
174
148
|
let isActuallySuccessful = success === true;
|
|
175
|
-
|
|
149
|
+
|
|
176
150
|
if (success === undefined) {
|
|
177
151
|
const [statusResult, creditsResult] = await Promise.all([
|
|
178
152
|
refetchStatus(),
|
|
@@ -183,23 +157,29 @@ export function usePaywallActions({
|
|
|
183
157
|
|
|
184
158
|
if (isActuallySuccessful) {
|
|
185
159
|
if (__DEV__) {
|
|
186
|
-
console.log('[usePaywallActions] Restore successful,
|
|
160
|
+
console.log('[usePaywallActions] 🎉 Restore successful, closing paywall');
|
|
187
161
|
}
|
|
188
162
|
onPurchaseSuccessRef.current?.();
|
|
189
163
|
onCloseRef.current?.();
|
|
190
164
|
} else {
|
|
191
165
|
if (__DEV__) {
|
|
192
|
-
console.warn('[usePaywallActions] Restore did not indicate success
|
|
166
|
+
console.warn('[usePaywallActions] ⚠️ Restore did not indicate success');
|
|
193
167
|
}
|
|
194
168
|
}
|
|
195
169
|
} catch (error) {
|
|
170
|
+
if (__DEV__) {
|
|
171
|
+
console.error('[usePaywallActions] ❌ Restore error:', error);
|
|
172
|
+
}
|
|
196
173
|
onPurchaseErrorRef.current?.(error instanceof Error ? error : new Error(String(error)));
|
|
197
174
|
} finally {
|
|
198
175
|
setIsLocalProcessing(false);
|
|
199
176
|
}
|
|
200
|
-
}, [refetchStatus, refetchCredits]);
|
|
177
|
+
}, [refetchStatus, refetchCredits]);
|
|
201
178
|
|
|
202
179
|
const resetState = useCallback(() => {
|
|
180
|
+
if (__DEV__) {
|
|
181
|
+
console.log('[usePaywallActions] 🧹 Resetting state');
|
|
182
|
+
}
|
|
203
183
|
setSelectedPlanId(null);
|
|
204
184
|
setIsLocalProcessing(false);
|
|
205
185
|
}, []);
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { useEffect,
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import type { NavigationProp } from "@react-navigation/native";
|
|
3
|
+
import type { ImageSourcePropType } from "react-native";
|
|
2
4
|
import { usePremium } from "../../subscription/presentation/usePremium";
|
|
3
5
|
import { useSubscriptionFlowStore } from "../../subscription/presentation/useSubscriptionFlow";
|
|
4
6
|
import { usePaywallVisibility } from "../../subscription/presentation/usePaywallVisibility";
|
|
5
7
|
import { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../entities/types";
|
|
6
8
|
|
|
7
9
|
export interface PaywallOrchestratorOptions {
|
|
8
|
-
navigation: any
|
|
10
|
+
navigation: NavigationProp<any>;
|
|
9
11
|
translations: PaywallTranslations;
|
|
10
12
|
features: SubscriptionFeature[];
|
|
11
13
|
legalUrls: PaywallLegalUrls;
|
|
12
|
-
heroImage:
|
|
14
|
+
heroImage: ImageSourcePropType;
|
|
13
15
|
isNavReady?: boolean;
|
|
14
16
|
isLocalizationReady?: boolean;
|
|
15
|
-
onAuthRequired?: () => void;
|
|
16
|
-
onPurchaseSuccess?: () => void;
|
|
17
17
|
bestValueIdentifier?: string;
|
|
18
18
|
creditsLabel?: string;
|
|
19
19
|
}
|
|
@@ -31,16 +31,15 @@ export function usePaywallOrchestrator({
|
|
|
31
31
|
heroImage,
|
|
32
32
|
isNavReady = true,
|
|
33
33
|
isLocalizationReady = true,
|
|
34
|
-
onAuthRequired,
|
|
35
|
-
onPurchaseSuccess,
|
|
36
34
|
bestValueIdentifier = "yearly",
|
|
37
35
|
creditsLabel,
|
|
38
36
|
}: PaywallOrchestratorOptions) {
|
|
37
|
+
// Sadece isPremium ve packages alıyoruz
|
|
38
|
+
// purchasePackage ve restoreRestore fonksiyonlarını ALMIYORUZ
|
|
39
|
+
// Çünkü PaywallScreen zaten usePremium hook'unu doğrudan kullanıyor
|
|
39
40
|
const {
|
|
40
41
|
isPremium,
|
|
41
|
-
packages
|
|
42
|
-
purchasePackage,
|
|
43
|
-
restorePurchase
|
|
42
|
+
packages
|
|
44
43
|
} = usePremium();
|
|
45
44
|
|
|
46
45
|
// Selectors for stable references and fine-grained updates
|
|
@@ -54,7 +53,6 @@ export function usePaywallOrchestrator({
|
|
|
54
53
|
const setShowFeedback = useSubscriptionFlowStore((state) => state.setShowFeedback);
|
|
55
54
|
|
|
56
55
|
const { showPaywall, closePaywall } = usePaywallVisibility();
|
|
57
|
-
const purchasedRef = useRef(false);
|
|
58
56
|
const hasNavigatedRef = useRef(false);
|
|
59
57
|
|
|
60
58
|
useEffect(() => {
|
|
@@ -74,10 +72,13 @@ export function usePaywallOrchestrator({
|
|
|
74
72
|
if (hasNavigatedRef.current) return;
|
|
75
73
|
hasNavigatedRef.current = true;
|
|
76
74
|
|
|
77
|
-
if (__DEV__) console.log('[usePaywallOrchestrator] 🚀 Navigating to Paywall', {
|
|
78
|
-
source: shouldShowPostOnboarding ? "onboarding" : "manual"
|
|
75
|
+
if (__DEV__) console.log('[usePaywallOrchestrator] 🚀 Navigating to Paywall', {
|
|
76
|
+
source: shouldShowPostOnboarding ? "onboarding" : "manual",
|
|
77
|
+
packagesCount: packages.length
|
|
79
78
|
});
|
|
80
79
|
|
|
80
|
+
// SADECE DATA geçiyoruz - FONKSİYON YOK
|
|
81
|
+
// PaywallScreen kendi usePremium hook'unu kullanacak
|
|
81
82
|
navigation.navigate("PaywallScreen", {
|
|
82
83
|
translations,
|
|
83
84
|
legalUrls,
|
|
@@ -87,10 +88,6 @@ export function usePaywallOrchestrator({
|
|
|
87
88
|
heroImage,
|
|
88
89
|
source: shouldShowPostOnboarding ? "onboarding" : "manual",
|
|
89
90
|
packages,
|
|
90
|
-
onPurchase: purchasePackage,
|
|
91
|
-
onRestore: restorePurchase,
|
|
92
|
-
onPurchaseSuccess,
|
|
93
|
-
onAuthRequired,
|
|
94
91
|
});
|
|
95
92
|
|
|
96
93
|
if (shouldShowPostOnboarding) {
|
|
@@ -119,14 +116,10 @@ export function usePaywallOrchestrator({
|
|
|
119
116
|
features,
|
|
120
117
|
heroImage,
|
|
121
118
|
packages,
|
|
122
|
-
purchasePackage,
|
|
123
|
-
restorePurchase,
|
|
124
119
|
markPaywallShown,
|
|
125
120
|
closePaywall,
|
|
126
121
|
bestValueIdentifier,
|
|
127
122
|
creditsLabel,
|
|
128
|
-
onPurchaseSuccess,
|
|
129
|
-
onAuthRequired,
|
|
130
123
|
]);
|
|
131
124
|
|
|
132
125
|
const completeOnboarding = useSubscriptionFlowStore((state) => state.completeOnboarding);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
+
import type { NavigationProp } from "@react-navigation/native";
|
|
3
|
+
import type { ImageSourcePropType } from "react-native";
|
|
2
4
|
import { SplashScreen, useSplashFlow } from "@umituz/react-native-design-system/molecules";
|
|
3
5
|
import { OnboardingScreen } from "@umituz/react-native-design-system/onboarding";
|
|
4
6
|
import { OfflineBanner } from "@umituz/react-native-design-system/offline";
|
|
@@ -11,7 +13,7 @@ import { usePaywallFeedbackSubmit } from "../../../../presentation/hooks/feedbac
|
|
|
11
13
|
|
|
12
14
|
export interface ManagedSubscriptionFlowProps {
|
|
13
15
|
children: React.ReactNode;
|
|
14
|
-
navigation: any
|
|
16
|
+
navigation: NavigationProp<any>;
|
|
15
17
|
islocalizationReady: boolean;
|
|
16
18
|
|
|
17
19
|
// Splash Configuration
|
|
@@ -40,11 +42,9 @@ export interface ManagedSubscriptionFlowProps {
|
|
|
40
42
|
translations: PaywallTranslations;
|
|
41
43
|
features: SubscriptionFeature[];
|
|
42
44
|
legalUrls: PaywallLegalUrls;
|
|
43
|
-
heroImage:
|
|
45
|
+
heroImage: ImageSourcePropType;
|
|
44
46
|
bestValueIdentifier?: string;
|
|
45
47
|
creditsLabel?: string;
|
|
46
|
-
onAuthRequired?: () => void;
|
|
47
|
-
onPurchaseSuccess?: () => void;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
// Feedback Configuration
|
|
@@ -53,7 +53,7 @@ export interface ManagedSubscriptionFlowProps {
|
|
|
53
53
|
onSubmit?: (data: { reason: string; otherText?: string }) => void | Promise<void>;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
-
// Offline Configuration
|
|
56
|
+
// Offline Configuration (optional)
|
|
57
57
|
offline?: {
|
|
58
58
|
isOffline: boolean;
|
|
59
59
|
message: string;
|
|
@@ -102,10 +102,10 @@ const ManagedSubscriptionFlowInner = React.memo<ManagedSubscriptionFlowProps>(({
|
|
|
102
102
|
}
|
|
103
103
|
}, [isSplashComplete, islocalizationReady]);
|
|
104
104
|
|
|
105
|
-
const {
|
|
106
|
-
flowState,
|
|
105
|
+
const {
|
|
106
|
+
flowState,
|
|
107
107
|
setShowFeedback,
|
|
108
|
-
completeOnboarding
|
|
108
|
+
completeOnboarding
|
|
109
109
|
} = usePaywallOrchestrator({
|
|
110
110
|
navigation,
|
|
111
111
|
isNavReady,
|
|
@@ -114,8 +114,6 @@ const ManagedSubscriptionFlowInner = React.memo<ManagedSubscriptionFlowProps>(({
|
|
|
114
114
|
features: paywall.features,
|
|
115
115
|
legalUrls: paywall.legalUrls,
|
|
116
116
|
heroImage: paywall.heroImage,
|
|
117
|
-
onAuthRequired: paywall.onAuthRequired,
|
|
118
|
-
onPurchaseSuccess: paywall.onPurchaseSuccess,
|
|
119
117
|
bestValueIdentifier: paywall.bestValueIdentifier,
|
|
120
118
|
creditsLabel: paywall.creditsLabel,
|
|
121
119
|
});
|
|
@@ -14,7 +14,6 @@ import { UsePremiumResult } from './usePremium.types';
|
|
|
14
14
|
const EMPTY_PACKAGES: PurchasesPackage[] = [];
|
|
15
15
|
|
|
16
16
|
export const usePremium = (): UsePremiumResult => {
|
|
17
|
-
|
|
18
17
|
const { isPremium: subscriptionActive, isLoading: statusLoading } = useSubscriptionStatus();
|
|
19
18
|
const { credits, isLoading: creditsLoading } = useCredits();
|
|
20
19
|
|
|
@@ -38,7 +37,10 @@ export const usePremium = (): UsePremiumResult => {
|
|
|
38
37
|
try {
|
|
39
38
|
const result = await purchaseMutation.mutateAsync(pkg);
|
|
40
39
|
return result.success;
|
|
41
|
-
} catch {
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (__DEV__) {
|
|
42
|
+
console.error('[usePremium] Purchase failed:', error);
|
|
43
|
+
}
|
|
42
44
|
return false;
|
|
43
45
|
}
|
|
44
46
|
},
|
|
@@ -49,7 +51,10 @@ export const usePremium = (): UsePremiumResult => {
|
|
|
49
51
|
try {
|
|
50
52
|
const result = await restoreMutation.mutateAsync();
|
|
51
53
|
return result.success;
|
|
52
|
-
} catch {
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (__DEV__) {
|
|
56
|
+
console.error('[usePremium] Restore failed:', error);
|
|
57
|
+
}
|
|
53
58
|
return false;
|
|
54
59
|
}
|
|
55
60
|
}, [restoreMutation]);
|