@umituz/react-native-subscription 2.27.4 → 2.27.7
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.7",
|
|
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",
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Fetches user credits with TanStack Query best practices.
|
|
5
5
|
* Uses status-based state management for reliable loading detection.
|
|
6
|
-
*
|
|
6
|
+
* Free credits initialization is delegated to useFreeCreditsInit hook.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { useQuery } from "@umituz/react-native-design-system";
|
|
10
|
-
import { useCallback, useMemo
|
|
10
|
+
import { useCallback, useMemo } from "react";
|
|
11
11
|
import {
|
|
12
12
|
useAuthStore,
|
|
13
13
|
selectUserId,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getCreditsConfig,
|
|
20
20
|
isCreditsRepositoryConfigured,
|
|
21
21
|
} from "../../infrastructure/repositories/CreditsRepositoryProvider";
|
|
22
|
+
import { useFreeCreditsInit } from "./useFreeCreditsInit";
|
|
22
23
|
|
|
23
24
|
declare const __DEV__: boolean;
|
|
24
25
|
|
|
@@ -27,8 +28,6 @@ export const creditsQueryKeys = {
|
|
|
27
28
|
user: (userId: string) => ["credits", userId] as const,
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
const freeCreditsInitAttempted = new Set<string>();
|
|
31
|
-
|
|
32
31
|
export type CreditsLoadStatus = "idle" | "loading" | "initializing" | "ready" | "error";
|
|
33
32
|
|
|
34
33
|
export interface UseCreditsResult {
|
|
@@ -59,7 +58,6 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
59
58
|
const userId = useAuthStore(selectUserId);
|
|
60
59
|
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
61
60
|
const isRegisteredUser = !!userId && !isAnonymous;
|
|
62
|
-
const [isInitializingFreeCredits, setIsInitializingFreeCredits] = useState(false);
|
|
63
61
|
|
|
64
62
|
const isConfigured = isCreditsRepositoryConfigured();
|
|
65
63
|
const config = getCreditsConfig();
|
|
@@ -96,47 +94,18 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
96
94
|
});
|
|
97
95
|
|
|
98
96
|
const credits = data ?? null;
|
|
99
|
-
const freeCredits = config.freeCredits ?? 0;
|
|
100
|
-
const autoInit = config.autoInitializeFreeCredits !== false && freeCredits > 0;
|
|
101
97
|
const querySuccess = status === "success";
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (shouldInitFreeCredits) {
|
|
114
|
-
freeCreditsInitAttempted.add(userId);
|
|
115
|
-
setIsInitializingFreeCredits(true);
|
|
116
|
-
|
|
117
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
118
|
-
console.log("[useCredits] Initializing free credits:", userId.slice(0, 8));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const repository = getCreditsRepository();
|
|
122
|
-
repository.initializeFreeCredits(userId).then((result) => {
|
|
123
|
-
setIsInitializingFreeCredits(false);
|
|
124
|
-
|
|
125
|
-
if (result.success) {
|
|
126
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
127
|
-
console.log("[useCredits] Free credits initialized:", result.data?.credits);
|
|
128
|
-
}
|
|
129
|
-
refetch();
|
|
130
|
-
} else if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
131
|
-
console.warn("[useCredits] Free credits init failed:", result.error?.message);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
} else if (querySuccess && userId && isAnonymous && !credits && autoInit) {
|
|
135
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
136
|
-
console.log("[useCredits] Skipping free credits - anonymous user must register first");
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}, [querySuccess, userId, isRegisteredUser, isAnonymous, isConfigured, credits, autoInit, refetch]);
|
|
98
|
+
const hasCreditsData = (credits?.credits ?? 0) > 0;
|
|
99
|
+
|
|
100
|
+
// Delegate free credits initialization to dedicated hook
|
|
101
|
+
const { isInitializing, needsInit } = useFreeCreditsInit({
|
|
102
|
+
userId,
|
|
103
|
+
isRegisteredUser,
|
|
104
|
+
isAnonymous,
|
|
105
|
+
hasCredits: hasCreditsData,
|
|
106
|
+
querySuccess,
|
|
107
|
+
onInitComplete: refetch,
|
|
108
|
+
});
|
|
140
109
|
|
|
141
110
|
const derivedValues = useMemo(() => {
|
|
142
111
|
const has = (credits?.credits ?? 0) > 0;
|
|
@@ -149,7 +118,12 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
149
118
|
[credits]
|
|
150
119
|
);
|
|
151
120
|
|
|
152
|
-
|
|
121
|
+
// Include needsInit in initializing state for accurate loading detection
|
|
122
|
+
const loadStatus = deriveLoadStatus(
|
|
123
|
+
status,
|
|
124
|
+
isInitializing || needsInit,
|
|
125
|
+
queryEnabled
|
|
126
|
+
);
|
|
153
127
|
const isCreditsLoaded = loadStatus === "ready";
|
|
154
128
|
const isLoading = loadStatus === "loading" || loadStatus === "initializing";
|
|
155
129
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFreeCreditsInit Hook
|
|
3
|
+
*
|
|
4
|
+
* Handles free credits initialization for newly registered users.
|
|
5
|
+
* Separated from useCredits for better maintainability and testability.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useState, useEffect, useCallback } from "react";
|
|
9
|
+
import {
|
|
10
|
+
getCreditsRepository,
|
|
11
|
+
getCreditsConfig,
|
|
12
|
+
isCreditsRepositoryConfigured,
|
|
13
|
+
} from "../../infrastructure/repositories/CreditsRepositoryProvider";
|
|
14
|
+
|
|
15
|
+
declare const __DEV__: boolean;
|
|
16
|
+
|
|
17
|
+
const freeCreditsInitAttempted = new Set<string>();
|
|
18
|
+
|
|
19
|
+
export interface UseFreeCreditsInitParams {
|
|
20
|
+
userId: string | null | undefined;
|
|
21
|
+
isRegisteredUser: boolean;
|
|
22
|
+
isAnonymous: boolean;
|
|
23
|
+
hasCredits: boolean;
|
|
24
|
+
querySuccess: boolean;
|
|
25
|
+
onInitComplete: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UseFreeCreditsInitResult {
|
|
29
|
+
isInitializing: boolean;
|
|
30
|
+
needsInit: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useFreeCreditsInit(params: UseFreeCreditsInitParams): UseFreeCreditsInitResult {
|
|
34
|
+
const { userId, isRegisteredUser, isAnonymous, hasCredits, querySuccess, onInitComplete } = params;
|
|
35
|
+
const [isInitializing, setIsInitializing] = useState(false);
|
|
36
|
+
|
|
37
|
+
const isConfigured = isCreditsRepositoryConfigured();
|
|
38
|
+
const config = getCreditsConfig();
|
|
39
|
+
const freeCredits = config.freeCredits ?? 0;
|
|
40
|
+
const autoInit = config.autoInitializeFreeCredits !== false && freeCredits > 0;
|
|
41
|
+
|
|
42
|
+
const needsInit =
|
|
43
|
+
querySuccess &&
|
|
44
|
+
!!userId &&
|
|
45
|
+
isRegisteredUser &&
|
|
46
|
+
isConfigured &&
|
|
47
|
+
!hasCredits &&
|
|
48
|
+
autoInit &&
|
|
49
|
+
!freeCreditsInitAttempted.has(userId);
|
|
50
|
+
|
|
51
|
+
const initializeFreeCredits = useCallback(async (uid: string) => {
|
|
52
|
+
freeCreditsInitAttempted.add(uid);
|
|
53
|
+
setIsInitializing(true);
|
|
54
|
+
|
|
55
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
56
|
+
console.log("[useFreeCreditsInit] Initializing free credits:", uid.slice(0, 8));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const repository = getCreditsRepository();
|
|
60
|
+
const result = await repository.initializeFreeCredits(uid);
|
|
61
|
+
setIsInitializing(false);
|
|
62
|
+
|
|
63
|
+
if (result.success) {
|
|
64
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
65
|
+
console.log("[useFreeCreditsInit] Free credits initialized:", result.data?.credits);
|
|
66
|
+
}
|
|
67
|
+
onInitComplete();
|
|
68
|
+
} else if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
69
|
+
console.warn("[useFreeCreditsInit] Free credits init failed:", result.error?.message);
|
|
70
|
+
}
|
|
71
|
+
}, [onInitComplete]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (needsInit && userId) {
|
|
75
|
+
initializeFreeCredits(userId);
|
|
76
|
+
} else if (querySuccess && userId && isAnonymous && !hasCredits && autoInit) {
|
|
77
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
78
|
+
console.log("[useFreeCreditsInit] Skipping - anonymous user must register first");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [needsInit, userId, querySuccess, isAnonymous, hasCredits, autoInit, initializeFreeCredits]);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
isInitializing,
|
|
85
|
+
needsInit,
|
|
86
|
+
};
|
|
87
|
+
}
|