@umituz/react-native-subscription 2.35.7 → 2.35.9
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 +1 -1
- package/src/domains/subscription/{presentation/featureGateHelpers.ts → application/featureGate/featureGateBusinessRules.ts} +6 -6
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +5 -2
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.tsx +1 -1
- package/src/domains/subscription/presentation/useFeatureGate.ts +5 -5
- package/src/domains/subscription/utils/dateFormatters.ts +28 -0
- package/src/domains/subscription/utils/expirationHelpers.ts +5 -0
- package/src/domains/subscription/presentation/components/details/premiumDetailsHelpers.ts +0 -5
- package/src/domains/subscription/presentation/utils/subscriptionDateUtils.ts +0 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.35.
|
|
3
|
+
"version": "2.35.9",
|
|
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,31 +1,31 @@
|
|
|
1
1
|
export const DEFAULT_REQUIRED_CREDITS = 1;
|
|
2
2
|
|
|
3
|
-
export
|
|
3
|
+
export function canExecuteAuthAction(
|
|
4
4
|
isWaitingForAuthCredits: boolean,
|
|
5
5
|
isCreditsLoaded: boolean,
|
|
6
6
|
hasPendingAction: boolean,
|
|
7
7
|
hasSubscription: boolean,
|
|
8
8
|
creditBalance: number,
|
|
9
9
|
requiredCredits: number
|
|
10
|
-
): boolean
|
|
10
|
+
): boolean {
|
|
11
11
|
if (!isWaitingForAuthCredits || !isCreditsLoaded || !hasPendingAction) {
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
14
|
return hasSubscription || creditBalance >= requiredCredits;
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
export
|
|
17
|
+
export function canExecutePurchaseAction(
|
|
18
18
|
isWaitingForPurchase: boolean,
|
|
19
19
|
creditBalance: number,
|
|
20
20
|
prevBalance: number,
|
|
21
21
|
hasSubscription: boolean,
|
|
22
22
|
prevHasSubscription: boolean,
|
|
23
23
|
hasPendingAction: boolean
|
|
24
|
-
): boolean
|
|
24
|
+
): boolean {
|
|
25
25
|
if (!isWaitingForPurchase || !hasPendingAction) {
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
28
|
const creditsIncreased = creditBalance > prevBalance;
|
|
29
29
|
const subscriptionAcquired = hasSubscription && !prevHasSubscription;
|
|
30
30
|
return creditsIncreased || subscriptionAcquired;
|
|
31
|
-
}
|
|
31
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CustomerInfo } from "react-native-purchases";
|
|
2
2
|
import { getPremiumEntitlement } from "../../../revenuecat/core/types";
|
|
3
3
|
import { toDate } from "../../../../shared/utils/dateConverter";
|
|
4
|
+
import { detectPackageType } from "../../../../utils/packageTypeDetector";
|
|
4
5
|
|
|
5
6
|
export interface PremiumStatus {
|
|
6
7
|
isPremium: boolean;
|
|
@@ -23,17 +24,19 @@ export class PurchaseStatusResolver {
|
|
|
23
24
|
const entitlement = getPremiumEntitlement(customerInfo, entitlementId);
|
|
24
25
|
|
|
25
26
|
if (entitlement) {
|
|
27
|
+
const productIdentifier = entitlement.productIdentifier ?? null;
|
|
28
|
+
|
|
26
29
|
return {
|
|
27
30
|
isPremium: true,
|
|
28
31
|
expirationDate: toDate(entitlement.expirationDate),
|
|
29
32
|
willRenew: entitlement.willRenew ?? false,
|
|
30
|
-
productIdentifier
|
|
33
|
+
productIdentifier,
|
|
31
34
|
originalPurchaseDate: toDate(entitlement.originalPurchaseDate) ?? null,
|
|
32
35
|
latestPurchaseDate: toDate(entitlement.latestPurchaseDate) ?? null,
|
|
33
36
|
billingIssuesDetected: entitlement.billingIssueDetectedAt !== null && entitlement.billingIssueDetectedAt !== undefined,
|
|
34
37
|
isSandbox: entitlement.isSandbox ?? false,
|
|
35
38
|
periodType: entitlement.periodType ?? null,
|
|
36
|
-
packageType: null,
|
|
39
|
+
packageType: productIdentifier ? detectPackageType(productIdentifier) : null,
|
|
37
40
|
store: null,
|
|
38
41
|
gracePeriodExpiresDate: null,
|
|
39
42
|
unsubscribeDetectedAt: toDate(entitlement.unsubscribeDetectedAt) ?? null,
|
|
@@ -7,7 +7,7 @@ import { styles } from "./PremiumDetailsCard.styles";
|
|
|
7
7
|
import type { PremiumDetailsCardProps } from "./PremiumDetailsCardTypes";
|
|
8
8
|
import { PremiumDetailsCardHeader } from "./PremiumDetailsCardHeader";
|
|
9
9
|
import { PremiumDetailsCardActions } from "./PremiumDetailsCardActions";
|
|
10
|
-
import { shouldHighlightExpiration } from "
|
|
10
|
+
import { shouldHighlightExpiration } from "../../../../subscription/utils/expirationHelpers";
|
|
11
11
|
|
|
12
12
|
export type { CreditInfo, PremiumDetailsTranslations, PremiumDetailsCardProps } from "./PremiumDetailsCardTypes";
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useRef, useEffect } from "react";
|
|
2
2
|
import type { UseFeatureGateParams, UseFeatureGateResult } from "./useFeatureGate.types";
|
|
3
|
-
import { DEFAULT_REQUIRED_CREDITS,
|
|
3
|
+
import { DEFAULT_REQUIRED_CREDITS, canExecuteAuthAction, canExecutePurchaseAction } from "../application/featureGate/featureGateBusinessRules";
|
|
4
4
|
import { useSyncedRefs } from "./featureGateRefs";
|
|
5
5
|
import { executeFeatureAction } from "./featureGateActions";
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const shouldExecute =
|
|
41
|
+
const shouldExecute = canExecuteAuthAction(
|
|
42
42
|
isWaitingForAuthCreditsRef.current,
|
|
43
43
|
isCreditsLoaded,
|
|
44
44
|
!!pendingActionRef.current,
|
|
@@ -48,7 +48,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
48
48
|
);
|
|
49
49
|
|
|
50
50
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
51
|
-
console.log("[FeatureGate]
|
|
51
|
+
console.log("[FeatureGate] canExecuteAuthAction:", shouldExecute);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
if (shouldExecute) {
|
|
@@ -84,7 +84,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const shouldExecute =
|
|
87
|
+
const shouldExecute = canExecutePurchaseAction(
|
|
88
88
|
isWaitingForPurchaseRef.current,
|
|
89
89
|
creditBalance,
|
|
90
90
|
prevCreditBalanceRef.current ?? 0,
|
|
@@ -94,7 +94,7 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
94
94
|
);
|
|
95
95
|
|
|
96
96
|
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
97
|
-
console.log("[FeatureGate]
|
|
97
|
+
console.log("[FeatureGate] canExecutePurchaseAction:", shouldExecute);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (shouldExecute) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { timezoneService } from "@umituz/react-native-design-system";
|
|
2
|
+
|
|
3
|
+
export function convertFirestoreTimestampToISO(timestamp: unknown): string | null {
|
|
4
|
+
if (!timestamp) return null;
|
|
5
|
+
|
|
6
|
+
let date: Date;
|
|
7
|
+
if (
|
|
8
|
+
typeof timestamp === "object" &&
|
|
9
|
+
timestamp !== null &&
|
|
10
|
+
"toDate" in timestamp
|
|
11
|
+
) {
|
|
12
|
+
date = (timestamp as { toDate: () => Date }).toDate();
|
|
13
|
+
} else if (timestamp instanceof Date) {
|
|
14
|
+
date = timestamp;
|
|
15
|
+
} else {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return timezoneService.formatToISOString(date);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatDateForDisplay(dateStr: string | null): string | null {
|
|
23
|
+
if (!dateStr) return null;
|
|
24
|
+
const date = new Date(dateStr);
|
|
25
|
+
if (isNaN(date.getTime())) return null;
|
|
26
|
+
|
|
27
|
+
return timezoneService.formatToDisplayDate(date);
|
|
28
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const DAYS_REMAINING_WARNING_THRESHOLD = 7;
|
|
2
|
+
|
|
3
|
+
export function shouldHighlightExpiration(daysRemaining: number | null | undefined): boolean {
|
|
4
|
+
return daysRemaining !== null && daysRemaining !== undefined && daysRemaining > 0 && daysRemaining <= DAYS_REMAINING_WARNING_THRESHOLD;
|
|
5
|
+
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { DAYS_REMAINING_WARNING_THRESHOLD } from "./PremiumDetailsCard.constants";
|
|
2
|
-
|
|
3
|
-
export const shouldHighlightExpiration = (daysRemaining: number | null | undefined): boolean => {
|
|
4
|
-
return daysRemaining !== null && daysRemaining !== undefined && daysRemaining > 0 && daysRemaining <= DAYS_REMAINING_WARNING_THRESHOLD;
|
|
5
|
-
};
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { timezoneService } from "@umituz/react-native-design-system";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Converts Firestore timestamp or Date to ISO string
|
|
5
|
-
*/
|
|
6
|
-
export const convertPurchasedAt = (purchasedAt: unknown): string | null => {
|
|
7
|
-
if (!purchasedAt) return null;
|
|
8
|
-
|
|
9
|
-
let date: Date;
|
|
10
|
-
if (
|
|
11
|
-
typeof purchasedAt === "object" &&
|
|
12
|
-
purchasedAt !== null &&
|
|
13
|
-
"toDate" in purchasedAt
|
|
14
|
-
) {
|
|
15
|
-
date = (purchasedAt as { toDate: () => Date }).toDate();
|
|
16
|
-
} else if (purchasedAt instanceof Date) {
|
|
17
|
-
date = purchasedAt;
|
|
18
|
-
} else {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return timezoneService.formatToISOString(date);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Formats a date string to a simple DD.MM.YYYY format using timezoneService
|
|
27
|
-
*/
|
|
28
|
-
export const formatDate = (dateStr: string | null): string | null => {
|
|
29
|
-
if (!dateStr) return null;
|
|
30
|
-
const date = new Date(dateStr);
|
|
31
|
-
if (isNaN(date.getTime())) return null;
|
|
32
|
-
|
|
33
|
-
return timezoneService.formatToDisplayDate(date);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
|