@umituz/react-native-subscription 2.35.15 → 2.35.17

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/domains/config/utils/planSelectors.ts +5 -1
  3. package/src/domains/credits/presentation/useCredits.ts +6 -5
  4. package/src/domains/paywall/hooks/usePaywallActions.ts +2 -82
  5. package/src/domains/revenuecat/core/customerInfoHelpers.ts +21 -0
  6. package/src/domains/subscription/application/SubscriptionAuthListener.ts +0 -19
  7. package/src/domains/subscription/application/SubscriptionSyncProcessor.ts +1 -1
  8. package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +2 -8
  9. package/src/domains/subscription/application/initializer/ConfigValidator.ts +2 -2
  10. package/src/domains/subscription/application/initializer/ServiceConfigurator.ts +23 -3
  11. package/src/domains/subscription/application/statusChangeHandlers.ts +0 -30
  12. package/src/domains/subscription/constants/thresholds.ts +10 -0
  13. package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +3 -3
  14. package/src/domains/subscription/infrastructure/handlers/package-operations/PackageFetcher.ts +0 -19
  15. package/src/domains/subscription/infrastructure/hooks/customer-info/useCustomerInfo.ts +1 -1
  16. package/src/domains/subscription/infrastructure/hooks/useInitializeSubscription.ts +2 -4
  17. package/src/domains/subscription/infrastructure/hooks/usePaywallFlow.ts +12 -2
  18. package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +0 -44
  19. package/src/domains/subscription/infrastructure/hooks/useRevenueCat.ts +23 -5
  20. package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +1 -31
  21. package/src/domains/subscription/infrastructure/services/OfferingsFetcher.ts +0 -21
  22. package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +6 -36
  23. package/src/domains/subscription/infrastructure/services/purchase/PurchaseExecutor.ts +0 -6
  24. package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -44
  25. package/src/domains/subscription/presentation/featureGateActions.ts +0 -37
  26. package/src/domains/subscription/presentation/screens/components/SubscriptionHeader.tsx +1 -1
  27. package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx +1 -1
  28. package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +0 -43
  29. package/src/domains/subscription/presentation/useFeatureGate.ts +1 -40
  30. package/src/domains/subscription/presentation/useSubscriptionStatus.ts +6 -20
  31. package/src/domains/subscription/utils/authGuards.ts +26 -2
  32. package/src/domains/subscription/utils/expirationHelpers.ts +2 -2
  33. package/src/domains/trial/application/TrialEligibilityService.ts +1 -1
  34. package/src/domains/trial/application/TrialService.ts +12 -4
  35. package/src/domains/wallet/infrastructure/repositories/transaction/TransactionWriter.ts +1 -1
  36. package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +2 -5
  37. package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +2 -5
  38. package/src/shared/infrastructure/react-query/hooks/usePreviousUserCleanup.ts +39 -0
  39. package/src/shared/infrastructure/react-query/queryConfig.ts +22 -0
  40. package/src/shared/infrastructure/react-query/queryInvalidation.ts +46 -0
  41. package/src/shared/presentation/hooks/useServiceCall.ts +2 -1
  42. package/src/shared/utils/errorUtils.ts +32 -0
  43. package/src/utils/appUtils.ts +6 -0
  44. package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.constants.ts +0 -1
  45. package/src/domains/subscription/presentation/screens/components/SubscriptionHeader.constants.ts +0 -1
@@ -6,8 +6,6 @@ import { executeFeatureAction } from "./featureGateActions";
6
6
 
7
7
  export type { UseFeatureGateParams, UseFeatureGateResult } from "./useFeatureGate.types";
8
8
 
