@umituz/react-native-subscription 2.9.7 → 2.10.0

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.9.7",
3
+ "version": "2.10.0",
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",
package/src/index.ts CHANGED
@@ -239,6 +239,22 @@ export {
239
239
  type UseCreditCheckerResult,
240
240
  } from "./presentation/hooks/useCreditChecker";
241
241
 
242
+ export {
243
+ usePaywallVisibility,
244
+ type UsePaywallVisibilityResult,
245
+ } from "./presentation/hooks/usePaywallVisibility";
246
+
247
+ export {
248
+ usePremiumWithConfig,
249
+ type UsePremiumWithConfigParams,
250
+ type UsePremiumWithConfigResult,
251
+ } from "./presentation/hooks/usePremiumWithConfig";
252
+
253
+ export {
254
+ useAuthSubscriptionSync,
255
+ type AuthSubscriptionSyncConfig,
256
+ } from "./presentation/hooks/useAuthSubscriptionSync";
257
+
242
258
  // =============================================================================
243
259
  // CREDITS SYSTEM - Utilities
244
260
  // =============================================================================
@@ -0,0 +1,56 @@
1
+ /**
2
+ * useAuthSubscriptionSync Hook
3
+ * Single source of truth for RevenueCat initialization
4
+ * Handles initial setup and auth state transitions
5
+ * Generic implementation for 100+ apps with auth provider abstraction
6
+ */
7
+
8
+ import { useEffect, useRef, useCallback } from "react";
9
+
10
+ export interface AuthSubscriptionSyncConfig {
11
+ /** Function to subscribe to auth state changes - returns unsubscribe function */
12
+ onAuthStateChanged: (callback: (userId: string | null) => void) => () => void;
13
+ /** Function to initialize subscription for a user */
14
+ initializeSubscription: (userId: string) => Promise<void>;
15
+ }
16
+
17
+ export function useAuthSubscriptionSync(
18
+ config: AuthSubscriptionSyncConfig,
19
+ ): void {
20
+ const { onAuthStateChanged, initializeSubscription } = config;
21
+ const previousUserIdRef = useRef<string | null>(null);
22
+ const isInitializedRef = useRef(false);
23
+
24
+ const initialize = useCallback(
25
+ async (userId: string) => {
26
+ await initializeSubscription(userId);
27
+ },
28
+ [initializeSubscription],
29
+ );
30
+
31
+ useEffect(() => {
32
+ const unsubscribe = onAuthStateChanged(async (userId: string | null) => {
33
+ if (!userId) {
34
+ previousUserIdRef.current = null;
35
+ return;
36
+ }
37
+
38
+ const previousUserId = previousUserIdRef.current;
39
+
40
+ if (userId === previousUserId) {
41
+ return;
42
+ }
43
+
44
+ if (previousUserId && previousUserId !== userId) {
45
+ await initialize(userId);
46
+ } else if (!isInitializedRef.current) {
47
+ await initialize(userId);
48
+ isInitializedRef.current = true;
49
+ }
50
+
51
+ previousUserIdRef.current = userId;
52
+ });
53
+
54
+ return () => unsubscribe();
55
+ }, [onAuthStateChanged, initialize]);
56
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Paywall Visibility Hook
3
+ * Simple global state for paywall visibility using module-level state
4
+ * Generic implementation for 100+ apps
5
+ */
6
+
7
+ import { useCallback, useSyncExternalStore } from "react";
8
+
9
+ type Listener = () => void;
10
+
11
+ let paywallVisible = false;
12
+ const listeners = new Set<Listener>();
13
+
14
+ const subscribe = (listener: Listener): (() => void) => {
15
+ listeners.add(listener);
16
+ return () => listeners.delete(listener);
17
+ };
18
+
19
+ const getSnapshot = (): boolean => paywallVisible;
20
+
21
+ const setPaywallVisible = (visible: boolean): void => {
22
+ paywallVisible = visible;
23
+ listeners.forEach((listener) => listener());
24
+ };
25
+
26
+ export interface UsePaywallVisibilityResult {
27
+ showPaywall: boolean;
28
+ setShowPaywall: (visible: boolean) => void;
29
+ openPaywall: () => void;
30
+ closePaywall: () => void;
31
+ }
32
+
33
+ export function usePaywallVisibility(): UsePaywallVisibilityResult {
34
+ const showPaywall = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
35
+
36
+ const setShowPaywall = useCallback((visible: boolean) => {
37
+ setPaywallVisible(visible);
38
+ }, []);
39
+
40
+ const openPaywall = useCallback(() => {
41
+ setPaywallVisible(true);
42
+ }, []);
43
+
44
+ const closePaywall = useCallback(() => {
45
+ setPaywallVisible(false);
46
+ }, []);
47
+
48
+ return {
49
+ showPaywall,
50
+ setShowPaywall,
51
+ openPaywall,
52
+ closePaywall,
53
+ };
54
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * usePremiumWithConfig Hook
3
+ * Premium status from TanStack Query (credits)
4
+ * Subscription state from TanStack Query
5
+ * Generic implementation for 100+ apps with auth provider abstraction
6
+ */
7
+
8
+ import { useCallback } from "react";
9
+ import type { PurchasesPackage } from "react-native-purchases";
10
+ import { useCredits } from "./useCredits";
11
+ import { useInitializeCredits } from "./useDeductCredit";
12
+ import {
13
+ useSubscriptionPackages,
14
+ usePurchasePackage,
15
+ useRestorePurchase,
16
+ } from "../../revenuecat/presentation/hooks/useSubscriptionQueries";
17
+ import { usePaywallVisibility } from "./usePaywallVisibility";
18
+
19
+ export interface UsePremiumWithConfigParams {
20
+ /** Function to get current user ID (can be from Firebase, Supabase, etc.) */
21
+ getUserId: () => string | null | undefined;
22
+ /** Enable/disable credits query */
23
+ enabled?: boolean;
24
+ }
25
+
26
+ export interface UsePremiumWithConfigResult {
27
+ isPremium: boolean;
28
+ isLoading: boolean;
29
+ packages: PurchasesPackage[];
30
+ credits: any; // UserCredits from subscription package
31
+ showPaywall: boolean;
32
+ purchasePackage: (pkg: PurchasesPackage) => Promise<boolean>;
33
+ restorePurchase: () => Promise<boolean>;
34
+ setShowPaywall: (show: boolean) => void;
35
+ closePaywall: () => void;
36
+ openPaywall: () => void;
37
+ }
38
+
39
+ export const usePremiumWithConfig = (
40
+ params: UsePremiumWithConfigParams,
41
+ ): UsePremiumWithConfigResult => {
42
+ const { getUserId, enabled = true } = params;
43
+ const userId = getUserId();
44
+
45
+ const { credits, isLoading: creditsLoading } = useCredits({
46
+ userId,
47
+ enabled: enabled && !!userId,
48
+ });
49
+
50
+ const { initializeCredits } = useInitializeCredits({ userId });
51
+
52
+ const { data: packages = [], isLoading: packagesLoading } =
53
+ useSubscriptionPackages(userId);
54
+ const purchaseMutation = usePurchasePackage(userId);
55
+ const restoreMutation = useRestorePurchase(userId);
56
+
57
+ const { showPaywall, setShowPaywall, closePaywall, openPaywall } =
58
+ usePaywallVisibility();
59
+
60
+ const isPremium = credits !== null;
61
+
62
+ const handlePurchase = useCallback(
63
+ async (pkg: PurchasesPackage): Promise<boolean> => {
64
+ const success = await purchaseMutation.mutateAsync(pkg);
65
+ if (success && userId) {
66
+ await initializeCredits();
67
+ }
68
+ return success;
69
+ },
70
+ [purchaseMutation, userId, initializeCredits],
71
+ );
72
+
73
+ const handleRestore = useCallback(async (): Promise<boolean> => {
74
+ const success = await restoreMutation.mutateAsync();
75
+ if (success && userId) {
76
+ await initializeCredits();
77
+ }
78
+ return success;
79
+ }, [restoreMutation, userId, initializeCredits]);
80
+
81
+ return {
82
+ isPremium,
83
+ isLoading:
84
+ creditsLoading ||
85
+ packagesLoading ||
86
+ purchaseMutation.isPending ||
87
+ restoreMutation.isPending,
88
+ packages,
89
+ credits,
90
+ showPaywall,
91
+ purchasePackage: handlePurchase,
92
+ restorePurchase: handleRestore,
93
+ setShowPaywall,
94
+ closePaywall,
95
+ openPaywall,
96
+ };
97
+ };