@umituz/react-native-subscription 2.37.12 → 2.37.14
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/credits/application/PurchaseMetadataGenerator.ts +1 -1
- package/src/domains/credits/application/credit-strategies/CreditAllocationOrchestrator.ts +1 -1
- package/src/domains/credits/application/creditDocumentHelpers.ts +0 -14
- package/src/domains/credits/presentation/deduct-credit/index.ts +0 -1
- package/src/domains/credits/presentation/useCredits.ts +1 -6
- package/src/domains/credits/utils/creditValidation.ts +3 -3
- package/src/domains/paywall/components/PaywallFeatures.tsx +1 -1
- package/src/domains/paywall/hooks/usePaywallActions.ts +1 -1
- package/src/domains/revenuecat/core/errors/RevenueCatError.ts +1 -14
- package/src/domains/revenuecat/core/errors/RevenueCatErrorHandler.ts +1 -1
- package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +3 -3
- package/src/domains/revenuecat/infrastructure/services/ConfigurationStateManager.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.ts +0 -2
- package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +9 -0
- package/src/domains/subscription/application/initializer/index.ts +1 -1
- package/src/domains/subscription/core/SubscriptionStatus.ts +4 -10
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +1 -1
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +0 -2
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +1 -2
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -4
- package/src/domains/subscription/infrastructure/services/OfferingsFetcher.ts +1 -1
- package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -2
- package/src/domains/subscription/infrastructure/services/revenueCatServiceInstance.ts +0 -4
- package/src/domains/subscription/infrastructure/utils/renewal/index.ts +1 -1
- package/src/domains/subscription/presentation/components/details/CreditRow.tsx +1 -1
- package/src/domains/subscription/presentation/components/details/DetailRow.tsx +1 -1
- package/src/domains/subscription/presentation/components/feedback/FeedbackOption.tsx +0 -2
- package/src/domains/subscription/presentation/components/feedback/FeedbackTextInput.tsx +1 -1
- package/src/domains/subscription/presentation/featureGateRefs.ts +1 -1
- package/src/domains/subscription/presentation/screens/components/CreditsList.tsx +2 -2
- package/src/domains/subscription/presentation/screens/components/SubscriptionHeader.tsx +0 -2
- package/src/domains/subscription/presentation/stores/index.ts +0 -5
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +3 -7
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +2 -8
- package/src/domains/subscription/presentation/useFeatureGate.ts +0 -2
- package/src/domains/subscription/presentation/usePaywallVisibility.ts +1 -1
- package/src/domains/subscription/utils/authGuards.ts +0 -23
- package/src/domains/subscription/utils/syncStatus.ts +1 -1
- package/src/domains/wallet/index.ts +0 -112
- package/src/domains/wallet/infrastructure/config/walletConfig.ts +1 -23
- package/src/domains/wallet/infrastructure/repositories/transaction/CollectionBuilder.ts +1 -1
- package/src/domains/wallet/infrastructure/repositories/transaction/index.ts +0 -9
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +1 -1
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +0 -2
- package/src/domains/wallet/presentation/components/TransactionList.tsx +3 -2
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +2 -2
- package/src/domains/wallet/presentation/hooks/useWallet.ts +2 -2
- package/src/shared/application/FeedbackService.ts +2 -2
- package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -1
- package/src/shared/infrastructure/firestore/collectionUtils.ts +0 -7
- package/src/shared/infrastructure/firestore/resultUtils.ts +3 -39
- package/src/shared/presentation/layouts/ScreenLayout.tsx +1 -1
- package/src/shared/utils/numberUtils.core.ts +0 -27
- package/src/shared/utils/numberUtils.ts +1 -9
- package/src/shared/utils/validators.ts +0 -58
- package/src/domains/config/domain/entities/Plan.ts +0 -44
- package/src/domains/config/domain/index.ts +0 -2
- package/src/domains/config/domain/value-objects/Config.ts +0 -49
- package/src/domains/config/index.ts +0 -6
- package/src/domains/config/utils/planSelectors.ts +0 -56
- package/src/domains/paywall/components/FeatureItem.tsx +0 -50
- package/src/domains/paywall/components/FeatureList.tsx +0 -34
- package/src/domains/paywall/components/PaywallHeader.tsx +0 -112
- package/src/domains/paywall/hooks/usePaywallTranslations.ts +0 -78
- package/src/domains/paywall/hooks/useSubscriptionModal.ts +0 -45
- package/src/domains/paywall/index.ts +0 -13
- package/src/domains/revenuecat/core/constants/RevenueCatConstants.ts +0 -201
- package/src/domains/revenuecat/core/constants/index.ts +0 -5
- package/src/domains/revenuecat/core/customerInfoHelpers.ts +0 -21
- package/src/domains/revenuecat/core/index.ts +0 -7
- package/src/domains/revenuecat/index.ts +0 -7
- package/src/domains/revenuecat/infrastructure/index.ts +0 -5
- package/src/domains/revenuecat/infrastructure/services/index.ts +0 -7
- package/src/domains/subscription/infrastructure/hooks/customer-info/index.ts +0 -2
- package/src/domains/subscription/infrastructure/hooks/customer-info/types.ts +0 -9
- package/src/domains/subscription/infrastructure/hooks/customer-info/useCustomerInfo.ts +0 -52
- package/src/domains/subscription/infrastructure/hooks/useInitializeSubscription.ts +0 -30
- package/src/domains/subscription/infrastructure/hooks/usePaywallFlow.ts +0 -78
- package/src/domains/subscription/infrastructure/hooks/useRevenueCat.ts +0 -119
- package/src/domains/subscription/presentation/components/feedback/FeedbackConstants.ts +0 -6
- package/src/domains/subscription/presentation/screens/components/SubscriptionActions.tsx +0 -56
- package/src/domains/subscription/utils/dateFormatters.ts +0 -28
- package/src/domains/wallet/domain/entities/CreditCost.ts +0 -45
- package/src/domains/wallet/domain/entities/README.md +0 -41
- package/src/domains/wallet/domain/errors/README.md +0 -40
- package/src/domains/wallet/domain/errors/WalletError.ts +0 -17
- package/src/domains/wallet/domain/errors/WalletError.types.ts +0 -30
- package/src/domains/wallet/domain/errors/WalletErrorClasses.ts +0 -82
- package/src/domains/wallet/domain/errors/WalletErrorFactory.ts +0 -24
- package/src/domains/wallet/domain/errors/WalletErrorMessages.ts +0 -17
- package/src/domains/wallet/domain/types/credit-cost.types.ts +0 -86
- package/src/domains/wallet/domain/types/index.ts +0 -33
- package/src/domains/wallet/domain/types/wallet.types.ts +0 -50
- package/src/domains/wallet/infrastructure/services/product-metadata/CacheManager.ts +0 -30
- package/src/domains/wallet/infrastructure/services/product-metadata/FirebaseFetcher.ts +0 -17
- package/src/domains/wallet/infrastructure/services/product-metadata/ProductMetadataService.ts +0 -57
- package/src/domains/wallet/infrastructure/services/product-metadata/ServiceManager.ts +0 -29
- package/src/domains/wallet/infrastructure/services/product-metadata/index.ts +0 -7
- package/src/domains/wallet/presentation/hooks/index.ts +0 -8
- package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +0 -72
- package/src/domains/wallet/utils/index.ts +0 -1
- package/src/shared/application/ActivationHandler.ts +0 -108
- package/src/shared/application/ports/ISubscriptionService.ts +0 -27
- package/src/shared/infrastructure/index.ts +0 -6
- package/src/shared/infrastructure/react-query/queryInvalidation.ts +0 -46
- package/src/shared/presentation/hooks/index.ts +0 -6
- package/src/shared/presentation/hooks/useAsyncState.ts +0 -72
- package/src/shared/presentation/hooks/useServiceCall.ts +0 -77
- package/src/shared/types/ReactTypes.ts +0 -80
- package/src/shared/utils/InsufficientCreditsError.ts +0 -32
- package/src/shared/utils/Logger.ts +0 -81
- package/src/shared/utils/SubscriptionConfig.ts +0 -15
- package/src/shared/utils/SubscriptionError.ts +0 -47
- package/src/shared/utils/appValidators.ts +0 -38
- package/src/shared/utils/arrayUtils.core.ts +0 -81
- package/src/shared/utils/arrayUtils.query.ts +0 -118
- package/src/shared/utils/arrayUtils.transforms.ts +0 -116
- package/src/shared/utils/arrayUtils.ts +0 -19
- package/src/shared/utils/errorUtils.ts +0 -32
- package/src/shared/utils/index.ts +0 -14
- package/src/shared/utils/numberUtils.aggregate.ts +0 -35
- package/src/shared/utils/numberUtils.format.ts +0 -42
- package/src/shared/utils/numberUtils.math.ts +0 -48
- package/src/shared/utils/queryKeyFactory.ts +0 -9
- package/src/shared/utils/stringUtils.case.ts +0 -64
- package/src/shared/utils/stringUtils.check.ts +0 -65
- package/src/shared/utils/stringUtils.format.ts +0 -84
- package/src/shared/utils/stringUtils.generate.ts +0 -47
- package/src/shared/utils/stringUtils.modify.ts +0 -67
- package/src/shared/utils/stringUtils.ts +0 -10
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature Item
|
|
3
|
-
* Single feature row with icon
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
8
|
-
import { AtomicText, AtomicIcon, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
10
|
-
interface FeatureItemProps {
|
|
11
|
-
icon: string;
|
|
12
|
-
text: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const FeatureItem: React.FC<FeatureItemProps> = React.memo(({ icon, text }) => {
|
|
16
|
-
const tokens = useAppDesignTokens();
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<View style={styles.container}>
|
|
20
|
-
<View style={[styles.iconContainer, { backgroundColor: tokens.colors.primaryLight }]}>
|
|
21
|
-
<AtomicIcon
|
|
22
|
-
name={icon || "checkmark-circle"}
|
|
23
|
-
customSize={16}
|
|
24
|
-
customColor={tokens.colors.primary}
|
|
25
|
-
/>
|
|
26
|
-
</View>
|
|
27
|
-
<AtomicText type="bodyMedium" style={{ color: tokens.colors.textPrimary, flex: 1 }}>
|
|
28
|
-
{text}
|
|
29
|
-
</AtomicText>
|
|
30
|
-
</View>
|
|
31
|
-
);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
FeatureItem.displayName = "FeatureItem";
|
|
35
|
-
|
|
36
|
-
const styles = StyleSheet.create({
|
|
37
|
-
container: {
|
|
38
|
-
flexDirection: "row",
|
|
39
|
-
alignItems: "center",
|
|
40
|
-
marginBottom: 12,
|
|
41
|
-
},
|
|
42
|
-
iconContainer: {
|
|
43
|
-
width: 28,
|
|
44
|
-
height: 28,
|
|
45
|
-
borderRadius: 14,
|
|
46
|
-
justifyContent: "center",
|
|
47
|
-
alignItems: "center",
|
|
48
|
-
marginRight: 12,
|
|
49
|
-
},
|
|
50
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature List
|
|
3
|
-
* List of premium features
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View, StyleSheet } from "react-native";
|
|
8
|
-
import { FeatureItem } from "./FeatureItem";
|
|
9
|
-
import type { SubscriptionFeature } from '../entities/types';
|
|
10
|
-
|
|
11
|
-
interface FeatureListProps {
|
|
12
|
-
features: SubscriptionFeature[];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const FeatureList: React.FC<FeatureListProps> = React.memo(({ features }) => {
|
|
16
|
-
if (features.length === 0) return null;
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<View style={styles.container}>
|
|
20
|
-
{features.map((feature, index) => (
|
|
21
|
-
<FeatureItem key={`${feature.icon}-${index}`} icon={feature.icon} text={feature.text} />
|
|
22
|
-
))}
|
|
23
|
-
</View>
|
|
24
|
-
);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
FeatureList.displayName = "FeatureList";
|
|
28
|
-
|
|
29
|
-
const styles = StyleSheet.create({
|
|
30
|
-
container: {
|
|
31
|
-
paddingHorizontal: 24,
|
|
32
|
-
marginBottom: 20,
|
|
33
|
-
},
|
|
34
|
-
});
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paywall Header
|
|
3
|
-
* Header with background, close button, title and subtitle
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
AtomicText,
|
|
11
|
-
AtomicIcon,
|
|
12
|
-
useDesignSystemTheme,
|
|
13
|
-
useAppDesignTokens,
|
|
14
|
-
} from "@umituz/react-native-design-system";
|
|
15
|
-
|
|
16
|
-
interface PaywallHeaderProps {
|
|
17
|
-
title: string;
|
|
18
|
-
subtitle?: string;
|
|
19
|
-
onClose: () => void;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const PaywallHeader: React.FC<PaywallHeaderProps> = React.memo(
|
|
23
|
-
({ title, subtitle, onClose }) => {
|
|
24
|
-
const tokens = useAppDesignTokens();
|
|
25
|
-
const { themeMode } = useDesignSystemTheme();
|
|
26
|
-
const isDark = themeMode === "dark";
|
|
27
|
-
|
|
28
|
-
const backgroundColor = isDark
|
|
29
|
-
? tokens.colors.surface
|
|
30
|
-
: tokens.colors.primary;
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<View
|
|
34
|
-
style={[styles.container, { backgroundColor }]}
|
|
35
|
-
>
|
|
36
|
-
<TouchableOpacity
|
|
37
|
-
onPress={onClose}
|
|
38
|
-
style={[styles.closeButton, { backgroundColor: tokens.colors.onPrimary }]}
|
|
39
|
-
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
40
|
-
>
|
|
41
|
-
<AtomicIcon
|
|
42
|
-
name="close-outline"
|
|
43
|
-
size="md"
|
|
44
|
-
customColor={isDark ? tokens.colors.textPrimary : tokens.colors.primary}
|
|
45
|
-
/>
|
|
46
|
-
</TouchableOpacity>
|
|
47
|
-
|
|
48
|
-
<View style={styles.content}>
|
|
49
|
-
<AtomicText
|
|
50
|
-
type="headlineLarge"
|
|
51
|
-
style={[styles.title, { color: tokens.colors.onPrimary }]}
|
|
52
|
-
>
|
|
53
|
-
{title}
|
|
54
|
-
</AtomicText>
|
|
55
|
-
{subtitle && (
|
|
56
|
-
<AtomicText
|
|
57
|
-
type="bodyLarge"
|
|
58
|
-
style={[styles.subtitle, { color: tokens.colors.onPrimary }]}
|
|
59
|
-
>
|
|
60
|
-
{subtitle}
|
|
61
|
-
</AtomicText>
|
|
62
|
-
)}
|
|
63
|
-
</View>
|
|
64
|
-
|
|
65
|
-
<View style={[styles.wave, { backgroundColor: tokens.colors.background }]} />
|
|
66
|
-
</View>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
PaywallHeader.displayName = "PaywallHeader";
|
|
72
|
-
|
|
73
|
-
const styles = StyleSheet.create({
|
|
74
|
-
container: {
|
|
75
|
-
paddingTop: 56,
|
|
76
|
-
paddingBottom: 36,
|
|
77
|
-
paddingHorizontal: 24,
|
|
78
|
-
position: "relative",
|
|
79
|
-
},
|
|
80
|
-
closeButton: {
|
|
81
|
-
position: "absolute",
|
|
82
|
-
top: 48,
|
|
83
|
-
right: 20,
|
|
84
|
-
width: 36,
|
|
85
|
-
height: 36,
|
|
86
|
-
borderRadius: 18,
|
|
87
|
-
justifyContent: "center",
|
|
88
|
-
alignItems: "center",
|
|
89
|
-
zIndex: 10,
|
|
90
|
-
},
|
|
91
|
-
content: {
|
|
92
|
-
alignItems: "center",
|
|
93
|
-
},
|
|
94
|
-
title: {
|
|
95
|
-
fontWeight: "700",
|
|
96
|
-
textAlign: "center",
|
|
97
|
-
marginBottom: 8,
|
|
98
|
-
},
|
|
99
|
-
subtitle: {
|
|
100
|
-
textAlign: "center",
|
|
101
|
-
opacity: 0.9,
|
|
102
|
-
},
|
|
103
|
-
wave: {
|
|
104
|
-
position: "absolute",
|
|
105
|
-
bottom: -1,
|
|
106
|
-
left: 0,
|
|
107
|
-
right: 0,
|
|
108
|
-
height: 24,
|
|
109
|
-
borderTopLeftRadius: 24,
|
|
110
|
-
borderTopRightRadius: 24,
|
|
111
|
-
},
|
|
112
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import type { PaywallTranslations, PaywallLegalUrls } from "../entities/types";
|
|
3
|
-
|
|
4
|
-
type TranslationFunction = (key: string) => string;
|
|
5
|
-
|
|
6
|
-
interface PaywallTranslationKeys {
|
|
7
|
-
title: string;
|
|
8
|
-
subtitle: string;
|
|
9
|
-
loadingText: string;
|
|
10
|
-
emptyText: string;
|
|
11
|
-
purchaseButtonText: string;
|
|
12
|
-
restoreButtonText: string;
|
|
13
|
-
processingText: string;
|
|
14
|
-
privacyText: string;
|
|
15
|
-
termsOfServiceText: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface UsePaywallTranslationsParams {
|
|
19
|
-
t: TranslationFunction;
|
|
20
|
-
keys?: Partial<PaywallTranslationKeys>;
|
|
21
|
-
legalUrls: PaywallLegalUrls;
|
|
22
|
-
creditsLabel?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface UsePaywallTranslationsResult {
|
|
26
|
-
translations: PaywallTranslations;
|
|
27
|
-
legalUrls: PaywallLegalUrls;
|
|
28
|
-
creditsLabel: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const DEFAULT_KEYS: PaywallTranslationKeys = {
|
|
32
|
-
title: "premium.title",
|
|
33
|
-
subtitle: "premium.subtitle",
|
|
34
|
-
loadingText: "paywall.loading",
|
|
35
|
-
emptyText: "paywall.empty",
|
|
36
|
-
purchaseButtonText: "paywall.subscribe",
|
|
37
|
-
restoreButtonText: "paywall.restore",
|
|
38
|
-
processingText: "paywall.processing",
|
|
39
|
-
privacyText: "auth.privacyPolicy",
|
|
40
|
-
termsOfServiceText: "auth.termsOfService",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const usePaywallTranslations = ({
|
|
44
|
-
t,
|
|
45
|
-
keys,
|
|
46
|
-
legalUrls,
|
|
47
|
-
creditsLabel,
|
|
48
|
-
}: UsePaywallTranslationsParams): UsePaywallTranslationsResult => {
|
|
49
|
-
const mergedKeys = useMemo(
|
|
50
|
-
() => ({ ...DEFAULT_KEYS, ...keys }),
|
|
51
|
-
[keys],
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const translations: PaywallTranslations = useMemo(
|
|
55
|
-
() => ({
|
|
56
|
-
title: t(mergedKeys.title),
|
|
57
|
-
subtitle: t(mergedKeys.subtitle),
|
|
58
|
-
loadingText: t(mergedKeys.loadingText),
|
|
59
|
-
emptyText: t(mergedKeys.emptyText),
|
|
60
|
-
purchaseButtonText: t(mergedKeys.purchaseButtonText),
|
|
61
|
-
restoreButtonText: t(mergedKeys.restoreButtonText),
|
|
62
|
-
processingText: t(mergedKeys.processingText),
|
|
63
|
-
privacyText: t(mergedKeys.privacyText),
|
|
64
|
-
termsOfServiceText: t(mergedKeys.termsOfServiceText),
|
|
65
|
-
}),
|
|
66
|
-
[t, mergedKeys],
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
const finalCreditsLabel = creditsLabel ?? t("paywall.credits");
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
translations,
|
|
73
|
-
legalUrls,
|
|
74
|
-
creditsLabel: finalCreditsLabel,
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export type { UsePaywallTranslationsParams, UsePaywallTranslationsResult };
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback } from "react";
|
|
2
|
-
import type { PurchasesPackage } from "react-native-purchases";
|
|
3
|
-
|
|
4
|
-
interface UseSubscriptionModalProps {
|
|
5
|
-
onPurchase: (pkg: PurchasesPackage) => Promise<boolean>;
|
|
6
|
-
onRestore: () => Promise<boolean>;
|
|
7
|
-
onClose: () => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const useSubscriptionModal = ({
|
|
11
|
-
onPurchase,
|
|
12
|
-
onRestore,
|
|
13
|
-
onClose,
|
|
14
|
-
}: UseSubscriptionModalProps) => {
|
|
15
|
-
const [selectedPkg, setSelectedPkg] = useState<PurchasesPackage | null>(null);
|
|
16
|
-
const [isProcessing, setIsProcessing] = useState(false);
|
|
17
|
-
|
|
18
|
-
const handlePurchase = useCallback(async () => {
|
|
19
|
-
if (!selectedPkg || isProcessing) return;
|
|
20
|
-
setIsProcessing(true);
|
|
21
|
-
try {
|
|
22
|
-
if (await onPurchase(selectedPkg)) onClose();
|
|
23
|
-
} finally {
|
|
24
|
-
setIsProcessing(false);
|
|
25
|
-
}
|
|
26
|
-
}, [selectedPkg, onPurchase, onClose, isProcessing]);
|
|
27
|
-
|
|
28
|
-
const handleRestore = useCallback(async () => {
|
|
29
|
-
if (isProcessing) return;
|
|
30
|
-
setIsProcessing(true);
|
|
31
|
-
try {
|
|
32
|
-
if (await onRestore()) onClose();
|
|
33
|
-
} finally {
|
|
34
|
-
setIsProcessing(false);
|
|
35
|
-
}
|
|
36
|
-
}, [onRestore, onClose, isProcessing]);
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
selectedPkg,
|
|
40
|
-
setSelectedPkg,
|
|
41
|
-
isProcessing,
|
|
42
|
-
handlePurchase,
|
|
43
|
-
handleRestore,
|
|
44
|
-
};
|
|
45
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paywall Domain
|
|
3
|
-
* Complete paywall solution for subscription and credits
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// Entities
|
|
7
|
-
export * from "./entities/types";
|
|
8
|
-
|
|
9
|
-
// Hooks
|
|
10
|
-
export * from "./hooks/useSubscriptionModal";
|
|
11
|
-
export * from "./hooks/usePaywallActions";
|
|
12
|
-
export * from "./hooks/usePaywallTranslations";
|
|
13
|
-
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RevenueCat Constants
|
|
3
|
-
* Error codes, messages, and logging constants
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import Purchases from "react-native-purchases";
|
|
7
|
-
|
|
8
|
-
export const REVENUECAT_LOG_PREFIX = "[RevenueCat]";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* RevenueCat Error Code Type
|
|
12
|
-
* Re-export for type safety
|
|
13
|
-
*/
|
|
14
|
-
export type PurchasesErrorCode = typeof Purchases.PURCHASES_ERROR_CODE[keyof typeof Purchases.PURCHASES_ERROR_CODE];
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Error Message Configuration
|
|
18
|
-
*/
|
|
19
|
-
export interface ErrorMessage {
|
|
20
|
-
title: string;
|
|
21
|
-
message: string;
|
|
22
|
-
shouldShowAlert?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Error Code to Enum Mapping
|
|
27
|
-
* Maps both string keys and numeric codes to Purchases error enum values
|
|
28
|
-
*/
|
|
29
|
-
const ERROR_CODE_MAP = new Map<string, PurchasesErrorCode>([
|
|
30
|
-
["PURCHASE_CANCELLED_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR],
|
|
31
|
-
["PURCHASE_NOT_ALLOWED_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_NOT_ALLOWED_ERROR],
|
|
32
|
-
["PURCHASE_INVALID_ERROR", Purchases.PURCHASES_ERROR_CODE.PURCHASE_INVALID_ERROR],
|
|
33
|
-
["PRODUCT_ALREADY_PURCHASED_ERROR", Purchases.PURCHASES_ERROR_CODE.PRODUCT_ALREADY_PURCHASED_ERROR],
|
|
34
|
-
["PRODUCT_NOT_AVAILABLE_FOR_PURCHASE_ERROR", Purchases.PURCHASES_ERROR_CODE.PRODUCT_NOT_AVAILABLE_FOR_PURCHASE_ERROR],
|
|
35
|
-
["NETWORK_ERROR", Purchases.PURCHASES_ERROR_CODE.NETWORK_ERROR],
|
|
36
|
-
["UNKNOWN_ERROR", Purchases.PURCHASES_ERROR_CODE.UNKNOWN_ERROR],
|
|
37
|
-
["RECEIPT_ALREADY_IN_USE_ERROR", Purchases.PURCHASES_ERROR_CODE.RECEIPT_ALREADY_IN_USE_ERROR],
|
|
38
|
-
["INVALID_CREDENTIALS_ERROR", Purchases.PURCHASES_ERROR_CODE.INVALID_CREDENTIALS_ERROR],
|
|
39
|
-
["UNEXPECTED_BACKEND_RESPONSE_ERROR", Purchases.PURCHASES_ERROR_CODE.UNEXPECTED_BACKEND_RESPONSE_ERROR],
|
|
40
|
-
["CONFIGURATION_ERROR", Purchases.PURCHASES_ERROR_CODE.CONFIGURATION_ERROR],
|
|
41
|
-
["STORE_PROBLEM_ERROR", Purchases.PURCHASES_ERROR_CODE.STORE_PROBLEM_ERROR],
|
|
42
|
-
["PAYMENT_PENDING_ERROR", Purchases.PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR],
|
|
43
|
-
// Numeric codes as fallback
|
|
44
|
-
["1", Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR],
|
|
45
|
-
["2", Purchases.PURCHASES_ERROR_CODE.STORE_PROBLEM_ERROR],
|
|
46
|
-
["3", Purchases.PURCHASES_ERROR_CODE.PURCHASE_NOT_ALLOWED_ERROR],
|
|
47
|
-
["4", Purchases.PURCHASES_ERROR_CODE.PURCHASE_INVALID_ERROR],
|
|
48
|
-
["5", Purchases.PURCHASES_ERROR_CODE.PRODUCT_NOT_AVAILABLE_FOR_PURCHASE_ERROR],
|
|
49
|
-
["6", Purchases.PURCHASES_ERROR_CODE.PRODUCT_ALREADY_PURCHASED_ERROR],
|
|
50
|
-
["7", Purchases.PURCHASES_ERROR_CODE.NETWORK_ERROR],
|
|
51
|
-
["8", Purchases.PURCHASES_ERROR_CODE.RECEIPT_ALREADY_IN_USE_ERROR],
|
|
52
|
-
["9", Purchases.PURCHASES_ERROR_CODE.INVALID_CREDENTIALS_ERROR],
|
|
53
|
-
["10", Purchases.PURCHASES_ERROR_CODE.UNEXPECTED_BACKEND_RESPONSE_ERROR],
|
|
54
|
-
["16", Purchases.PURCHASES_ERROR_CODE.CONFIGURATION_ERROR],
|
|
55
|
-
["20", Purchases.PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR],
|
|
56
|
-
["0", Purchases.PURCHASES_ERROR_CODE.UNKNOWN_ERROR],
|
|
57
|
-
]);
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* User-friendly error messages mapped by error code enum
|
|
61
|
-
* Strategy Pattern: Each error code has its own message configuration
|
|
62
|
-
*/
|
|
63
|
-
const ERROR_MESSAGES_MAP = new Map<PurchasesErrorCode, ErrorMessage>([
|
|
64
|
-
[
|
|
65
|
-
Purchases.PURCHASES_ERROR_CODE.PURCHASE_CANCELLED_ERROR,
|
|
66
|
-
{
|
|
67
|
-
title: "Purchase Cancelled",
|
|
68
|
-
message: "The purchase was cancelled.",
|
|
69
|
-
shouldShowAlert: false, // Don't show alert for user cancellation
|
|
70
|
-
},
|
|
71
|
-
],
|
|
72
|
-
[
|
|
73
|
-
Purchases.PURCHASES_ERROR_CODE.PURCHASE_NOT_ALLOWED_ERROR,
|
|
74
|
-
{
|
|
75
|
-
title: "Purchase Not Allowed",
|
|
76
|
-
message: "In-app purchases are disabled on this device.",
|
|
77
|
-
shouldShowAlert: true,
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
[
|
|
81
|
-
Purchases.PURCHASES_ERROR_CODE.PURCHASE_INVALID_ERROR,
|
|
82
|
-
{
|
|
83
|
-
title: "Invalid Purchase",
|
|
84
|
-
message: "The purchase is invalid. Please contact support.",
|
|
85
|
-
shouldShowAlert: true,
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
[
|
|
89
|
-
Purchases.PURCHASES_ERROR_CODE.PRODUCT_ALREADY_PURCHASED_ERROR,
|
|
90
|
-
{
|
|
91
|
-
title: "Already Purchased",
|
|
92
|
-
message: "You already own this subscription. Restoring your purchase...",
|
|
93
|
-
shouldShowAlert: true,
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
[
|
|
97
|
-
Purchases.PURCHASES_ERROR_CODE.PRODUCT_NOT_AVAILABLE_FOR_PURCHASE_ERROR,
|
|
98
|
-
{
|
|
99
|
-
title: "Product Unavailable",
|
|
100
|
-
message: "This product is not available for purchase at this time.",
|
|
101
|
-
shouldShowAlert: true,
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
[
|
|
105
|
-
Purchases.PURCHASES_ERROR_CODE.NETWORK_ERROR,
|
|
106
|
-
{
|
|
107
|
-
title: "Network Error",
|
|
108
|
-
message: "Please check your internet connection and try again.",
|
|
109
|
-
shouldShowAlert: true,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
[
|
|
113
|
-
Purchases.PURCHASES_ERROR_CODE.UNKNOWN_ERROR,
|
|
114
|
-
{
|
|
115
|
-
title: "Unknown Error",
|
|
116
|
-
message: "An unexpected error occurred. Please try again.",
|
|
117
|
-
shouldShowAlert: true,
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
[
|
|
121
|
-
Purchases.PURCHASES_ERROR_CODE.RECEIPT_ALREADY_IN_USE_ERROR,
|
|
122
|
-
{
|
|
123
|
-
title: "Receipt Already Used",
|
|
124
|
-
message: "This receipt is already associated with another account.",
|
|
125
|
-
shouldShowAlert: true,
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
[
|
|
129
|
-
Purchases.PURCHASES_ERROR_CODE.INVALID_CREDENTIALS_ERROR,
|
|
130
|
-
{
|
|
131
|
-
title: "Configuration Error",
|
|
132
|
-
message: "The app is not configured correctly. Please contact support.",
|
|
133
|
-
shouldShowAlert: true,
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
[
|
|
137
|
-
Purchases.PURCHASES_ERROR_CODE.UNEXPECTED_BACKEND_RESPONSE_ERROR,
|
|
138
|
-
{
|
|
139
|
-
title: "Server Error",
|
|
140
|
-
message: "The server returned an unexpected response. Please try again later.",
|
|
141
|
-
shouldShowAlert: true,
|
|
142
|
-
},
|
|
143
|
-
],
|
|
144
|
-
[
|
|
145
|
-
Purchases.PURCHASES_ERROR_CODE.CONFIGURATION_ERROR,
|
|
146
|
-
{
|
|
147
|
-
title: "Configuration Error",
|
|
148
|
-
message: "RevenueCat is not configured correctly. Please contact support.",
|
|
149
|
-
shouldShowAlert: true,
|
|
150
|
-
},
|
|
151
|
-
],
|
|
152
|
-
[
|
|
153
|
-
Purchases.PURCHASES_ERROR_CODE.STORE_PROBLEM_ERROR,
|
|
154
|
-
{
|
|
155
|
-
title: "Store Error",
|
|
156
|
-
message: "There was a problem with the app store. Please try again.",
|
|
157
|
-
shouldShowAlert: true,
|
|
158
|
-
},
|
|
159
|
-
],
|
|
160
|
-
[
|
|
161
|
-
Purchases.PURCHASES_ERROR_CODE.PAYMENT_PENDING_ERROR,
|
|
162
|
-
{
|
|
163
|
-
title: "Payment Pending",
|
|
164
|
-
message: "Your payment is still being processed. Please check back later.",
|
|
165
|
-
shouldShowAlert: true,
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
]);
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Default error message for unknown errors
|
|
172
|
-
*/
|
|
173
|
-
const DEFAULT_ERROR_MESSAGE: ErrorMessage = {
|
|
174
|
-
title: "Error",
|
|
175
|
-
message: "An error occurred. Please try again.",
|
|
176
|
-
shouldShowAlert: true,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Get error message configuration for a given error code
|
|
181
|
-
* Uses Strategy Pattern with Map lookup - O(1) complexity
|
|
182
|
-
*
|
|
183
|
-
* @param errorCode - Error code string from RevenueCat error
|
|
184
|
-
* @returns ErrorMessage configuration
|
|
185
|
-
*/
|
|
186
|
-
export function getErrorMessageForCode(errorCode: string | null | undefined): ErrorMessage {
|
|
187
|
-
if (!errorCode) {
|
|
188
|
-
return DEFAULT_ERROR_MESSAGE;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Try to map string code to enum value
|
|
192
|
-
const enumCode = ERROR_CODE_MAP.get(errorCode);
|
|
193
|
-
if (enumCode) {
|
|
194
|
-
const message = ERROR_MESSAGES_MAP.get(enumCode);
|
|
195
|
-
if (message) {
|
|
196
|
-
return message;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return DEFAULT_ERROR_MESSAGE;
|
|
201
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Customer Info Helper Functions
|
|
3
|
-
* Utilities for extracting data from RevenueCat CustomerInfo objects
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { CustomerInfo } from "react-native-purchases";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Extracts active entitlement IDs from CustomerInfo
|
|
10
|
-
* Useful for logging and debugging
|
|
11
|
-
*
|
|
12
|
-
* @param customerInfo - RevenueCat CustomerInfo object
|
|
13
|
-
* @returns Array of active entitlement IDs
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const activeIds = getActiveEntitlementIds(customerInfo);
|
|
17
|
-
* console.log("Active entitlements:", activeIds); // ["premium", "pro_features"]
|
|
18
|
-
*/
|
|
19
|
-
export function getActiveEntitlementIds(customerInfo: CustomerInfo): string[] {
|
|
20
|
-
return Object.keys(customerInfo.entitlements.active);
|
|
21
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Customer Info Hook
|
|
3
|
-
* Fetches customer info without registering a listener
|
|
4
|
-
* CustomerInfoListenerManager handles all listener logic
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useQuery, useQueryClient } from "@umituz/react-native-design-system";
|
|
8
|
-
import { useEffect, useRef } from "react";
|
|
9
|
-
import Purchases from "react-native-purchases";
|
|
10
|
-
import type { UseCustomerInfoResult } from "./types";
|
|
11
|
-
import { SUBSCRIPTION_QUERY_KEYS } from "../subscriptionQueryKeys";
|
|
12
|
-
|
|
13
|
-
export function useCustomerInfo(): UseCustomerInfoResult {
|
|
14
|
-
const queryClient = useQueryClient();
|
|
15
|
-
const mountedRef = useRef(true);
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
mountedRef.current = true;
|
|
19
|
-
return () => {
|
|
20
|
-
mountedRef.current = false;
|
|
21
|
-
};
|
|
22
|
-
}, []);
|
|
23
|
-
|
|
24
|
-
const query = useQuery({
|
|
25
|
-
queryKey: SUBSCRIPTION_QUERY_KEYS.customerInfo,
|
|
26
|
-
queryFn: async () => {
|
|
27
|
-
const info = await Purchases.getCustomerInfo();
|
|
28
|
-
return info;
|
|
29
|
-
},
|
|
30
|
-
staleTime: 30 * 1000, // 30 seconds
|
|
31
|
-
gcTime: 5 * 60 * 1000, // 5 minutes
|
|
32
|
-
refetchOnMount: true,
|
|
33
|
-
refetchOnWindowFocus: false,
|
|
34
|
-
retry: 2,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Expose refetch as a method
|
|
38
|
-
const refetch = async () => {
|
|
39
|
-
if (!mountedRef.current) return;
|
|
40
|
-
await queryClient.invalidateQueries({
|
|
41
|
-
queryKey: SUBSCRIPTION_QUERY_KEYS.customerInfo,
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
customerInfo: query.data ?? null,
|
|
47
|
-
loading: query.isLoading,
|
|
48
|
-
error: query.error?.message ?? null,
|
|
49
|
-
refetch,
|
|
50
|
-
isFetching: query.isFetching,
|
|
51
|
-
};
|
|
52
|
-
}
|