@umituz/react-native-subscription 2.27.3 → 2.27.4
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.27.
|
|
3
|
+
"version": "2.27.4",
|
|
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",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useCredits Hook
|
|
3
3
|
*
|
|
4
|
-
* Fetches user credits
|
|
5
|
-
*
|
|
4
|
+
* Fetches user credits with TanStack Query best practices.
|
|
5
|
+
* Uses status-based state management for reliable loading detection.
|
|
6
6
|
* Auto-initializes free credits for registered users only.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -29,10 +29,13 @@ export const creditsQueryKeys = {
|
|
|
29
29
|
|
|
30
30
|
const freeCreditsInitAttempted = new Set<string>();
|
|
31
31
|
|
|
32
|
+
export type CreditsLoadStatus = "idle" | "loading" | "initializing" | "ready" | "error";
|
|
33
|
+
|
|
32
34
|
export interface UseCreditsResult {
|
|
33
35
|
credits: UserCredits | null;
|
|
34
36
|
isLoading: boolean;
|
|
35
37
|
isCreditsLoaded: boolean;
|
|
38
|
+
loadStatus: CreditsLoadStatus;
|
|
36
39
|
error: Error | null;
|
|
37
40
|
hasCredits: boolean;
|
|
38
41
|
creditsPercent: number;
|
|
@@ -40,6 +43,18 @@ export interface UseCreditsResult {
|
|
|
40
43
|
canAfford: (cost: number) => boolean;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
function deriveLoadStatus(
|
|
47
|
+
queryStatus: "pending" | "error" | "success",
|
|
48
|
+
isInitializing: boolean,
|
|
49
|
+
queryEnabled: boolean
|
|
50
|
+
): CreditsLoadStatus {
|
|
51
|
+
if (!queryEnabled) return "idle";
|
|
52
|
+
if (queryStatus === "pending") return "loading";
|
|
53
|
+
if (queryStatus === "error") return "error";
|
|
54
|
+
if (isInitializing) return "initializing";
|
|
55
|
+
return "ready";
|
|
56
|
+
}
|
|
57
|
+
|
|
43
58
|
export const useCredits = (): UseCreditsResult => {
|
|
44
59
|
const userId = useAuthStore(selectUserId);
|
|
45
60
|
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
@@ -48,17 +63,16 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
48
63
|
|
|
49
64
|
const isConfigured = isCreditsRepositoryConfigured();
|
|
50
65
|
const config = getCreditsConfig();
|
|
51
|
-
|
|
52
66
|
const queryEnabled = !!userId && isConfigured;
|
|
53
67
|
|
|
54
|
-
const { data,
|
|
68
|
+
const { data, status, error, refetch } = useQuery({
|
|
55
69
|
queryKey: creditsQueryKeys.user(userId ?? ""),
|
|
56
70
|
queryFn: async () => {
|
|
57
|
-
if (!userId || !isConfigured)
|
|
58
|
-
|
|
59
|
-
}
|
|
71
|
+
if (!userId || !isConfigured) return null;
|
|
72
|
+
|
|
60
73
|
const repository = getCreditsRepository();
|
|
61
74
|
const result = await repository.getCredits(userId);
|
|
75
|
+
|
|
62
76
|
if (!result.success) {
|
|
63
77
|
throw new Error(result.error?.message || "Failed to fetch credits");
|
|
64
78
|
}
|
|
@@ -82,72 +96,69 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
82
96
|
});
|
|
83
97
|
|
|
84
98
|
const credits = data ?? null;
|
|
85
|
-
|
|
86
99
|
const freeCredits = config.freeCredits ?? 0;
|
|
87
100
|
const autoInit = config.autoInitializeFreeCredits !== false && freeCredits > 0;
|
|
101
|
+
const querySuccess = status === "success";
|
|
88
102
|
|
|
89
103
|
useEffect(() => {
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
const shouldInitFreeCredits =
|
|
105
|
+
querySuccess &&
|
|
92
106
|
userId &&
|
|
93
107
|
isRegisteredUser &&
|
|
94
108
|
isConfigured &&
|
|
95
109
|
!credits &&
|
|
96
110
|
autoInit &&
|
|
97
|
-
!freeCreditsInitAttempted.has(userId)
|
|
98
|
-
|
|
111
|
+
!freeCreditsInitAttempted.has(userId);
|
|
112
|
+
|
|
113
|
+
if (shouldInitFreeCredits) {
|
|
99
114
|
freeCreditsInitAttempted.add(userId);
|
|
100
115
|
setIsInitializingFreeCredits(true);
|
|
101
116
|
|
|
102
117
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
103
|
-
console.log("[useCredits] Initializing free credits
|
|
118
|
+
console.log("[useCredits] Initializing free credits:", userId.slice(0, 8));
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
const repository = getCreditsRepository();
|
|
107
122
|
repository.initializeFreeCredits(userId).then((result) => {
|
|
108
123
|
setIsInitializingFreeCredits(false);
|
|
124
|
+
|
|
109
125
|
if (result.success) {
|
|
110
126
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
111
127
|
console.log("[useCredits] Free credits initialized:", result.data?.credits);
|
|
112
128
|
}
|
|
113
129
|
refetch();
|
|
114
|
-
} else {
|
|
115
|
-
|
|
116
|
-
console.warn("[useCredits] Free credits init failed:", result.error?.message);
|
|
117
|
-
}
|
|
130
|
+
} else if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
131
|
+
console.warn("[useCredits] Free credits init failed:", result.error?.message);
|
|
118
132
|
}
|
|
119
133
|
});
|
|
120
|
-
} else if (
|
|
134
|
+
} else if (querySuccess && userId && isAnonymous && !credits && autoInit) {
|
|
121
135
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
122
136
|
console.log("[useCredits] Skipping free credits - anonymous user must register first");
|
|
123
137
|
}
|
|
124
138
|
}
|
|
125
|
-
}, [
|
|
139
|
+
}, [querySuccess, userId, isRegisteredUser, isAnonymous, isConfigured, credits, autoInit, refetch]);
|
|
126
140
|
|
|
127
141
|
const derivedValues = useMemo(() => {
|
|
128
142
|
const has = (credits?.credits ?? 0) > 0;
|
|
129
|
-
const percent = credits
|
|
130
|
-
? Math.round((credits.credits / config.creditLimit) * 100)
|
|
131
|
-
: 0;
|
|
132
|
-
|
|
143
|
+
const percent = credits ? Math.round((credits.credits / config.creditLimit) * 100) : 0;
|
|
133
144
|
return { hasCredits: has, creditsPercent: percent };
|
|
134
145
|
}, [credits, config.creditLimit]);
|
|
135
146
|
|
|
136
147
|
const canAfford = useCallback(
|
|
137
|
-
(cost: number): boolean =>
|
|
138
|
-
if (!credits) return false;
|
|
139
|
-
return credits.credits >= cost;
|
|
140
|
-
},
|
|
148
|
+
(cost: number): boolean => (credits?.credits ?? 0) >= cost,
|
|
141
149
|
[credits]
|
|
142
150
|
);
|
|
143
151
|
|
|
144
|
-
const
|
|
152
|
+
const loadStatus = deriveLoadStatus(status, isInitializingFreeCredits, queryEnabled);
|
|
153
|
+
const isCreditsLoaded = loadStatus === "ready";
|
|
154
|
+
const isLoading = loadStatus === "loading" || loadStatus === "initializing";
|
|
145
155
|
|
|
146
156
|
return {
|
|
147
157
|
credits,
|
|
148
158
|
isLoading,
|
|
149
159
|
isCreditsLoaded,
|
|
150
|
-
|
|
160
|
+
loadStatus,
|
|
161
|
+
error: error instanceof Error ? error : null,
|
|
151
162
|
hasCredits: derivedValues.hasCredits,
|
|
152
163
|
creditsPercent: derivedValues.creditsPercent,
|
|
153
164
|
refetch,
|