@umituz/react-native-subscription 2.26.12 → 2.26.14
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.26.
|
|
3
|
+
"version": "2.26.14",
|
|
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",
|
|
@@ -2,61 +2,62 @@
|
|
|
2
2
|
* useCreditsGate Hook
|
|
3
3
|
*
|
|
4
4
|
* Single responsibility: Credits gating
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```typescript
|
|
9
|
-
* const { requireCredits, hasCredits } = useCreditsGate({
|
|
10
|
-
* hasCredits: canAfford(cost),
|
|
11
|
-
* creditBalance: credits,
|
|
12
|
-
* requiredCredits: cost,
|
|
13
|
-
* onCreditsRequired: (required) => showPaywall(required),
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* const handleGenerate = () => {
|
|
17
|
-
* requireCredits(() => generate());
|
|
18
|
-
* };
|
|
19
|
-
* ```
|
|
5
|
+
* Uses ref pattern to avoid stale closure issues.
|
|
20
6
|
*/
|
|
21
7
|
|
|
22
|
-
import { useCallback } from "react";
|
|
8
|
+
import { useCallback, useRef, useEffect } from "react";
|
|
9
|
+
|
|
10
|
+
declare const __DEV__: boolean;
|
|
23
11
|
|
|
24
12
|
export interface UseCreditsGateParams {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
creditBalance: number;
|
|
29
|
-
/** Credits required for this action (optional, for display) */
|
|
30
|
-
requiredCredits?: number;
|
|
31
|
-
/** Callback when credits are required - receives required amount */
|
|
32
|
-
onCreditsRequired: (requiredCredits?: number) => void;
|
|
13
|
+
readonly creditBalance: number;
|
|
14
|
+
readonly requiredCredits?: number;
|
|
15
|
+
readonly onCreditsRequired: (requiredCredits?: number) => void;
|
|
33
16
|
}
|
|
34
17
|
|
|
35
18
|
export interface UseCreditsGateResult {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
creditBalance: number;
|
|
40
|
-
/** Gate action behind credits - executes if has credits, else shows paywall */
|
|
41
|
-
requireCredits: (action: () => void | Promise<void>) => boolean;
|
|
19
|
+
readonly hasCredits: boolean;
|
|
20
|
+
readonly creditBalance: number;
|
|
21
|
+
readonly requireCredits: () => boolean;
|
|
42
22
|
}
|
|
43
23
|
|
|
44
24
|
export function useCreditsGate(
|
|
45
25
|
params: UseCreditsGateParams
|
|
46
26
|
): UseCreditsGateResult {
|
|
47
|
-
const {
|
|
48
|
-
|
|
27
|
+
const { creditBalance, requiredCredits = 1, onCreditsRequired } = params;
|
|
28
|
+
|
|
29
|
+
const creditBalanceRef = useRef(creditBalance);
|
|
30
|
+
const onCreditsRequiredRef = useRef(onCreditsRequired);
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
creditBalanceRef.current = creditBalance;
|
|
34
|
+
}, [creditBalance]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
onCreditsRequiredRef.current = onCreditsRequired;
|
|
38
|
+
}, [onCreditsRequired]);
|
|
39
|
+
|
|
40
|
+
const hasCredits = creditBalance >= requiredCredits;
|
|
41
|
+
|
|
42
|
+
const requireCredits = useCallback((): boolean => {
|
|
43
|
+
const currentBalance = creditBalanceRef.current;
|
|
44
|
+
const canAfford = currentBalance >= requiredCredits;
|
|
45
|
+
|
|
46
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
47
|
+
console.log("[useCreditsGate] requireCredits", {
|
|
48
|
+
currentBalance,
|
|
49
|
+
requiredCredits,
|
|
50
|
+
canAfford,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!canAfford) {
|
|
55
|
+
onCreditsRequiredRef.current(requiredCredits);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (!hasCredits) {
|
|
53
|
-
onCreditsRequired(requiredCredits);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
return true;
|
|
57
|
-
},
|
|
58
|
-
[hasCredits, requiredCredits, onCreditsRequired]
|
|
59
|
-
);
|
|
59
|
+
return true;
|
|
60
|
+
}, [requiredCredits]);
|
|
60
61
|
|
|
61
62
|
return {
|
|
62
63
|
hasCredits,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useFeatureGate Hook
|
|
3
3
|
* Combines auth, subscription, and credits gates into a unified feature gate.
|
|
4
|
+
* Uses ref pattern to avoid stale closure issues.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { useCallback, useRef, useEffect } from "react";
|
|
@@ -10,38 +11,22 @@ import { useCreditsGate } from "./useCreditsGate";
|
|
|
10
11
|
|
|
11
12
|
declare const __DEV__: boolean;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
14
|
export interface UseFeatureGateParams {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** Whether user has enough credits for the action */
|
|
23
|
-
hasCredits: boolean;
|
|
24
|
-
/** Current credit balance */
|
|
25
|
-
creditBalance: number;
|
|
26
|
-
/** Credits required for this action (optional, for paywall display) */
|
|
27
|
-
requiredCredits?: number;
|
|
28
|
-
/** Callback to show paywall - receives required credits */
|
|
29
|
-
onShowPaywall: (requiredCredits?: number) => void;
|
|
15
|
+
readonly isAuthenticated: boolean;
|
|
16
|
+
readonly onShowAuthModal: (pendingCallback: () => void | Promise<void>) => void;
|
|
17
|
+
readonly hasSubscription?: boolean;
|
|
18
|
+
readonly creditBalance: number;
|
|
19
|
+
readonly requiredCredits?: number;
|
|
20
|
+
readonly onShowPaywall: (requiredCredits?: number) => void;
|
|
30
21
|
}
|
|
31
22
|
|
|
32
23
|
export interface UseFeatureGateResult {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/** Whether user has enough credits */
|
|
40
|
-
hasCredits: boolean;
|
|
41
|
-
/** Current credit balance */
|
|
42
|
-
creditBalance: number;
|
|
43
|
-
/** Whether feature access is allowed */
|
|
44
|
-
canAccess: boolean;
|
|
24
|
+
readonly requireFeature: (action: () => void | Promise<void>) => void;
|
|
25
|
+
readonly isAuthenticated: boolean;
|
|
26
|
+
readonly hasSubscription: boolean;
|
|
27
|
+
readonly hasCredits: boolean;
|
|
28
|
+
readonly creditBalance: number;
|
|
29
|
+
readonly canAccess: boolean;
|
|
45
30
|
}
|
|
46
31
|
|
|
47
32
|
export function useFeatureGate(
|
|
@@ -51,25 +36,22 @@ export function useFeatureGate(
|
|
|
51
36
|
isAuthenticated,
|
|
52
37
|
onShowAuthModal,
|
|
53
38
|
hasSubscription = false,
|
|
54
|
-
hasCredits,
|
|
55
39
|
creditBalance,
|
|
56
|
-
requiredCredits,
|
|
40
|
+
requiredCredits = 1,
|
|
57
41
|
onShowPaywall,
|
|
58
42
|
} = params;
|
|
59
43
|
|
|
60
|
-
// Store pending action for execution after purchase
|
|
61
44
|
const pendingActionRef = useRef<(() => void | Promise<void>) | null>(null);
|
|
62
45
|
const prevCreditBalanceRef = useRef(creditBalance);
|
|
63
46
|
const isWaitingForPurchaseRef = useRef(false);
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
const hasCreditsRef = useRef(hasCredits);
|
|
48
|
+
const creditBalanceRef = useRef(creditBalance);
|
|
67
49
|
const hasSubscriptionRef = useRef(hasSubscription);
|
|
68
50
|
const onShowPaywallRef = useRef(onShowPaywall);
|
|
69
51
|
|
|
70
52
|
useEffect(() => {
|
|
71
|
-
|
|
72
|
-
}, [
|
|
53
|
+
creditBalanceRef.current = creditBalance;
|
|
54
|
+
}, [creditBalance]);
|
|
73
55
|
|
|
74
56
|
useEffect(() => {
|
|
75
57
|
hasSubscriptionRef.current = hasSubscription;
|
|
@@ -79,11 +61,9 @@ export function useFeatureGate(
|
|
|
79
61
|
onShowPaywallRef.current = onShowPaywall;
|
|
80
62
|
}, [onShowPaywall]);
|
|
81
63
|
|
|
82
|
-
// Execute pending action when credits increase after purchase
|
|
83
64
|
useEffect(() => {
|
|
84
|
-
const prevBalance = prevCreditBalanceRef.current ?? 0;
|
|
85
|
-
const
|
|
86
|
-
const creditsIncreased = currentBalance > prevBalance;
|
|
65
|
+
const prevBalance = prevCreditBalanceRef.current ?? 0;
|
|
66
|
+
const creditsIncreased = creditBalance > prevBalance;
|
|
87
67
|
|
|
88
68
|
if (isWaitingForPurchaseRef.current && creditsIncreased && pendingActionRef.current) {
|
|
89
69
|
const action = pendingActionRef.current;
|
|
@@ -91,11 +71,7 @@ export function useFeatureGate(
|
|
|
91
71
|
isWaitingForPurchaseRef.current = false;
|
|
92
72
|
|
|
93
73
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
94
|
-
console.log("[useFeatureGate] Credits increased, executing pending action"
|
|
95
|
-
prevBalance,
|
|
96
|
-
currentBalance,
|
|
97
|
-
creditsIncreased,
|
|
98
|
-
});
|
|
74
|
+
console.log("[useFeatureGate] Credits increased, executing pending action");
|
|
99
75
|
}
|
|
100
76
|
action();
|
|
101
77
|
}
|
|
@@ -103,7 +79,6 @@ export function useFeatureGate(
|
|
|
103
79
|
prevCreditBalanceRef.current = creditBalance;
|
|
104
80
|
}, [creditBalance]);
|
|
105
81
|
|
|
106
|
-
// Compose individual gates
|
|
107
82
|
const authGate = useAuthGate({
|
|
108
83
|
isAuthenticated,
|
|
109
84
|
onAuthRequired: onShowAuthModal,
|
|
@@ -115,7 +90,6 @@ export function useFeatureGate(
|
|
|
115
90
|
});
|
|
116
91
|
|
|
117
92
|
const creditsGate = useCreditsGate({
|
|
118
|
-
hasCredits,
|
|
119
93
|
creditBalance,
|
|
120
94
|
requiredCredits,
|
|
121
95
|
onCreditsRequired: onShowPaywall,
|
|
@@ -123,85 +97,65 @@ export function useFeatureGate(
|
|
|
123
97
|
|
|
124
98
|
const requireFeature = useCallback(
|
|
125
99
|
(action: () => void | Promise<void>) => {
|
|
100
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
101
|
+
console.log("[useFeatureGate] requireFeature", {
|
|
102
|
+
isAuthenticated,
|
|
103
|
+
hasSubscription,
|
|
104
|
+
creditBalance: creditBalanceRef.current,
|
|
105
|
+
requiredCredits,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
126
108
|
|
|
127
|
-
// Step 1: Auth check
|
|
128
109
|
if (!authGate.requireAuth(() => {})) {
|
|
129
|
-
// Wrap action to re-check credits after auth succeeds
|
|
130
|
-
// Using refs to get current values when callback executes
|
|
131
110
|
const postAuthAction = () => {
|
|
132
|
-
// Subscription check (bypasses credits if subscribed)
|
|
133
111
|
if (hasSubscriptionRef.current) {
|
|
134
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
135
|
-
console.log("[useFeatureGate] User has subscription, executing action");
|
|
136
|
-
}
|
|
137
112
|
action();
|
|
138
113
|
return;
|
|
139
114
|
}
|
|
140
115
|
|
|
141
|
-
|
|
142
|
-
if (
|
|
116
|
+
const currentBalance = creditBalanceRef.current;
|
|
117
|
+
if (currentBalance < requiredCredits) {
|
|
143
118
|
pendingActionRef.current = action;
|
|
144
119
|
isWaitingForPurchaseRef.current = true;
|
|
145
120
|
onShowPaywallRef.current(requiredCredits);
|
|
146
121
|
return;
|
|
147
122
|
}
|
|
148
123
|
|
|
149
|
-
// All checks passed
|
|
150
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
151
|
-
console.log("[useFeatureGate] User has credits, executing action");
|
|
152
|
-
}
|
|
153
124
|
action();
|
|
154
125
|
};
|
|
155
126
|
onShowAuthModal(postAuthAction);
|
|
156
127
|
return;
|
|
157
128
|
}
|
|
158
129
|
|
|
159
|
-
// Step 2: Subscription check (bypasses credits if subscribed)
|
|
160
130
|
if (hasSubscription) {
|
|
161
131
|
action();
|
|
162
132
|
return;
|
|
163
133
|
}
|
|
164
134
|
|
|
165
|
-
|
|
166
|
-
if (!creditsGate.requireCredits(() => {})) {
|
|
167
|
-
// Store pending action for execution after purchase
|
|
135
|
+
if (!creditsGate.requireCredits()) {
|
|
168
136
|
pendingActionRef.current = action;
|
|
169
137
|
isWaitingForPurchaseRef.current = true;
|
|
170
138
|
return;
|
|
171
139
|
}
|
|
172
140
|
|
|
173
|
-
// Step 4: All checks passed, execute action
|
|
174
141
|
action();
|
|
175
142
|
},
|
|
176
|
-
[
|
|
177
|
-
authGate,
|
|
178
|
-
creditsGate,
|
|
179
|
-
hasSubscription,
|
|
180
|
-
requiredCredits,
|
|
181
|
-
onShowAuthModal,
|
|
182
|
-
]
|
|
143
|
+
[authGate, creditsGate, hasSubscription, requiredCredits, isAuthenticated, onShowAuthModal]
|
|
183
144
|
);
|
|
184
145
|
|
|
146
|
+
const hasCredits = creditBalance >= requiredCredits;
|
|
147
|
+
|
|
185
148
|
return {
|
|
186
149
|
requireFeature,
|
|
187
150
|
isAuthenticated: authGate.isAuthenticated,
|
|
188
151
|
hasSubscription: subscriptionGate.hasSubscription,
|
|
189
|
-
hasCredits
|
|
190
|
-
creditBalance
|
|
152
|
+
hasCredits,
|
|
153
|
+
creditBalance,
|
|
191
154
|
canAccess: isAuthenticated && (hasSubscription || hasCredits),
|
|
192
155
|
};
|
|
193
156
|
}
|
|
194
157
|
|
|
195
158
|
export { useAuthGate, useSubscriptionGate, useCreditsGate };
|
|
196
|
-
export type {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
} from "./useAuthGate";
|
|
200
|
-
export type {
|
|
201
|
-
UseSubscriptionGateParams,
|
|
202
|
-
UseSubscriptionGateResult,
|
|
203
|
-
} from "./useSubscriptionGate";
|
|
204
|
-
export type {
|
|
205
|
-
UseCreditsGateParams,
|
|
206
|
-
UseCreditsGateResult,
|
|
207
|
-
} from "./useCreditsGate";
|
|
159
|
+
export type { UseAuthGateParams, UseAuthGateResult } from "./useAuthGate";
|
|
160
|
+
export type { UseSubscriptionGateParams, UseSubscriptionGateResult } from "./useSubscriptionGate";
|
|
161
|
+
export type { UseCreditsGateParams, UseCreditsGateResult } from "./useCreditsGate";
|