@umituz/react-native-subscription 2.27.150 → 2.27.154

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.150",
3
+ "version": "2.27.154",
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,9 @@
1
- import { Timestamp, serverTimestamp } from "firebase/firestore";
1
+ import { serverTimestamp } from "firebase/firestore";
2
2
  import { resolveSubscriptionStatus } from "../../subscription/core/SubscriptionStatus";
3
3
  import { creditAllocationOrchestrator } from "./credit-strategies/CreditAllocationOrchestrator";
4
4
  import { isPast } from "../../../utils/dateUtils";
5
5
  import { isCreditPackage } from "../../../utils/packageTypeDetector";
6
+ import { toTimestamp } from "../../../shared/utils/dateConverter";
6
7
  import {
7
8
  CalculateCreditsParams,
8
9
  BuildCreditsDataParams
@@ -61,7 +62,9 @@ export function buildCreditsData({
61
62
  platform,
62
63
  ...(purchaseHistory.length > 0 && { purchaseHistory }),
63
64
  ...(isPurchaseOrRenewal && { lastPurchaseAt: serverTimestamp() }),
64
- ...(metadata.expirationDate && { expirationDate: Timestamp.fromDate(new Date(metadata.expirationDate)) }),
65
+ ...(metadata.expirationDate && {
66
+ expirationDate: toTimestamp(metadata.expirationDate)
67
+ }),
65
68
  ...(metadata.willRenew !== undefined && { willRenew: metadata.willRenew }),
66
69
  ...(metadata.originalTransactionId && { originalTransactionId: metadata.originalTransactionId }),
67
70
  };
@@ -7,7 +7,7 @@ import { useCallback } from "react";
7
7
  import { useMutation, useQueryClient } from "@umituz/react-native-design-system";
8
8
  import type { UserCredits } from "../core/Credits";
9
9
  import { getCreditsRepository } from "../infrastructure/CreditsRepositoryManager";
10
- import { creditsQueryKeys } from "./useCredits";
10
+ import { creditsQueryKeys } from "./creditsQueryKeys";
11
11
  import { calculateRemaining } from "../../../shared/utils/numberUtils";
12
12
 
13
13
  import { timezoneService } from "@umituz/react-native-design-system";
@@ -1,4 +1,4 @@
1
- import { isDefined, isValidNumber, isNonNegative } from "../../../shared/utils/typeGuards";
1
+ import { isValidNumber, isNonNegative } from "../../../shared/utils/typeGuards";
2
2
 
3
3
  export const isValidBalance = (balance: number | null | undefined): balance is number => {
4
4
  return isValidNumber(balance) && isNonNegative(balance);
@@ -1,5 +1,6 @@
1
1
  import type { CustomerInfo } from "react-native-purchases";
2
2
  import { getPremiumEntitlement } from "../../core/RevenueCatTypes";
3
+ import { toDate } from "../../../../shared/utils/dateConverter";
3
4
 
4
5
  export interface PremiumStatus {
5
6
  isPremium: boolean;
@@ -13,9 +14,7 @@ export class PurchaseStatusResolver {
13
14
  if (entitlement) {
14
15
  return {
15
16
  isPremium: true,
16
- expirationDate: entitlement.expirationDate
17
- ? new Date(entitlement.expirationDate)
18
- : null,
17
+ expirationDate: toDate(entitlement.expirationDate),
19
18
  };
20
19
  }
21
20
 
@@ -15,7 +15,7 @@ import {
15
15
  import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionManager";
16
16
  import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
17
17
  import { subscriptionStatusQueryKeys } from "../../presentation/useSubscriptionStatus";
18
- import { creditsQueryKeys } from "../../../credits/presentation/useCredits";
18
+ import { creditsQueryKeys } from "../../../credits/presentation/creditsQueryKeys";
19
19
 
20
20
  /** Purchase mutation result - simplified for presentation layer */
21
21
  export interface PurchaseMutationResult {
@@ -13,7 +13,7 @@ import {
13
13
  import { SubscriptionManager } from "../../infrastructure/managers/SubscriptionManager";
14
14
  import { SUBSCRIPTION_QUERY_KEYS } from "./subscriptionQueryKeys";
15
15
  import { subscriptionStatusQueryKeys } from "../../presentation/useSubscriptionStatus";
16
- import { creditsQueryKeys } from "../../../credits/presentation/useCredits";
16
+ import { creditsQueryKeys } from "../../../credits/presentation/creditsQueryKeys";
17
17
 
18
18
  interface RestoreResult {
19
19
  success: boolean;
package/src/index.ts CHANGED
@@ -88,6 +88,7 @@ export * from "./utils/types";
88
88
  export * from "./utils/validation";
89
89
  export * from "./utils/dateUtils";
90
90
  export * from "./utils/appUtils";
91
+ export * from "./shared/utils/dateConverter";
91
92
 
92
93
  // Init Module Factory
93
94
  export {
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Date Converter Utilities
3
+ * Centralized date conversion and validation logic
4
+ * Handles Date objects, ISO strings, timestamps, and null values safely
5
+ */
6
+
7
+ /**
8
+ * Safely converts any date-like value to a Date object or null
9
+ * Handles: Date objects, ISO strings, timestamps, null, undefined, invalid values
10
+ */
11
+ export function toDate(value: Date | string | number | null | undefined): Date | null {
12
+ if (value === null || value === undefined) {
13
+ return null;
14
+ }
15
+
16
+ if (value instanceof Date) {
17
+ return isValidDate(value) ? value : null;
18
+ }
19
+
20
+ if (typeof value === 'string' || typeof value === 'number') {
21
+ const parsed = new Date(value);
22
+ return isValidDate(parsed) ? parsed : null;
23
+ }
24
+
25
+ return null;
26
+ }
27
+
28
+ /**
29
+ * Safely converts any date-like value to an ISO string or null
30
+ * Handles: Date objects, ISO strings, timestamps, null, undefined, invalid values
31
+ */
32
+ export function toISOString(value: Date | string | number | null | undefined): string | null {
33
+ if (value === null || value === undefined) {
34
+ return null;
35
+ }
36
+
37
+ // If already a string, validate it
38
+ if (typeof value === 'string') {
39
+ const parsed = new Date(value);
40
+ return isValidDate(parsed) ? value : null;
41
+ }
42
+
43
+ // Convert to Date and then to ISO string
44
+ const date = toDate(value);
45
+ return date ? date.toISOString() : null;
46
+ }
47
+
48
+ /**
49
+ * Checks if a Date object is valid (not Invalid Date)
50
+ * @internal - Use for internal validation only
51
+ */
52
+ function isValidDate(date: Date): boolean {
53
+ return date instanceof Date && !isNaN(date.getTime());
54
+ }
55
+
56
+ /**
57
+ * Safely converts a Date or string to a Firestore Timestamp
58
+ * Returns null if the value is invalid
59
+ */
60
+ export function toTimestamp(value: Date | string | number | null | undefined): any {
61
+ const date = toDate(value);
62
+ if (!date) return null;
63
+
64
+ // Lazy import to avoid circular dependencies
65
+ const { Timestamp } = require('firebase/firestore');
66
+ return Timestamp.fromDate(date);
67
+ }
68
+
69
+ /**
70
+ * Gets current date as ISO string
71
+ */
72
+ export function getCurrentISOString(): string {
73
+ return new Date().toISOString();
74
+ }
75
+
76
+ /**
77
+ * Gets current date as Date object
78
+ */
79
+ export function getCurrentDate(): Date {
80
+ return new Date();
81
+ }
82
+
83
+ /**
84
+ * Compares two dates and returns if first is before second
85
+ */
86
+ export function isBefore(date1: Date | string | null | undefined, date2: Date | string | null | undefined): boolean {
87
+ const d1 = toDate(date1);
88
+ const d2 = toDate(date2);
89
+
90
+ if (!d1 || !d2) return false;
91
+ return d1.getTime() < d2.getTime();
92
+ }
93
+
94
+ /**
95
+ * Compares two dates and returns if first is after second
96
+ */
97
+ export function isAfter(date1: Date | string | null | undefined, date2: Date | string | null | undefined): boolean {
98
+ const d1 = toDate(date1);
99
+ const d2 = toDate(date2);
100
+
101
+ if (!d1 || !d2) return false;
102
+ return d1.getTime() > d2.getTime();
103
+ }
104
+
105
+ /**
106
+ * Checks if a date is in the past
107
+ */
108
+ export function isInPast(date: Date | string | null | undefined): boolean {
109
+ const d = toDate(date);
110
+ if (!d) return false;
111
+ return d.getTime() < Date.now();
112
+ }
113
+
114
+ /**
115
+ * Checks if a date is in the future
116
+ */
117
+ export function isInFuture(date: Date | string | null | undefined): boolean {
118
+ const d = toDate(date);
119
+ if (!d) return false;
120
+ return d.getTime() > Date.now();
121
+ }