@umituz/react-native-subscription 2.27.97 → 2.27.99

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.97",
3
+ "version": "2.27.99",
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",
@@ -37,8 +37,8 @@
37
37
  "expo-constants": ">=17.0.0",
38
38
  "expo-image": ">=3.0.0",
39
39
  "firebase": ">=10.0.0",
40
- "react": ">=18.2.0",
41
- "react-native": ">=0.74.0",
40
+ "react": ">=19.0.0",
41
+ "react-native": ">=0.81.0",
42
42
  "react-native-purchases": ">=7.0.0",
43
43
  "react-native-safe-area-context": ">=5.0.0",
44
44
  "zustand": ">=5.0.0"
@@ -82,24 +82,20 @@ export const useDeductCredit = ({
82
82
 
83
83
  const deductCredit = useCallback(async (cost: number = 1): Promise<boolean> => {
84
84
  if (typeof __DEV__ !== "undefined" && __DEV__) {
85
- console.log("[useDeductCredit] Attempting to deduct:", cost);
86
85
  }
87
86
  try {
88
87
  const res = await mutation.mutateAsync(cost);
89
88
  if (!res.success) {
90
89
  if (typeof __DEV__ !== "undefined" && __DEV__) {
91
- console.log("[useDeductCredit] Deduction failed:", res.error?.code, res.error?.message);
92
90
  }
93
91
  if (res.error?.code === "CREDITS_EXHAUSTED") onCreditsExhausted?.();
94
92
  return false;
95
93
  }
96
94
  if (typeof __DEV__ !== "undefined" && __DEV__) {
97
- console.log("[useDeductCredit] Deduction successful, remaining:", res.remainingCredits);
98
95
  }
99
96
  return true;
100
97
  } catch (err) {
101
98
  if (typeof __DEV__ !== "undefined" && __DEV__) {
102
- console.log("[useDeductCredit] Deduction error:", err);
103
99
  }
104
100
  return false;
105
101
  }
@@ -74,7 +74,6 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
74
74
  if (!selectedPlanId || !onPurchase) return;
75
75
 
76
76
  if (__DEV__) {
77
- console.log("[PaywallModal] handlePurchase starting:", { selectedPlanId });
78
77
  }
79
78
 
80
79
  setIsLocalProcessing(true);
@@ -84,11 +83,9 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
84
83
  const pkg = packages.find((p) => p.product.identifier === selectedPlanId);
85
84
  if (pkg) {
86
85
  if (__DEV__) {
87
- console.log("[PaywallModal] Calling onPurchase:", { productId: pkg.product.identifier });
88
86
  }
89
87
  await onPurchase(pkg);
90
88
  if (__DEV__) {
91
- console.log("[PaywallModal] onPurchase completed");
92
89
  }
93
90
  }
94
91
  } catch (error) {
@@ -100,7 +97,6 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
100
97
  setIsLocalProcessing(false);
101
98
  endPurchase();
102
99
  if (__DEV__) {
103
- console.log("[PaywallModal] handlePurchase finished");
104
100
  }
105
101
  }
106
102
  }, [selectedPlanId, packages, onPurchase, startPurchase, endPurchase]);
@@ -109,14 +105,12 @@ export const PaywallModal: React.FC<PaywallModalProps> = React.memo((props) => {
109
105
  if (!onRestore || isProcessing) return;
110
106
 
111
107
  if (__DEV__) {
112
- console.log("[PaywallModal] handleRestore starting");
113
108
  }
114
109
 
115
110
  setIsLocalProcessing(true);
116
111
  try {
117
112
  await onRestore();
118
113
  if (__DEV__) {
119
- console.log("[PaywallModal] handleRestore completed");
120
114
  }
121
115
  } finally {
122
116
  setIsLocalProcessing(false);
@@ -94,9 +94,6 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
94
94
 
95
95
  const initializeInBackground = async (userId?: string): Promise<void> => {
96
96
  await SubscriptionManager.initialize(userId);
97
- if (__DEV__) {
98
- console.log('[SubscriptionInitializer] Background init complete');
99
- }
100
97
  };
101
98
 
102
99
  // 5. Start Background Init
@@ -110,9 +107,6 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
110
107
 
111
108
  // 6. Listen for Auth Changes
112
109
  setupAuthStateListener(() => auth, (newUserId) => {
113
- if (__DEV__) {
114
- console.log('[SubscriptionInitializer] Auth changed, re-init:', newUserId);
115
- }
116
110
  initializeInBackground(newUserId);
117
111
  });
118
112
  };
@@ -32,27 +32,18 @@ export class PackageHandler {
32
32
  const offering = await this.service.fetchOfferings();
33
33
 
34
34
  if (!offering) {
35
- if (__DEV__) {
36
- console.warn("[PackageHandler] No offerings available from RevenueCat");
37
- }
38
35
  // Return empty array instead of throwing - allows graceful degradation
39
36
  return [];
40
37
  }
41
38
 
42
39
  const packages = offering.availablePackages;
43
40
  if (!packages || packages.length === 0) {
44
- if (__DEV__) {
45
- console.warn("[PackageHandler] No packages available in offering");
46
- }
47
41
  // Return empty array instead of throwing - allows graceful degradation
48
42
  return [];
49
43
  }
50
44
 
51
45
  return packages;
52
46
  } catch (error) {
53
- if (__DEV__) {
54
- console.error("[PackageHandler] Failed to fetch packages:", error);
55
- }
56
47
  // Re-throw with more context
57
48
  throw new Error(
58
49
  `Failed to fetch subscription packages. ${
@@ -71,23 +71,10 @@ export function useCustomerInfo(): UseCustomerInfoResult {
71
71
  const info = await Purchases.getCustomerInfo();
72
72
 
73
73
  setCustomerInfo(info);
74
-
75
- if (__DEV__) {
76
- console.log('[DEBUG useCustomerInfo] Fetched:', {
77
- hasActiveEntitlements: Object.keys(info.entitlements.active).length > 0,
78
- latestExpiration: info.latestExpirationDate || "none",
79
- });
80
- }
81
74
  } catch (err) {
82
75
  const errorMessage =
83
76
  err instanceof Error ? err.message : "Failed to fetch customer info";
84
77
  setError(errorMessage);
85
-
86
- if (__DEV__) {
87
- console.error('[DEBUG useCustomerInfo] Error:', {
88
- error: err,
89
- });
90
- }
91
78
  } finally {
92
79
  setLoading(false);
93
80
  setIsFetching(false);
@@ -100,12 +87,6 @@ export function useCustomerInfo(): UseCustomerInfoResult {
100
87
 
101
88
  // Listen for real-time updates (renewals, purchases, restore)
102
89
  const listener = (info: CustomerInfo) => {
103
- if (__DEV__) {
104
- console.log('[DEBUG useCustomerInfo] Listener update:', {
105
- hasActiveEntitlements: Object.keys(info.entitlements.active).length > 0,
106
- });
107
- }
108
-
109
90
  setCustomerInfo(info);
110
91
  setError(null);
111
92
  };
@@ -19,10 +19,6 @@ export const useInitializeSubscription = (userId: string | undefined) => {
19
19
  throw new Error("User not authenticated");
20
20
  }
21
21
 
22
- if (__DEV__) {
23
- console.log('[DEBUG useInitializeSubscription] Initializing:', { userId });
24
- }
25
-
26
22
  return SubscriptionManager.initialize(userId);
27
23
  },
28
24
  onSuccess: () => {
@@ -30,18 +26,6 @@ export const useInitializeSubscription = (userId: string | undefined) => {
30
26
  queryClient.invalidateQueries({
31
27
  queryKey: SUBSCRIPTION_QUERY_KEYS.packages,
32
28
  });
33
-
34
- if (__DEV__) {
35
- console.log('[DEBUG useInitializeSubscription] Success:', { userId });
36
- }
37
- }
38
- },
39
- onError: (error) => {
40
- if (__DEV__) {
41
- console.error('[DEBUG useInitializeSubscription] Error:', {
42
- error,
43
- userId: userId ?? "ANONYMOUS",
44
- });
45
29
  }
46
30
  },
47
31
  });
@@ -40,33 +40,12 @@ export const usePurchasePackage = () => {
40
40
  }
41
41
 
42
42
  const productId = pkg.product.identifier;
43
-
44
- if (__DEV__) {
45
- console.log('[DEBUG usePurchasePackage] Starting purchase:', {
46
- packageId: pkg.identifier,
47
- productId,
48
- userId,
49
- });
50
- }
51
-
52
43
  const success = await SubscriptionManager.purchasePackage(pkg);
53
44
 
54
- if (__DEV__) {
55
- console.log('[DEBUG usePurchasePackage] Purchase result:', {
56
- success,
57
- packageId: pkg.identifier,
58
- productId,
59
- userId,
60
- });
61
- }
62
-
63
45
  return { success, productId };
64
46
  },
65
47
  onSuccess: (result) => {
66
48
  if (result.success) {
67
- if (__DEV__) {
68
- console.log('[DEBUG usePurchasePackage] onSuccess - invalidating queries');
69
- }
70
49
  showSuccess("Purchase Successful", "Your subscription is now active!");
71
50
  queryClient.invalidateQueries({
72
51
  queryKey: SUBSCRIPTION_QUERY_KEYS.packages,
@@ -77,20 +56,10 @@ export const usePurchasePackage = () => {
77
56
  });
78
57
  }
79
58
  } else {
80
- if (__DEV__) {
81
- console.log('[DEBUG usePurchasePackage] onSuccess but result.success=false');
82
- }
83
59
  showError("Purchase Failed", "Unable to complete purchase. Please try again.");
84
60
  }
85
61
  },
86
62
  onError: (error) => {
87
- if (__DEV__) {
88
- console.error('[DEBUG usePurchasePackage] onError:', {
89
- error,
90
- userId: userId ?? "ANONYMOUS",
91
- });
92
- }
93
-
94
63
  let title = "Purchase Error";
95
64
  let message = "Unable to complete purchase. Please try again.";
96
65
 
@@ -34,26 +34,7 @@ export const useRestorePurchase = () => {
34
34
  throw new Error("User not authenticated");
35
35
  }
36
36
 
37
- if (__DEV__) {
38
- console.log('[DEBUG useRestorePurchase] Starting restore:', { userId });
39
- }
40
-
41
37
  const result = await SubscriptionManager.restore();
42
-
43
- if (result.success) {
44
- if (__DEV__) {
45
- console.log('[DEBUG useRestorePurchase] Restore successful:', {
46
- userId,
47
- productId: result.productId,
48
- });
49
- }
50
- // Credits will be initialized by CustomerInfoListener
51
- } else {
52
- if (__DEV__) {
53
- console.log('[DEBUG useRestorePurchase] Restore failed:', { userId });
54
- }
55
- }
56
-
57
38
  return result;
58
39
  },
59
40
  onSuccess: (result) => {
@@ -68,13 +49,5 @@ export const useRestorePurchase = () => {
68
49
  }
69
50
  }
70
51
  },
71
- onError: (error) => {
72
- if (__DEV__) {
73
- console.error('[DEBUG useRestorePurchase] Restore mutation failed:', {
74
- error,
75
- userId: userId ?? "ANONYMOUS",
76
- });
77
- }
78
- },
79
52
  });
80
53
  };
@@ -85,9 +85,6 @@ export function useRevenueCatTrialEligibility(): UseRevenueCatTrialEligibilityRe
85
85
 
86
86
  const service = getRevenueCatService();
87
87
  if (!service || !service.isInitialized()) {
88
- if (__DEV__) {
89
- console.log("[TrialEligibility] RevenueCat not initialized");
90
- }
91
88
  return;
92
89
  }
93
90
 
@@ -109,12 +106,6 @@ export function useRevenueCatTrialEligibility(): UseRevenueCatTrialEligibilityRe
109
106
  eligible: isEligible,
110
107
  trialDurationDays: 7, // Default to 7 days as configured in App Store Connect
111
108
  };
112
-
113
- if (__DEV__) {
114
- console.log(
115
- `[TrialEligibility] ${productId}: ${isEligible ? "ELIGIBLE" : "NOT_ELIGIBLE"}`
116
- );
117
- }
118
109
  }
119
110
 
120
111
  // Update cache
@@ -127,9 +118,6 @@ export function useRevenueCatTrialEligibility(): UseRevenueCatTrialEligibilityRe
127
118
  setEligibilityMap((prev) => ({ ...prev, ...newMap }));
128
119
  }
129
120
  } catch (error) {
130
- if (__DEV__) {
131
- console.log("[TrialEligibility] Error checking eligibility:", error);
132
- }
133
121
  // On error, default to eligible (better UX)
134
122
  const fallbackMap: TrialEligibilityMap = {};
135
123
  for (const productId of productIds) {
@@ -58,14 +58,6 @@ export class CustomerInfoListenerManager {
58
58
 
59
59
  // Handle renewal (same product, extended expiration)
60
60
  if (renewalResult.isRenewal && config.onRenewalDetected) {
61
- if (__DEV__) {
62
- console.log("[CustomerInfoListener] Renewal detected:", {
63
- userId: this.currentUserId,
64
- productId: renewalResult.productId,
65
- newExpiration: renewalResult.newExpirationDate,
66
- });
67
- }
68
-
69
61
  try {
70
62
  await config.onRenewalDetected(
71
63
  this.currentUserId,
@@ -82,16 +74,6 @@ export class CustomerInfoListenerManager {
82
74
 
83
75
  // Handle plan change (upgrade/downgrade)
84
76
  if (renewalResult.isPlanChange && config.onPlanChanged) {
85
- if (__DEV__) {
86
- console.log("[CustomerInfoListener] Plan change detected:", {
87
- userId: this.currentUserId,
88
- previousProductId: renewalResult.previousProductId,
89
- newProductId: renewalResult.productId,
90
- isUpgrade: renewalResult.isUpgrade,
91
- isDowngrade: renewalResult.isDowngrade,
92
- });
93
- }
94
-
95
77
  try {
96
78
  await config.onPlanChanged(
97
79
  this.currentUserId,
@@ -133,9 +115,6 @@ export class CustomerInfoListenerManager {
133
115
  }
134
116
 
135
117
  destroy(): void {
136
- if (__DEV__) {
137
- console.log('[CustomerInfoListenerManager] Destroying listener manager');
138
- }
139
118
  this.removeListener();
140
119
  this.clearUserId();
141
120
  // Reset renewal state to ensure clean state
@@ -85,15 +85,12 @@ export async function initializeSDK(
85
85
 
86
86
  const key = apiKey || resolveApiKey(deps.config);
87
87
  if (!key) {
88
- if (__DEV__) console.log('[RevenueCat] No API key');
89
88
  return { success: false, offering: null, isPremium: false };
90
89
  }
91
90
 
92
91
  configurationState.configurationInProgress = true;
93
92
  try {
94
93
  configureLogHandler();
95
- if (__DEV__) console.log('[RevenueCat] Configuring:', key.substring(0, 10) + '...');
96
-
97
94
  await Purchases.configure({ apiKey: key, appUserID: userId });
98
95
  configurationState.isPurchasesConfigured = true;
99
96
  deps.setInitialized(true);
@@ -104,14 +101,8 @@ export async function initializeSDK(
104
101
  Purchases.getOfferings(),
105
102
  ]);
106
103
 
107
- if (__DEV__) {
108
- console.log('[RevenueCat] Initialized', {
109
- packages: offerings.current?.availablePackages?.length ?? 0,
110
- });
111
- }
112
104
  return buildSuccessResult(deps, customerInfo, offerings);
113
105
  } catch (error) {
114
- if (__DEV__) console.error('[RevenueCat] Init failed:', error);
115
106
  return { success: false, offering: null, isPremium: false };
116
107
  } finally {
117
108
  configurationState.configurationInProgress = false;
@@ -49,27 +49,12 @@ export async function notifyPurchaseCompleted(
49
49
  customerInfo: CustomerInfo,
50
50
  source?: PurchaseSource
51
51
  ): Promise<void> {
52
- if (__DEV__) {
53
- console.log('[PremiumStatusSyncer] notifyPurchaseCompleted called:', {
54
- userId,
55
- productId,
56
- source,
57
- hasCallback: !!config.onPurchaseCompleted,
58
- });
59
- }
60
-
61
52
  if (!config.onPurchaseCompleted) {
62
- if (__DEV__) {
63
- console.warn('[PremiumStatusSyncer] No onPurchaseCompleted callback configured!');
64
- }
65
53
  return;
66
54
  }
67
55
 
68
56
  try {
69
57
  await config.onPurchaseCompleted(userId, productId, customerInfo, source);
70
- if (__DEV__) {
71
- console.log('[PremiumStatusSyncer] onPurchaseCompleted callback executed successfully');
72
- }
73
58
  } catch (error) {
74
59
  if (__DEV__) {
75
60
  console.error('[PremiumStatusSyncer] onPurchaseCompleted callback failed:', error);
@@ -13,7 +13,6 @@ import {
13
13
  import { SubscriptionHeader } from "./components/SubscriptionHeader";
14
14
  import { CreditsList, type CreditItem } from "./components/CreditsList";
15
15
  import { UpgradePrompt, type Benefit } from "./components/UpgradePrompt";
16
- import { DevTestSection, type DevTestActions } from "./components/DevTestSection";
17
16
 
18
17
  export interface SubscriptionDisplayFlags {
19
18
  showHeader: boolean;
@@ -39,15 +38,11 @@ export interface SubscriptionDetailTranslations {
39
38
  upgradeButton: string;
40
39
  }
41
40
 
42
- export interface DevToolsConfig {
43
- actions: DevTestActions;
44
- title?: string;
45
- }
46
-
47
41
  export interface UpgradePromptConfig {
48
42
  title: string;
49
43
  subtitle?: string;
50
44
  benefits?: readonly Benefit[];
45
+ onUpgrade?: () => void;
51
46
  }
52
47
 
53
48
  export interface SubscriptionDetailConfig {
@@ -59,8 +54,7 @@ export interface SubscriptionDetailConfig {
59
54
  daysRemaining?: number | null;
60
55
  credits?: readonly CreditItem[];
61
56
  translations: SubscriptionDetailTranslations;
62
- upgradePrompt?: UpgradePromptConfig & { onUpgrade?: () => void };
63
- devTools?: DevToolsConfig;
57
+ upgradePrompt?: UpgradePromptConfig;
64
58
  }
65
59
 
66
60
  export interface SubscriptionDetailScreenProps {
@@ -98,14 +92,6 @@ export const SubscriptionDetailScreen: React.FC<
98
92
  edges={["bottom"]}
99
93
  backgroundColor={tokens.colors.backgroundPrimary}
100
94
  contentContainerStyle={styles.content}
101
- footer={
102
- config.devTools ? (
103
- <DevTestSection
104
- actions={config.devTools.actions}
105
- title={config.devTools.title}
106
- />
107
- ) : undefined
108
- }
109
95
  >
110
96
  <View style={styles.cardsContainer}>
111
97
  {showHeader && (
@@ -49,7 +49,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
49
49
  // Still update to the new purchase to recover from potential stuck state
50
50
  }
51
51
  if (__DEV__) {
52
- console.log("[PurchaseLoadingStore] startPurchase:", { productId, source });
53
52
  }
54
53
  set({
55
54
  isPurchasing: true,
@@ -67,7 +66,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
67
66
  // Reset to initial state to recover from potential stuck state
68
67
  }
69
68
  if (__DEV__) {
70
- console.log("[PurchaseLoadingStore] endPurchase");
71
69
  }
72
70
  set({
73
71
  isPurchasing: false,
@@ -78,7 +76,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
78
76
 
79
77
  reset: () => {
80
78
  if (__DEV__) {
81
- console.log("[PurchaseLoadingStore] reset");
82
79
  }
83
80
  set(initialState);
84
81
  },
@@ -64,48 +64,19 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
64
64
  }, [requiredCredits]);
65
65
 
66
66
  useEffect(() => {
67
- if (typeof __DEV__ !== "undefined" && __DEV__) {
68
- console.log("[useFeatureGate] Auth credits effect", {
69
- isWaiting: isWaitingForAuthCreditsRef.current,
70
- isLoaded: isCreditsLoaded,
71
- hasPending: !!pendingActionRef.current,
72
- credits: creditBalance,
73
- });
74
- }
75
-
76
67
  if (!isWaitingForAuthCreditsRef.current || !isCreditsLoaded || !pendingActionRef.current) {
77
68
  return;
78
69
  }
79
70
 
80
- if (typeof __DEV__ !== "undefined" && __DEV__) {
81
- console.log("[useFeatureGate] Credits loaded after auth", {
82
- credits: creditBalance,
83
- hasSubscription,
84
- isCreditsLoaded,
85
- });
86
- }
87
-
88
71
  isWaitingForAuthCreditsRef.current = false;
89
72
 
90
73
  if (hasSubscription || creditBalance >= requiredCredits) {
91
74
  const action = pendingActionRef.current;
92
75
  pendingActionRef.current = null;
93
-
94
- if (typeof __DEV__ !== "undefined" && __DEV__) {
95
- console.log("[useFeatureGate] Proceeding with action after auth", {
96
- credits: creditBalance,
97
- hasSubscription,
98
- });
99
- }
100
76
  action();
101
77
  return;
102
78
  }
103
79
 
104
- if (typeof __DEV__ !== "undefined" && __DEV__) {
105
- console.log("[useFeatureGate] No credits after auth, showing paywall", {
106
- credits: creditBalance,
107
- });
108
- }
109
80
  isWaitingForPurchaseRef.current = true;
110
81
  onShowPaywall(requiredCredits);
111
82
  }, [isCreditsLoaded, creditBalance, hasSubscription, requiredCredits, onShowPaywall]);
@@ -119,10 +90,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
119
90
  const action = pendingActionRef.current;
120
91
  pendingActionRef.current = null;
121
92
  isWaitingForPurchaseRef.current = false;
122
-
123
- if (typeof __DEV__ !== "undefined" && __DEV__) {
124
- console.log("[useFeatureGate] Access acquired (credits or subscription), executing pending action");
125
- }
126
93
  action();
127
94
  }
128
95
 
@@ -132,24 +99,10 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
132
99
 
133
100
  const requireFeature = useCallback(
134
101
  (action: () => void | Promise<void>) => {
135
- if (typeof __DEV__ !== "undefined" && __DEV__) {
136
- console.log("[useFeatureGate] requireFeature", {
137
- isAuthenticated,
138
- hasSubscription: hasSubscriptionRef.current,
139
- creditBalance: creditBalanceRef.current,
140
- requiredCredits: requiredCreditsRef.current,
141
- isCreditsLoaded,
142
- });
143
- }
144
-
145
102
  if (!isAuthenticated) {
146
103
  const postAuthAction = () => {
147
104
  pendingActionRef.current = action;
148
105
  isWaitingForAuthCreditsRef.current = true;
149
-
150
- if (typeof __DEV__ !== "undefined" && __DEV__) {
151
- console.log("[useFeatureGate] Auth completed, waiting for credits to load");
152
- }
153
106
  };
154
107
  onShowAuthModal(postAuthAction);
155
108
  return;
@@ -166,9 +119,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
166
119
  }
167
120
 
168
121
  if (currentBalance < currentRequiredCredits) {
169
- if (typeof __DEV__ !== "undefined" && __DEV__) {
170
- console.log("[useFeatureGate] No credits, showing paywall");
171
- }
172
122
  pendingActionRef.current = action;
173
123
  isWaitingForPurchaseRef.current = true;
174
124
  onShowPaywallRef.current(currentRequiredCredits);
@@ -1,133 +0,0 @@
1
- /**
2
- * Dev Test Section
3
- * Developer testing tools for subscription renewals
4
- * Only visible in __DEV__ mode
5
- */
6
-
7
- import React, { useMemo } from "react";
8
- import { View, TouchableOpacity, StyleSheet } from "react-native";
9
- import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
10
-
11
- export interface DevTestActions {
12
- onTestRenewal: () => void;
13
- onCheckCredits: () => void;
14
- onTestDuplicate: () => void;
15
- }
16
-
17
- export interface DevTestSectionProps {
18
- actions: DevTestActions;
19
- title?: string;
20
- }
21
-
22
- /** Dev test button translations */
23
- export interface DevTestTranslations {
24
- title: string;
25
- testRenewal: string;
26
- checkCredits: string;
27
- testDuplicate: string;
28
- }
29
-
30
- export interface DevTestSectionWithTranslationsProps extends DevTestSectionProps {
31
- translations?: DevTestTranslations;
32
- }
33
-
34
- export const DevTestSection: React.FC<DevTestSectionWithTranslationsProps> = ({
35
- actions,
36
- title,
37
- translations,
38
- }) => {
39
- const tokens = useAppDesignTokens();
40
-
41
- const styles = useMemo(
42
- () =>
43
- StyleSheet.create({
44
- container: {
45
- padding: tokens.spacing.lg,
46
- gap: tokens.spacing.md,
47
- borderTopWidth: 1,
48
- backgroundColor: tokens.colors.surfaceSecondary,
49
- borderTopColor: tokens.colors.border,
50
- },
51
- title: {
52
- fontWeight: "600",
53
- marginBottom: tokens.spacing.xs,
54
- },
55
- button: {
56
- paddingVertical: tokens.spacing.md,
57
- paddingHorizontal: tokens.spacing.lg,
58
- borderRadius: tokens.radius.md,
59
- alignItems: "center",
60
- },
61
- primaryButton: {
62
- backgroundColor: tokens.colors.primary,
63
- },
64
- secondaryButton: {
65
- backgroundColor: tokens.colors.surfaceSecondary,
66
- borderWidth: 1,
67
- borderColor: tokens.colors.border,
68
- },
69
- buttonText: {
70
- fontWeight: "500",
71
- },
72
- }),
73
- [tokens]
74
- );
75
-
76
- if (!__DEV__) {
77
- return null;
78
- }
79
-
80
- const displayTitle = title || translations?.title;
81
- const renewalText = translations?.testRenewal || "Test Auto-Renewal";
82
- const creditsText = translations?.checkCredits || "Check Credits";
83
- const duplicateText = translations?.testDuplicate || "Test Duplicate Protection";
84
-
85
- return (
86
- <View style={styles.container}>
87
- {displayTitle && (
88
- <AtomicText
89
- type="titleMedium"
90
- style={[styles.title, { color: tokens.colors.textPrimary }]}
91
- >
92
- {displayTitle}
93
- </AtomicText>
94
- )}
95
-
96
- <TouchableOpacity
97
- style={[styles.button, styles.primaryButton]}
98
- onPress={actions.onTestRenewal}
99
- >
100
- <AtomicText
101
- type="bodyMedium"
102
- style={[styles.buttonText, { color: tokens.colors.onPrimary }]}
103
- >
104
- {renewalText}
105
- </AtomicText>
106
- </TouchableOpacity>
107
-
108
- <TouchableOpacity
109
- style={[styles.button, styles.secondaryButton]}
110
- onPress={actions.onCheckCredits}
111
- >
112
- <AtomicText
113
- type="bodyMedium"
114
- style={[styles.buttonText, { color: tokens.colors.textPrimary }]}
115
- >
116
- {creditsText}
117
- </AtomicText>
118
- </TouchableOpacity>
119
-
120
- <TouchableOpacity
121
- style={[styles.button, styles.secondaryButton]}
122
- onPress={actions.onTestDuplicate}
123
- >
124
- <AtomicText
125
- type="bodyMedium"
126
- style={[styles.buttonText, { color: tokens.colors.textPrimary }]}
127
- >
128
- {duplicateText}
129
- </AtomicText>
130
- </TouchableOpacity>
131
- </View>
132
- );
133
- };