@umituz/react-native-subscription 2.26.9 → 2.26.11

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.9",
3
+ "version": "2.26.11",
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",
@@ -166,9 +166,10 @@ export class CreditsRepository extends BaseRepository {
166
166
  isPremium: false,
167
167
  status: "free" as const,
168
168
 
169
- // Free credits
169
+ // Free credits - store initial amount for tracking
170
170
  credits: freeCredits,
171
171
  creditLimit: freeCredits,
172
+ initialFreeCredits: freeCredits,
172
173
  isFreeCredits: true,
173
174
 
174
175
  // Dates
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { useQuery } from "@umituz/react-native-design-system";
10
- import { useCallback, useMemo, useRef, useEffect } from "react";
10
+ import { useCallback, useMemo, useEffect } from "react";
11
11
  import type { UserCredits } from "../../domain/entities/Credits";
12
12
  import {
13
13
  getCreditsRepository,
@@ -27,6 +27,12 @@ const DEFAULT_STALE_TIME = 30 * 1000;
27
27
  /** Default gc time: 5 minutes */
28
28
  const DEFAULT_GC_TIME = 5 * 60 * 1000;
29
29
 
30
+ /**
31
+ * Global tracker for free credits initialization attempts.
32
+ * Shared across all useCredits hook instances to prevent multiple inits.
33
+ */
34
+ const freeCreditsInitAttempted = new Set<string>();
35
+
30
36
  export interface CreditsCacheConfig {
31
37
  /** Time in ms before data is considered stale. Default: 30 seconds */
32
38
  staleTime?: number;
@@ -37,6 +43,8 @@ export interface CreditsCacheConfig {
37
43
  export interface UseCreditsParams {
38
44
  userId: string | undefined;
39
45
  enabled?: boolean;
46
+ /** Whether user is anonymous. Anonymous users don't get free credits. */
47
+ isAnonymous?: boolean;
40
48
  /** Cache configuration. Default: 30 second staleTime, 5 minute gcTime */
41
49
  cache?: CreditsCacheConfig;
42
50
  }
@@ -55,6 +63,7 @@ export interface UseCreditsResult {
55
63
  export const useCredits = ({
56
64
  userId,
57
65
  enabled = true,
66
+ isAnonymous = false,
58
67
  cache,
59
68
  }: UseCreditsParams): UseCreditsResult => {
60
69
  const isConfigured = isCreditsRepositoryConfigured();
@@ -66,9 +75,6 @@ export const useCredits = ({
66
75
 
67
76
  const queryEnabled = enabled && !!userId && isConfigured;
68
77
 
69
- // Track if free credits initialization has been attempted
70
- const freeCreditsInitAttemptedRef = useRef<string | null>(null);
71
-
72
78
  const { data, isLoading, error, refetch, isFetched } = useQuery({
73
79
  queryKey: creditsQueryKeys.user(userId ?? ""),
74
80
  queryFn: async () => {
@@ -109,23 +115,26 @@ export const useCredits = ({
109
115
  useEffect(() => {
110
116
  // Only run if:
111
117
  // 1. Query has completed (isFetched)
112
- // 2. User is authenticated
118
+ // 2. User is authenticated (not anonymous)
113
119
  // 3. No credits data exists
114
120
  // 4. Free credits configured
115
121
  // 5. Auto-init enabled
116
- // 6. Haven't already attempted for this user
122
+ // 6. Haven't already attempted for this user (global tracking)
123
+ // 7. User is NOT anonymous (anonymous users must register first)
117
124
  if (
118
125
  isFetched &&
119
126
  userId &&
127
+ !isAnonymous &&
120
128
  isConfigured &&
121
129
  !credits &&
122
130
  autoInit &&
123
- freeCreditsInitAttemptedRef.current !== userId
131
+ !freeCreditsInitAttempted.has(userId)
124
132
  ) {
125
- freeCreditsInitAttemptedRef.current = userId;
133
+ // Mark as attempted IMMEDIATELY to prevent other hook instances
134
+ freeCreditsInitAttempted.add(userId);
126
135
 
127
136
  if (typeof __DEV__ !== "undefined" && __DEV__) {
128
- console.log("[useCredits] Auto-initializing free credits for new user:", userId.slice(0, 8));
137
+ console.log("[useCredits] Auto-initializing free credits for new registered user:", userId.slice(0, 8));
129
138
  }
130
139
 
131
140
  const repository = getCreditsRepository();
@@ -134,7 +143,6 @@ export const useCredits = ({
134
143
  if (typeof __DEV__ !== "undefined" && __DEV__) {
135
144
  console.log("[useCredits] Free credits initialized:", result.data?.credits);
136
145
  }
137
- // Refetch to get the new credits
138
146
  refetch();
139
147
  } else {
140
148
  if (typeof __DEV__ !== "undefined" && __DEV__) {
@@ -142,8 +150,12 @@ export const useCredits = ({
142
150
  }
143
151
  }
144
152
  });
153
+ } else if (isFetched && userId && isAnonymous && !credits && autoInit) {
154
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
155
+ console.log("[useCredits] Skipping free credits for anonymous user - registration required");
156
+ }
145
157
  }
146
- }, [isFetched, userId, isConfigured, credits, autoInit, refetch]);
158
+ }, [isFetched, userId, isAnonymous, isConfigured, credits, autoInit, refetch]);
147
159
 
148
160
  // Memoize derived values to prevent unnecessary re-renders
149
161
  const derivedValues = useMemo(() => {
@@ -179,9 +191,10 @@ export const useCredits = ({
179
191
  };
180
192
 
181
193
  export const useHasCredits = (
182
- userId: string | undefined
194
+ userId: string | undefined,
195
+ isAnonymous?: boolean
183
196
  ): boolean => {
184
- const { credits } = useCredits({ userId });
197
+ const { credits } = useCredits({ userId, isAnonymous });
185
198
  if (!credits) return false;
186
199
  return credits.credits > 0;
187
200
  };