@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",
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 - NO CACHE, always fresh data.
5
- * Auth info automatically read from @umituz/react-native-auth.
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, isLoading, error, refetch, isFetched } = useQuery({
68
+ const { data, status, error, refetch } = useQuery({
55
69
  queryKey: creditsQueryKeys.user(userId ?? ""),
56
70
  queryFn: async () => {
57
- if (!userId || !isConfigured) {
58
- return null;
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
- if (
91
- isFetched &&
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 for registered user:", userId.slice(0, 8));
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
- if (typeof __DEV__ !== "undefined" && __DEV__) {
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 (isFetched && userId && isAnonymous && !credits && autoInit) {
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
- }, [isFetched, userId, isRegisteredUser, isAnonymous, isConfigured, credits, autoInit, refetch]);
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 isCreditsLoaded = isFetched && !isLoading && !isInitializingFreeCredits;
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
- error: error as Error | null,
160
+ loadStatus,
161
+ error: error instanceof Error ? error : null,
151
162
  hasCredits: derivedValues.hasCredits,
152
163
  creditsPercent: derivedValues.creditsPercent,
153
164
  refetch,