@umituz/react-native-subscription 2.35.6 → 2.35.8
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/presentation/components/details/PremiumDetailsCard.tsx +1 -1
- package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx +3 -15
- 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/utils/packageTypeFormatter.ts +10 -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.8",
|
|
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
|
+
}
|
|
@@ -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
|
|
package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx
CHANGED
|
@@ -2,22 +2,10 @@ import React from "react";
|
|
|
2
2
|
import { View } from "react-native";
|
|
3
3
|
import { DetailRow } from "../../components/details/DetailRow";
|
|
4
4
|
import type { SubscriptionHeaderProps } from "./SubscriptionHeader.types";
|
|
5
|
+
import { formatPackageTypeForDisplay } from "../../../../subscription/utils/packageTypeFormatter";
|
|
5
6
|
|
|
6
7
|
declare const __DEV__: boolean;
|
|
7
8
|
|
|
8
|
-
function formatSubscriptionPeriod(packageType: string | null | undefined, periodType: string | null | undefined): string {
|
|
9
|
-
if (packageType) {
|
|
10
|
-
const formatted = packageType.toLowerCase().replace(/_/g, ' ');
|
|
11
|
-
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (periodType === "NORMAL") {
|
|
15
|
-
return "Standard";
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return periodType || "Unknown";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
9
|
interface SubscriptionHeaderContentProps {
|
|
22
10
|
isLifetime: boolean;
|
|
23
11
|
showExpirationDate: boolean;
|
|
@@ -93,10 +81,10 @@ export const SubscriptionHeaderContent: React.FC<SubscriptionHeaderContentProps>
|
|
|
93
81
|
valueStyle={styles.value}
|
|
94
82
|
/>
|
|
95
83
|
)}
|
|
96
|
-
{
|
|
84
|
+
{packageType && translations.periodTypeLabel && (
|
|
97
85
|
<DetailRow
|
|
98
86
|
label={translations.periodTypeLabel}
|
|
99
|
-
value={
|
|
87
|
+
value={formatPackageTypeForDisplay(packageType)}
|
|
100
88
|
style={styles.row}
|
|
101
89
|
labelStyle={styles.label}
|
|
102
90
|
valueStyle={styles.value}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PackageType } from "../../revenuecat/core/types";
|
|
2
|
+
|
|
3
|
+
export function formatPackageTypeForDisplay(packageType: PackageType | string | null | undefined): string {
|
|
4
|
+
if (!packageType) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const formatted = packageType.toLowerCase().replace(/_/g, " ");
|
|
9
|
+
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
|
10
|
+
}
|
|
@@ -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
|
-
|