9
- declare const __DEV__: boolean;
10
-
11
9
  export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResult {
12
10
  const {
13
11
  isAuthenticated,
@@ -27,16 +25,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
27
25
  const { creditBalanceRef, hasSubscriptionRef, onShowPaywallRef, requiredCreditsRef, isCreditsLoadedRef } = useSyncedRefs(creditBalance, hasSubscription, onShowPaywall, requiredCredits, isCreditsLoaded);
28
26
 
29
27
  useEffect(() => {
30
- if (typeof __DEV__ !== "undefined" && __DEV__) {
31
- console.log("[FeatureGate] Auth completion useEffect triggered:", {
32
- isWaitingForAuthCredits: isWaitingForAuthCreditsRef.current,
33
- isCreditsLoaded,
34
- hasPendingAction: !!pendingActionRef.current,
35
- hasSubscription,
36
- creditBalance,
37
- requiredCredits,
38
- });
39
- }
40
28
 
41
29
  const shouldExecute = canExecuteAuthAction(
42
30
  isWaitingForAuthCreditsRef.current,
@@ -47,14 +35,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
47
35
  requiredCredits
48
36
  );
49
37
 
50
- if (typeof __DEV__ !== "undefined" && __DEV__) {
51
- console.log("[FeatureGate] canExecuteAuthAction:", shouldExecute);
52
- }
53
-
54
38
  if (shouldExecute) {
55
- if (typeof __DEV__ !== "undefined" && __DEV__) {
56
- console.log("[FeatureGate] ✅ EXECUTING PENDING ACTION after auth!");
57
- }
58
39
  isWaitingForAuthCreditsRef.current = false;
59
40
  const action = pendingActionRef.current!;
60
41
  pendingActionRef.current = null;
@@ -63,9 +44,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
63
44
  }
64
45
 
65
46
  if (isWaitingForAuthCreditsRef.current && isCreditsLoaded && pendingActionRef.current) {
66
- if (typeof __DEV__ !== "undefined" && __DEV__) {
67
- console.log("[FeatureGate] Auth credits loaded but insufficient, showing paywall");
68
- }
69
47
  isWaitingForAuthCreditsRef.current = false;
70
48
  isWaitingForPurchaseRef.current = true;
71
49
  onShowPaywall(requiredCredits);
@@ -73,16 +51,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
73
51
  }, [isCreditsLoaded, creditBalance, hasSubscription, requiredCredits, onShowPaywall]);
74
52
 
75
53
  useEffect(() => {
76
- if (typeof __DEV__ !== "undefined" && __DEV__) {
77
- console.log("[FeatureGate] Purchase completion useEffect triggered:", {
78
- creditBalance,
79
- prevCreditBalance: prevCreditBalanceRef.current,
80
- hasSubscription,
81
- prevHasSubscription: hasSubscriptionRef.current,
82
- isWaitingForPurchase: isWaitingForPurchaseRef.current,
83
- hasPendingAction: !!pendingActionRef.current,
84
- });
85
- }
86
54
 
87
55
  const shouldExecute = canExecutePurchaseAction(
88
56
  isWaitingForPurchaseRef.current,
@@ -93,14 +61,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
93
61
  !!pendingActionRef.current
94
62
  );
95
63
 
96
- if (typeof __DEV__ !== "undefined" && __DEV__) {
97
- console.log("[FeatureGate] canExecutePurchaseAction:", shouldExecute);
98
- }
99
-
100
64
  if (shouldExecute) {
101
- if (typeof __DEV__ !== "undefined" && __DEV__) {
102
- console.log("[FeatureGate] ✅ EXECUTING PENDING ACTION after purchase!");
103
- }
104
65
  const action = pendingActionRef.current!;
105
66
  pendingActionRef.current = null;
106
67
  isWaitingForPurchaseRef.current = false;
@@ -108,7 +69,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
108
69
  }
109
70
 
110
71
  prevCreditBalanceRef.current = creditBalance;
111
- hasSubscriptionRef.current = hasSubscription;
72
+ // hasSubscriptionRef is already synced by useSyncedRefs, no need to update manually
112
73
  // eslint-disable-next-line react-hooks/exhaustive-deps
113
74
  }, [creditBalance, hasSubscription]);
114
75
 
@@ -1,10 +1,12 @@
1
1
  import { useQuery, useQueryClient } from "@umituz/react-native-design-system";
2
- import { useEffect, useRef } from "react";
2
+ import { useEffect } from "react";
3
3
  import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
4
4
  import { SubscriptionManager } from "../infrastructure/managers/SubscriptionManager";
5
5
  import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
6
6
  import { SubscriptionStatusResult } from "./useSubscriptionStatus.types";
7
7
  import { isAuthenticated } from "../utils/authGuards";
8
+ import { NO_CACHE_QUERY_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
9
+ import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
8
10
 
9
11
  export const subscriptionStatusQueryKeys = {
10
12
  all: ["subscriptionStatus"] as const,
@@ -38,27 +40,11 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
38
40
  }
39
41
  },
40
42
  enabled: queryEnabled,
41
- gcTime: 0,
42
- staleTime: 0,
43
- refetchOnMount: "always",
44
- refetchOnWindowFocus: "always",
45
- refetchOnReconnect: "always",
43
+ ...NO_CACHE_QUERY_CONFIG,
46
44
  });
47
45
 
48
- // Track previous userId to clear stale cache on logout/user switch
49
- const prevUserIdRef = useRef(userId);
50
-
51
- useEffect(() => {
52
- const prevUserId = prevUserIdRef.current;
53
- prevUserIdRef.current = userId;
54
-
55
- // Clear previous user's cache when userId changes (logout or user switch)
56
- if (prevUserId !== userId && isAuthenticated(prevUserId)) {
57
- queryClient.removeQueries({
58
- queryKey: subscriptionStatusQueryKeys.user(prevUserId),
59
- });
60
- }
61
- }, [userId, queryClient]);
46
+ // Clean up previous user's cache on logout/user switch
47
+ usePreviousUserCleanup(userId, queryClient, subscriptionStatusQueryKeys.user);
62
48
 
63
49
  useEffect(() => {
64
50
  if (!isAuthenticated(userId)) return undefined;
@@ -1,5 +1,29 @@
1
1
  import { isDefined } from "../../../shared/utils/validators";
2
2
 
3
- export const isAuthenticated = (userId: string | null | undefined): userId is string => {
3
+ export function isAuthenticated(userId: string | null | undefined): userId is string {
4
4
  return isDefined(userId) && userId.length > 0;
5
- };
5
+ }
6
+
7
+ /**
8
+ * Requires user to be authenticated, throws if not
9
+ * Type guard that asserts userId is string
10
+ *
11
+ * @param userId - User ID to check
12
+ * @param errorMessage - Custom error message (optional)
13
+ * @throws Error if user is not authenticated
14
+ *
15
+ * @example
16
+ * function purchaseProduct(userId: string | null) {
17
+ * requireAuthentication(userId); // throws if null/undefined
18
+ * // TypeScript now knows userId is string
19
+ * await purchase(userId);
20
+ * }
21
+ */
22
+ export function requireAuthentication(
23
+ userId: string | null | undefined,
24
+ errorMessage = "User not authenticated"
25
+ ): asserts userId is string {
26
+ if (!isAuthenticated(userId)) {
27
+ throw new Error(errorMessage);
28
+ }
29
+ }
@@ -1,5 +1,5 @@
1
- const DAYS_REMAINING_WARNING_THRESHOLD = 7;
1
+ import { EXPIRATION_WARNING_THRESHOLD_DAYS } from '../constants/thresholds';
2
2
 
3
3
  export function shouldHighlightExpiration(daysRemaining: number | null | undefined): boolean {
4
- return daysRemaining !== null && daysRemaining !== undefined && daysRemaining > 0 && daysRemaining <= DAYS_REMAINING_WARNING_THRESHOLD;
4
+ return daysRemaining !== null && daysRemaining !== undefined && daysRemaining > 0 && daysRemaining <= EXPIRATION_WARNING_THRESHOLD_DAYS;
5
5
  }
@@ -12,7 +12,7 @@ export class TrialEligibilityService {
12
12
 
13
13
  const { hasUsedTrial, trialInProgress, userIds = [] } = record;
14
14
 
15
- if (userId && userIds.includes(userId)) {
15
+ if (userId && userId.length > 0 && userIds.includes(userId)) {
16
16
  return { eligible: false, reason: "user_already_used", deviceId };
17
17
  }
18
18
 
@@ -26,9 +26,17 @@ const repository = new DeviceTrialRepository();
26
26
 
27
27
  export const getDeviceId = () => PersistentDeviceIdService.getDeviceId();
28
28
 
29
+ /**
30
+ * Ensures a valid device ID is available
31
+ * Uses provided deviceId if non-empty, otherwise fetches from PersistentDeviceIdService
32
+ */
33
+ async function ensureDeviceId(deviceId?: string): Promise<string> {
34
+ return (deviceId && deviceId.length > 0) ? deviceId : await getDeviceId();
35
+ }
36
+
29
37
  export async function checkTrialEligibility(userId?: string, deviceId?: string): Promise<TrialEligibilityResult> {
30
38
  try {
31
- const id = deviceId || await getDeviceId();
39
+ const id = await ensureDeviceId(deviceId);
32
40
  const record = await repository.getRecord(id);
33
41
  return TrialEligibilityService.check(userId, id, record);
34
42
  } catch {
@@ -38,7 +46,7 @@ export async function checkTrialEligibility(userId?: string, deviceId?: string):
38
46
 
39
47
  export async function recordTrialStart(userId: string, deviceId?: string): Promise<boolean> {
40
48
  try {
41
- const id = deviceId || await getDeviceId();
49
+ const id = await ensureDeviceId(deviceId);
42
50
  const record: TrialRecordWrite = {
43
51
  deviceId: id,
44
52
  trialInProgress: true,
@@ -54,7 +62,7 @@ export async function recordTrialStart(userId: string, deviceId?: string): Promi
54
62
 
55
63
  export async function recordTrialEnd(deviceId?: string): Promise<boolean> {
56
64
  try {
57
- const id = deviceId || await getDeviceId();
65
+ const id = await ensureDeviceId(deviceId);
58
66
  const record: TrialRecordWrite = {
59
67
  hasUsedTrial: true,
60
68
  trialInProgress: false,
@@ -68,7 +76,7 @@ export async function recordTrialEnd(deviceId?: string): Promise<boolean> {
68
76
 
69
77
  export async function recordTrialConversion(deviceId?: string): Promise<boolean> {
70
78
  try {
71
- const id = deviceId || await getDeviceId();
79
+ const id = await ensureDeviceId(deviceId);
72
80
  const record: TrialRecordWrite = {
73
81
  hasUsedTrial: true,
74
82
  trialInProgress: false,
@@ -34,7 +34,7 @@ export async function addTransaction(
34
34
  change,
35
35
  reason,
36
36
  ...metadata,
37
- createdAt: Date.now(),
37
+ createdAt: Date.now(), // Approximate - actual server timestamp written to Firestore
38
38
  },
39
39
  };
40
40
  } catch (error) {
@@ -1,5 +1,6 @@
1
1
  import { useQuery } from "@umituz/react-native-design-system";
2
2
  import { useMemo } from "react";
3
+ import { NO_CACHE_QUERY_CONFIG } from "../../../../shared/infrastructure/react-query/queryConfig";
3
4
  import type {
4
5
  ProductMetadata,
5
6
  ProductMetadataConfig,
@@ -50,11 +51,7 @@ export function useProductMetadata({
50
51
  return service.getAll();
51
52
  },
52
53
  enabled,
53
- gcTime: 0,
54
- staleTime: 0,
55
- refetchOnMount: "always",
56
- refetchOnWindowFocus: "always",
57
- refetchOnReconnect: "always",
54
+ ...NO_CACHE_QUERY_CONFIG,
58
55
  });
59
56
 
60
57
  const products = data ?? [];
@@ -1,6 +1,7 @@
1
1
  import { useQuery } from "@umituz/react-native-design-system";
2
2
  import { useMemo } from "react";
3
3
  import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
4
+ import { NO_CACHE_QUERY_CONFIG } from "../../../../shared/infrastructure/react-query/queryConfig";
4
5
  import type {
5
6
  CreditLog,
6
7
  TransactionRepositoryConfig,
@@ -53,11 +54,7 @@ export function useTransactionHistory({
53
54
  return result.data ?? [];
54
55
  },
55
56
  enabled: !!userId,
56
- gcTime: 0,
57
- staleTime: 0,
58
- refetchOnMount: "always",
59
- refetchOnWindowFocus: "always",
60
- refetchOnReconnect: "always",
57
+ ...NO_CACHE_QUERY_CONFIG,
61
58
  });
62
59
 
63
60
  const transactions = data ?? [];
@@ -0,0 +1,39 @@
1
+ /**
2
+ * User Cache Cleanup Hook
3
+ * Automatically cleans up previous user's query cache when userId changes
4
+ */
5
+
6
+ import { useEffect, useRef } from "react";
7
+ import type { QueryClient } from "@umituz/react-native-design-system";
8
+ import { isAuthenticated } from "../../../../domains/subscription/utils/authGuards";
9
+
10
+ /**
11
+ * Cleans up previous user's cache when userId changes (logout or user switch)
12
+ * Prevents data leakage between users
13
+ *
14
+ * @param userId - Current user ID
15
+ * @param queryClient - TanStack Query client
16
+ * @param queryKey - Query key factory function that takes userId
17
+ *
18
+ * @example
19
+ * usePreviousUserCleanup(userId, queryClient, (id) => creditsQueryKeys.user(id));
20
+ */
21
+ export function usePreviousUserCleanup(
22
+ userId: string | null | undefined,
23
+ queryClient: QueryClient,
24
+ queryKey: (userId: string) => readonly unknown[]
25
+ ): void {
26
+ const prevUserIdRef = useRef(userId);
27
+
28
+ useEffect(() => {
29
+ const prevUserId = prevUserIdRef.current;
30
+ prevUserIdRef.current = userId;
31
+
32
+ // Clear previous user's cache when userId changes (logout or user switch)
33
+ if (prevUserId !== userId && isAuthenticated(prevUserId)) {
34
+ queryClient.removeQueries({
35
+ queryKey: queryKey(prevUserId),
36
+ });
37
+ }
38
+ }, [userId, queryClient, queryKey]);
39
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared TanStack Query Configuration
3
+ * Common query configurations to ensure consistency across hooks
4
+ */
5
+
6
+ /**
7
+ * Configuration for queries that should never cache
8
+ * Used for real-time sensitive data (subscriptions, credits, transactions)
9
+ *
10
+ * - gcTime: 0 - Don't keep unused data in memory
11
+ * - staleTime: 0 - Always consider data stale
12
+ * - refetchOnMount: "always" - Always refetch when component mounts
13
+ * - refetchOnWindowFocus: "always" - Always refetch when window regains focus
14
+ * - refetchOnReconnect: "always" - Always refetch when reconnecting
15
+ */
16
+ export const NO_CACHE_QUERY_CONFIG = {
17
+ gcTime: 0,
18
+ staleTime: 0,
19
+ refetchOnMount: "always" as const,
20
+ refetchOnWindowFocus: "always" as const,
21
+ refetchOnReconnect: "always" as const,
22
+ };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Query Invalidation Utilities
3
+ * Centralized functions for invalidating multiple related queries
4
+ */
5
+
6
+ import type { QueryClient } from "@umituz/react-native-design-system";
7
+ import { creditsQueryKeys } from "../../../domains/credits/presentation/creditsQueryKeys";
8
+ import { subscriptionStatusQueryKeys } from "../../../domains/subscription/presentation/useSubscriptionStatus";
9
+
10
+ // Subscription packages query key
11
+ export const SUBSCRIPTION_QUERY_KEYS = {
12
+ packages: ["subscription", "packages"] as const,
13
+ };
14
+
15
+ /**
16
+ * Invalidates all subscription-related queries
17
+ * Use after purchases, restores, or subscription changes
18
+ *
19
+ * @param queryClient - TanStack Query client
20
+ * @param userId - Optional user ID to invalidate user-specific queries
21
+ *
22
+ * @example
23
+ * // After successful purchase
24
+ * await invalidateSubscriptionQueries(queryClient, userId);
25
+ */
26
+ export async function invalidateSubscriptionQueries(
27
+ queryClient: QueryClient,
28
+ userId?: string | null
29
+ ): Promise<void> {
30
+ // Invalidate packages (affects all users)
31
+ await queryClient.invalidateQueries({
32
+ queryKey: SUBSCRIPTION_QUERY_KEYS.packages,
33
+ });
34
+
35
+ // Invalidate user-specific queries if userId provided
36
+ if (userId) {
37
+ await Promise.all([
38
+ queryClient.invalidateQueries({
39
+ queryKey: subscriptionStatusQueryKeys.user(userId),
40
+ }),
41
+ queryClient.invalidateQueries({
42
+ queryKey: creditsQueryKeys.user(userId),
43
+ }),
44
+ ]);
45
+ }
46
+ }
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { useState, useCallback, useRef, useEffect } from "react";
7
+ import { normalizeError } from "../../utils/errorUtils";
7
8
 
8
9
  export interface ServiceCallState<T> {
9
10
  data: T | null;
@@ -54,7 +55,7 @@ export function useServiceCall<T>(
54
55
  setState({ data, isLoading: false, error: null });
55
56
  onSuccessRef.current?.(data);
56
57
  } catch (error) {
57
- const errorObj = error instanceof Error ? error : new Error("Service call failed");
58
+ const errorObj = normalizeError(error, "Service call failed");
58
59
  setState({ data: null, isLoading: false, error: errorObj });
59
60
  onErrorRef.current?.(errorObj);
60
61
  } finally {
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Error Utilities
3
+ * Common error handling and normalization functions
4
+ */
5
+
6
+ /**
7
+ * Normalizes unknown error types to Error objects
8
+ * Useful for catch blocks where error type is unknown
9
+ *
10
+ * @param error - The error to normalize (unknown type)
11
+ * @param fallbackMessage - Message to use if error is not an Error object
12
+ * @returns Always returns an Error object
13
+ *
14
+ * @example
15
+ * try {
16
+ * await someOperation();
17
+ * } catch (error) {
18
+ * const err = normalizeError(error, "Operation failed");
19
+ * console.error(err.message);
20
+ * }
21
+ */
22
+ export function normalizeError(
23
+ error: unknown,
24
+ fallbackMessage = "Unknown error"
25
+ ): Error {
26
+ if (error instanceof Error) {
27
+ return error;
28
+ }
29
+
30
+ const message = typeof error === "string" ? error : String(error);
31
+ return new Error(message || fallbackMessage);
32
+ }
@@ -4,6 +4,12 @@
4
4
  import { Platform } from "react-native";
5
5
  import Constants from "expo-constants";
6
6
 
7
+ /**
8
+ * Development mode flag
9
+ * Safe check for __DEV__ that works in all environments
10
+ */
11
+ export const IS_DEV_MODE = typeof __DEV__ !== "undefined" && __DEV__;
12
+
7
13
  /**
8
14
  * Gets the current app version from Expo constants
9
15
  */
@@ -1 +0,0 @@
1
- export const DAYS_REMAINING_WARNING_THRESHOLD = 7;
@@ -1 +0,0 @@
1
- export const EXPIRING_SOON_THRESHOLD_DAYS = 7;