@umituz/react-native-subscription 2.27.96 → 2.27.98
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/core/Credits.ts +3 -11
- package/src/domains/credits/presentation/useDeductCredit.ts +0 -4
- package/src/domains/paywall/components/PaywallContainer.tsx +17 -1
- package/src/domains/paywall/components/PaywallContainer.types.ts +2 -1
- package/src/domains/paywall/components/PaywallModal.tsx +0 -6
- package/src/domains/paywall/hooks/usePaywallActions.ts +1 -1
- package/src/domains/subscription/application/SubscriptionInitializer.ts +0 -6
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +23 -12
- package/src/domains/subscription/infrastructure/hooks/subscriptionQueryKeys.ts +4 -4
- package/src/domains/subscription/infrastructure/hooks/useCustomerInfo.ts +0 -19
- package/src/domains/subscription/infrastructure/hooks/useInitializeSubscription.ts +0 -16
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +0 -31
- package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +0 -27
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +0 -12
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionPackages.ts +1 -7
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -2
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +0 -21
- package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +0 -9
- package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +20 -7
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +1 -16
- package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.tsx +49 -24
- package/src/domains/subscription/presentation/screens/components/CreditsList.tsx +14 -1
- package/src/domains/subscription/presentation/screens/components/SubscriptionActions.tsx +6 -1
- package/src/domains/subscription/presentation/screens/components/SubscriptionHeader.tsx +20 -1
- package/src/domains/subscription/presentation/screens/components/UpgradePrompt.tsx +13 -1
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +0 -3
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +1 -1
- package/src/domains/subscription/presentation/useFeatureGate.ts +9 -55
- package/src/domains/subscription/presentation/usePaywallVisibility.ts +1 -1
- package/src/domains/subscription/presentation/useSubscriptionStatus.ts +1 -5
- package/src/init/index.ts +0 -3
- package/src/presentation/hooks/index.ts +0 -4
- package/src/shared/infrastructure/SubscriptionEventBus.ts +27 -0
- package/src/utils/packageTypeDetector.ts +0 -4
- package/src/domains/subscription/presentation/screens/components/DevTestSection.tsx +0 -125
- package/src/domains/subscription/presentation/types/README.md +0 -22
- package/src/domains/subscription/presentation/types/SubscriptionDetailTypes.ts +0 -153
- package/src/domains/subscription/presentation/types/SubscriptionSettingsTypes.ts +0 -74
- package/src/domains/subscription/presentation/useAuthSubscriptionSync.ts +0 -63
- package/src/domains/subscription/presentation/usePremiumGate.ts +0 -84
- package/src/domains/subscription/presentation/useSavedPurchaseAutoExecution.ts +0 -148
- package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.ts +0 -115
- package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.utils.ts +0 -57
|
@@ -11,22 +11,55 @@ import {
|
|
|
11
11
|
ScreenLayout,
|
|
12
12
|
} from "@umituz/react-native-design-system";
|
|
13
13
|
import { SubscriptionHeader } from "./components/SubscriptionHeader";
|
|
14
|
-
import { CreditsList } from "./components/CreditsList";
|
|
15
|
-
import { UpgradePrompt } from "./components/UpgradePrompt";
|
|
16
|
-
import { DevTestSection } from "./components/DevTestSection";
|
|
17
|
-
import type { SubscriptionDetailScreenProps } from "../types/SubscriptionDetailTypes";
|
|
14
|
+
import { CreditsList, type CreditItem } from "./components/CreditsList";
|
|
15
|
+
import { UpgradePrompt, type Benefit } from "./components/UpgradePrompt";
|
|
18
16
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
export interface SubscriptionDisplayFlags {
|
|
18
|
+
showHeader: boolean;
|
|
19
|
+
showCredits: boolean;
|
|
20
|
+
showUpgradePrompt: boolean;
|
|
21
|
+
showExpirationDate: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SubscriptionDetailTranslations {
|
|
25
|
+
title: string;
|
|
26
|
+
statusActive: string;
|
|
27
|
+
statusExpired: string;
|
|
28
|
+
statusFree: string;
|
|
29
|
+
statusCanceled: string;
|
|
30
|
+
statusLabel: string;
|
|
31
|
+
lifetimeLabel: string;
|
|
32
|
+
expiresLabel: string;
|
|
33
|
+
purchasedLabel: string;
|
|
34
|
+
usageTitle?: string;
|
|
35
|
+
creditsTitle: string;
|
|
36
|
+
creditsResetInfo?: string;
|
|
37
|
+
remainingLabel?: string;
|
|
38
|
+
upgradeButton: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface UpgradePromptConfig {
|
|
42
|
+
title: string;
|
|
43
|
+
subtitle?: string;
|
|
44
|
+
benefits?: readonly Benefit[];
|
|
45
|
+
onUpgrade?: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SubscriptionDetailConfig {
|
|
49
|
+
display: SubscriptionDisplayFlags;
|
|
50
|
+
statusType: "active" | "expired" | "none" | "canceled";
|
|
51
|
+
isLifetime: boolean;
|
|
52
|
+
expirationDate?: string;
|
|
53
|
+
purchaseDate?: string;
|
|
54
|
+
daysRemaining?: number | null;
|
|
55
|
+
credits?: readonly CreditItem[];
|
|
56
|
+
translations: SubscriptionDetailTranslations;
|
|
57
|
+
upgradePrompt?: UpgradePromptConfig;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface SubscriptionDetailScreenProps {
|
|
61
|
+
config: SubscriptionDetailConfig;
|
|
62
|
+
}
|
|
30
63
|
|
|
31
64
|
export const SubscriptionDetailScreen: React.FC<
|
|
32
65
|
SubscriptionDetailScreenProps
|
|
@@ -59,14 +92,6 @@ export const SubscriptionDetailScreen: React.FC<
|
|
|
59
92
|
edges={["bottom"]}
|
|
60
93
|
backgroundColor={tokens.colors.backgroundPrimary}
|
|
61
94
|
contentContainerStyle={styles.content}
|
|
62
|
-
footer={
|
|
63
|
-
config.devTools ? (
|
|
64
|
-
<DevTestSection
|
|
65
|
-
actions={config.devTools.actions}
|
|
66
|
-
title={config.devTools.title}
|
|
67
|
-
/>
|
|
68
|
-
) : undefined
|
|
69
|
-
}
|
|
70
95
|
>
|
|
71
96
|
<View style={styles.cardsContainer}>
|
|
72
97
|
{showHeader && (
|
|
@@ -98,7 +123,7 @@ export const SubscriptionDetailScreen: React.FC<
|
|
|
98
123
|
subtitle={config.upgradePrompt.subtitle}
|
|
99
124
|
benefits={config.upgradePrompt.benefits}
|
|
100
125
|
upgradeButtonLabel={config.translations.upgradeButton}
|
|
101
|
-
onUpgrade={config.onUpgrade}
|
|
126
|
+
onUpgrade={config.upgradePrompt.onUpgrade ?? (() => {})}
|
|
102
127
|
/>
|
|
103
128
|
)}
|
|
104
129
|
</View>
|
|
@@ -7,7 +7,20 @@ import React, { useMemo } from "react";
|
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
9
|
import { CreditRow } from "../../components/details/CreditRow";
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
export interface CreditItem {
|
|
12
|
+
id: string;
|
|
13
|
+
label: string;
|
|
14
|
+
current: number;
|
|
15
|
+
total: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface CreditsListProps {
|
|
19
|
+
credits: readonly CreditItem[];
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
remainingLabel?: string;
|
|
23
|
+
}
|
|
11
24
|
|
|
12
25
|
export const CreditsList: React.FC<CreditsListProps> = ({
|
|
13
26
|
credits,
|
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
import React, { useMemo } from "react";
|
|
7
7
|
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
export interface SubscriptionActionsProps {
|
|
11
|
+
isPremium: boolean;
|
|
12
|
+
upgradeButtonLabel?: string;
|
|
13
|
+
onUpgrade?: () => void;
|
|
14
|
+
}
|
|
10
15
|
|
|
11
16
|
export const SubscriptionActions: React.FC<SubscriptionActionsProps> = ({
|
|
12
17
|
isPremium,
|
|
@@ -8,7 +8,26 @@ import { View, StyleSheet } from "react-native";
|
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
9
|
import { PremiumStatusBadge } from "../../components/details/PremiumStatusBadge";
|
|
10
10
|
import { DetailRow } from "../../components/details/DetailRow";
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
export interface SubscriptionHeaderProps {
|
|
13
|
+
statusType: "active" | "expired" | "none" | "canceled";
|
|
14
|
+
showExpirationDate: boolean;
|
|
15
|
+
isLifetime: boolean;
|
|
16
|
+
expirationDate?: string;
|
|
17
|
+
purchaseDate?: string;
|
|
18
|
+
daysRemaining?: number | null;
|
|
19
|
+
translations: {
|
|
20
|
+
title: string;
|
|
21
|
+
statusActive: string;
|
|
22
|
+
statusExpired: string;
|
|
23
|
+
statusFree: string;
|
|
24
|
+
statusCanceled: string;
|
|
25
|
+
statusLabel: string;
|
|
26
|
+
lifetimeLabel: string;
|
|
27
|
+
expiresLabel: string;
|
|
28
|
+
purchasedLabel: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
12
31
|
|
|
13
32
|
export const SubscriptionHeader: React.FC<SubscriptionHeaderProps> = ({
|
|
14
33
|
statusType,
|
|
@@ -10,7 +10,19 @@ import {
|
|
|
10
10
|
AtomicText,
|
|
11
11
|
AtomicIcon,
|
|
12
12
|
} from "@umituz/react-native-design-system";
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
export interface Benefit {
|
|
15
|
+
icon?: string;
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UpgradePromptProps {
|
|
20
|
+
title: string;
|
|
21
|
+
subtitle?: string;
|
|
22
|
+
benefits?: readonly Benefit[];
|
|
23
|
+
upgradeButtonLabel: string;
|
|
24
|
+
onUpgrade: () => void;
|
|
25
|
+
}
|
|
14
26
|
|
|
15
27
|
export const UpgradePrompt: React.FC<UpgradePromptProps> = ({
|
|
16
28
|
title,
|
|
@@ -49,7 +49,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
|
|
|
49
49
|
// Still update to the new purchase to recover from potential stuck state
|
|
50
50
|
}
|
|
51
51
|
if (__DEV__) {
|
|
52
|
-
console.log("[PurchaseLoadingStore] startPurchase:", { productId, source });
|
|
53
52
|
}
|
|
54
53
|
set({
|
|
55
54
|
isPurchasing: true,
|
|
@@ -67,7 +66,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
|
|
|
67
66
|
// Reset to initial state to recover from potential stuck state
|
|
68
67
|
}
|
|
69
68
|
if (__DEV__) {
|
|
70
|
-
console.log("[PurchaseLoadingStore] endPurchase");
|
|
71
69
|
}
|
|
72
70
|
set({
|
|
73
71
|
isPurchasing: false,
|
|
@@ -78,7 +76,6 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
|
|
|
78
76
|
|
|
79
77
|
reset: () => {
|
|
80
78
|
if (__DEV__) {
|
|
81
|
-
console.log("[PurchaseLoadingStore] reset");
|
|
82
79
|
}
|
|
83
80
|
set(initialState);
|
|
84
81
|
},
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useCallback } from "react";
|
|
7
7
|
import type { PurchasesPackage } from "react-native-purchases";
|
|
8
8
|
import { usePremium } from "./usePremium";
|
|
9
|
-
import type { PurchaseSource } from "
|
|
9
|
+
import type { PurchaseSource } from "../core/SubscriptionConstants";
|
|
10
10
|
|
|
11
11
|
export interface PurchaseAuthProvider {
|
|
12
12
|
isAuthenticated: () => boolean;
|
|
@@ -64,48 +64,19 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
64
64
|
}, [requiredCredits]);
|
|
65
65
|
|
|
66
66
|
useEffect(() => {
|
|
67
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
68
|
-
console.log("[useFeatureGate] Auth credits effect", {
|
|
69
|
-
isWaiting: isWaitingForAuthCreditsRef.current,
|
|
70
|
-
isLoaded: isCreditsLoaded,
|
|
71
|
-
hasPending: !!pendingActionRef.current,
|
|
72
|
-
credits: creditBalance,
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
67
|
if (!isWaitingForAuthCreditsRef.current || !isCreditsLoaded || !pendingActionRef.current) {
|
|
77
68
|
return;
|
|
78
69
|
}
|
|
79
70
|
|
|
80
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
81
|
-
console.log("[useFeatureGate] Credits loaded after auth", {
|
|
82
|
-
credits: creditBalance,
|
|
83
|
-
hasSubscription,
|
|
84
|
-
isCreditsLoaded,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
71
|
isWaitingForAuthCreditsRef.current = false;
|
|
89
72
|
|
|
90
73
|
if (hasSubscription || creditBalance >= requiredCredits) {
|
|
91
74
|
const action = pendingActionRef.current;
|
|
92
75
|
pendingActionRef.current = null;
|
|
93
|
-
|
|
94
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
95
|
-
console.log("[useFeatureGate] Proceeding with action after auth", {
|
|
96
|
-
credits: creditBalance,
|
|
97
|
-
hasSubscription,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
76
|
action();
|
|
101
77
|
return;
|
|
102
78
|
}
|
|
103
79
|
|
|
104
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
105
|
-
console.log("[useFeatureGate] No credits after auth, showing paywall", {
|
|
106
|
-
credits: creditBalance,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
80
|
isWaitingForPurchaseRef.current = true;
|
|
110
81
|
onShowPaywall(requiredCredits);
|
|
111
82
|
}, [isCreditsLoaded, creditBalance, hasSubscription, requiredCredits, onShowPaywall]);
|
|
@@ -119,10 +90,6 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
119
90
|
const action = pendingActionRef.current;
|
|
120
91
|
pendingActionRef.current = null;
|
|
121
92
|
isWaitingForPurchaseRef.current = false;
|
|
122
|
-
|
|
123
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
124
|
-
console.log("[useFeatureGate] Access acquired (credits or subscription), executing pending action");
|
|
125
|
-
}
|
|
126
93
|
action();
|
|
127
94
|
}
|
|
128
95
|
|
|
@@ -132,48 +99,35 @@ export function useFeatureGate(params: UseFeatureGateParams): UseFeatureGateResu
|
|
|
132
99
|
|
|
133
100
|
const requireFeature = useCallback(
|
|
134
101
|
(action: () => void | Promise<void>) => {
|
|
135
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
136
|
-
console.log("[useFeatureGate] requireFeature", {
|
|
137
|
-
isAuthenticated,
|
|
138
|
-
hasSubscription,
|
|
139
|
-
creditBalance: creditBalanceRef.current,
|
|
140
|
-
requiredCredits,
|
|
141
|
-
isCreditsLoaded,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
102
|
if (!isAuthenticated) {
|
|
146
103
|
const postAuthAction = () => {
|
|
147
104
|
pendingActionRef.current = action;
|
|
148
105
|
isWaitingForAuthCreditsRef.current = true;
|
|
149
|
-
|
|
150
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
151
|
-
console.log("[useFeatureGate] Auth completed, waiting for credits to load");
|
|
152
|
-
}
|
|
153
106
|
};
|
|
154
107
|
onShowAuthModal(postAuthAction);
|
|
155
108
|
return;
|
|
156
109
|
}
|
|
157
110
|
|
|
158
|
-
|
|
111
|
+
// Use ref values to avoid stale closure
|
|
112
|
+
const currentHasSubscription = hasSubscriptionRef.current;
|
|
113
|
+
const currentBalance = creditBalanceRef.current;
|
|
114
|
+
const currentRequiredCredits = requiredCreditsRef.current;
|
|
115
|
+
|
|
116
|
+
if (currentHasSubscription) {
|
|
159
117
|
action();
|
|
160
118
|
return;
|
|
161
119
|
}
|
|
162
120
|
|
|
163
|
-
|
|
164
|
-
if (currentBalance < requiredCredits) {
|
|
165
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
166
|
-
console.log("[useFeatureGate] No credits, showing paywall");
|
|
167
|
-
}
|
|
121
|
+
if (currentBalance < currentRequiredCredits) {
|
|
168
122
|
pendingActionRef.current = action;
|
|
169
123
|
isWaitingForPurchaseRef.current = true;
|
|
170
|
-
|
|
124
|
+
onShowPaywallRef.current(currentRequiredCredits);
|
|
171
125
|
return;
|
|
172
126
|
}
|
|
173
127
|
|
|
174
128
|
action();
|
|
175
129
|
},
|
|
176
|
-
[isAuthenticated,
|
|
130
|
+
[isAuthenticated, onShowAuthModal, isCreditsLoaded]
|
|
177
131
|
);
|
|
178
132
|
|
|
179
133
|
const hasCredits = creditBalance >= requiredCredits;
|
|
@@ -48,11 +48,7 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
enabled: !!userId && SubscriptionManager.isInitializedForUser(userId),
|
|
51
|
-
|
|
52
|
-
gcTime: 5 * 60 * 1000, // 5 minutes
|
|
53
|
-
refetchOnMount: "always",
|
|
54
|
-
refetchOnWindowFocus: true,
|
|
55
|
-
refetchOnReconnect: true,
|
|
51
|
+
|
|
56
52
|
});
|
|
57
53
|
|
|
58
54
|
return {
|
package/src/init/index.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
export * from "../../domains/subscription/presentation/useAuthAwarePurchase";
|
|
2
|
-
export * from "../../domains/subscription/presentation/useAuthSubscriptionSync";
|
|
3
|
-
export * from "../../domains/subscription/presentation/useSavedPurchaseAutoExecution";
|
|
4
2
|
export * from "../../domains/credits/presentation/useCredits";
|
|
5
3
|
export * from "../../domains/credits/presentation/useDeductCredit";
|
|
6
4
|
export * from "../../domains/subscription/presentation/useFeatureGate";
|
|
7
5
|
export * from "../../domains/subscription/presentation/usePaywallVisibility";
|
|
8
6
|
export * from "../../domains/subscription/presentation/usePremium";
|
|
9
|
-
export * from "../../domains/subscription/presentation/usePremiumGate";
|
|
10
|
-
export * from "../../domains/subscription/presentation/useSubscriptionSettingsConfig";
|
|
11
7
|
export * from "../../domains/subscription/presentation/useSubscriptionStatus";
|
|
12
8
|
export * from "./feedback/usePaywallFeedback";
|
|
13
9
|
export * from "./feedback/useFeedbackSubmit";
|
|
@@ -28,6 +28,11 @@ export class SubscriptionEventBus {
|
|
|
28
28
|
const listeners = this.listeners[event];
|
|
29
29
|
if (listeners) {
|
|
30
30
|
this.listeners[event] = listeners.filter(l => l !== callback);
|
|
31
|
+
|
|
32
|
+
// Clean up empty event arrays to prevent memory leak
|
|
33
|
+
if (this.listeners[event].length === 0) {
|
|
34
|
+
delete this.listeners[event];
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
};
|
|
33
38
|
}
|
|
@@ -42,6 +47,28 @@ export class SubscriptionEventBus {
|
|
|
42
47
|
}
|
|
43
48
|
});
|
|
44
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Clear all listeners for a specific event or all events
|
|
53
|
+
* Useful for cleanup during testing or app state reset
|
|
54
|
+
*/
|
|
55
|
+
clear(event?: string): void {
|
|
56
|
+
if (event) {
|
|
57
|
+
delete this.listeners[event];
|
|
58
|
+
} else {
|
|
59
|
+
this.listeners = {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get listener count for debugging
|
|
65
|
+
*/
|
|
66
|
+
getListenerCount(event?: string): number {
|
|
67
|
+
if (event) {
|
|
68
|
+
return this.listeners[event]?.length ?? 0;
|
|
69
|
+
}
|
|
70
|
+
return Object.values(this.listeners).reduce((total, arr) => total + arr.length, 0);
|
|
71
|
+
}
|
|
45
72
|
}
|
|
46
73
|
|
|
47
74
|
export const subscriptionEventBus = SubscriptionEventBus.getInstance();
|
|
@@ -7,10 +7,6 @@ import { PACKAGE_TYPE, type PackageType } from "../domains/subscription/core/Sub
|
|
|
7
7
|
|
|
8
8
|
export type SubscriptionPackageType = PackageType;
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Check if identifier is a credit package (consumable purchase)
|
|
12
|
-
* Credit packages use a different system and don't need type detection
|
|
13
|
-
*/
|
|
14
10
|
/**
|
|
15
11
|
* Check if identifier is a credit package (consumable purchase)
|
|
16
12
|
* Credit packages use a different system and don't need type detection
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dev Test Section
|
|
3
|
-
* Developer testing tools for subscription renewals
|
|
4
|
-
* Only visible in __DEV__ mode
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useMemo } from "react";
|
|
8
|
-
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
9
|
-
import { AtomicText, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
10
|
-
import type { DevTestSectionProps } from "../../types/SubscriptionDetailTypes";
|
|
11
|
-
|
|
12
|
-
export type { DevTestActions, DevTestSectionProps } from "../../types/SubscriptionDetailTypes";
|
|
13
|
-
|
|
14
|
-
/** Dev test button translations */
|
|
15
|
-
export interface DevTestTranslations {
|
|
16
|
-
title: string;
|
|
17
|
-
testRenewal: string;
|
|
18
|
-
checkCredits: string;
|
|
19
|
-
testDuplicate: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface DevTestSectionWithTranslationsProps extends DevTestSectionProps {
|
|
23
|
-
translations?: DevTestTranslations;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const DevTestSection: React.FC<DevTestSectionWithTranslationsProps> = ({
|
|
27
|
-
actions,
|
|
28
|
-
title,
|
|
29
|
-
translations,
|
|
30
|
-
}) => {
|
|
31
|
-
const tokens = useAppDesignTokens();
|
|
32
|
-
|
|
33
|
-
const styles = useMemo(
|
|
34
|
-
() =>
|
|
35
|
-
StyleSheet.create({
|
|
36
|
-
container: {
|
|
37
|
-
padding: tokens.spacing.lg,
|
|
38
|
-
gap: tokens.spacing.md,
|
|
39
|
-
borderTopWidth: 1,
|
|
40
|
-
backgroundColor: tokens.colors.surfaceSecondary,
|
|
41
|
-
borderTopColor: tokens.colors.border,
|
|
42
|
-
},
|
|
43
|
-
title: {
|
|
44
|
-
fontWeight: "600",
|
|
45
|
-
marginBottom: tokens.spacing.xs,
|
|
46
|
-
},
|
|
47
|
-
button: {
|
|
48
|
-
paddingVertical: tokens.spacing.md,
|
|
49
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
50
|
-
borderRadius: tokens.radius.md,
|
|
51
|
-
alignItems: "center",
|
|
52
|
-
},
|
|
53
|
-
primaryButton: {
|
|
54
|
-
backgroundColor: tokens.colors.primary,
|
|
55
|
-
},
|
|
56
|
-
secondaryButton: {
|
|
57
|
-
backgroundColor: tokens.colors.surfaceSecondary,
|
|
58
|
-
borderWidth: 1,
|
|
59
|
-
borderColor: tokens.colors.border,
|
|
60
|
-
},
|
|
61
|
-
buttonText: {
|
|
62
|
-
fontWeight: "500",
|
|
63
|
-
},
|
|
64
|
-
}),
|
|
65
|
-
[tokens]
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
if (!__DEV__) {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const displayTitle = title || translations?.title;
|
|
73
|
-
const renewalText = translations?.testRenewal || "Test Auto-Renewal";
|
|
74
|
-
const creditsText = translations?.checkCredits || "Check Credits";
|
|
75
|
-
const duplicateText = translations?.testDuplicate || "Test Duplicate Protection";
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<View style={styles.container}>
|
|
79
|
-
{displayTitle && (
|
|
80
|
-
<AtomicText
|
|
81
|
-
type="titleMedium"
|
|
82
|
-
style={[styles.title, { color: tokens.colors.textPrimary }]}
|
|
83
|
-
>
|
|
84
|
-
{displayTitle}
|
|
85
|
-
</AtomicText>
|
|
86
|
-
)}
|
|
87
|
-
|
|
88
|
-
<TouchableOpacity
|
|
89
|
-
style={[styles.button, styles.primaryButton]}
|
|
90
|
-
onPress={actions.onTestRenewal}
|
|
91
|
-
>
|
|
92
|
-
<AtomicText
|
|
93
|
-
type="bodyMedium"
|
|
94
|
-
style={[styles.buttonText, { color: tokens.colors.onPrimary }]}
|
|
95
|
-
>
|
|
96
|
-
{renewalText}
|
|
97
|
-
</AtomicText>
|
|
98
|
-
</TouchableOpacity>
|
|
99
|
-
|
|
100
|
-
<TouchableOpacity
|
|
101
|
-
style={[styles.button, styles.secondaryButton]}
|
|
102
|
-
onPress={actions.onCheckCredits}
|
|
103
|
-
>
|
|
104
|
-
<AtomicText
|
|
105
|
-
type="bodyMedium"
|
|
106
|
-
style={[styles.buttonText, { color: tokens.colors.textPrimary }]}
|
|
107
|
-
>
|
|
108
|
-
{creditsText}
|
|
109
|
-
</AtomicText>
|
|
110
|
-
</TouchableOpacity>
|
|
111
|
-
|
|
112
|
-
<TouchableOpacity
|
|
113
|
-
style={[styles.button, styles.secondaryButton]}
|
|
114
|
-
onPress={actions.onTestDuplicate}
|
|
115
|
-
>
|
|
116
|
-
<AtomicText
|
|
117
|
-
type="bodyMedium"
|
|
118
|
-
style={[styles.buttonText, { color: tokens.colors.textPrimary }]}
|
|
119
|
-
>
|
|
120
|
-
{duplicateText}
|
|
121
|
-
</AtomicText>
|
|
122
|
-
</TouchableOpacity>
|
|
123
|
-
</View>
|
|
124
|
-
);
|
|
125
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Presentation Types
|
|
2
|
-
|
|
3
|
-
TypeScript type definitions and interfaces for the presentation layer.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This directory contains all type definitions used by presentation components and hooks.
|
|
8
|
-
|
|
9
|
-
## Contents
|
|
10
|
-
|
|
11
|
-
### Subscription Types
|
|
12
|
-
|
|
13
|
-
- **SubscriptionSettingsTypes.ts** - Configuration types for subscription settings UI
|
|
14
|
-
- **PaywallTypes.ts** - Paywall component types
|
|
15
|
-
- **SubscriptionTypes.ts** - General subscription UI types
|
|
16
|
-
|
|
17
|
-
## Key Exports
|
|
18
|
-
|
|
19
|
-
## Related
|
|
20
|
-
|
|
21
|
-
- [Hooks](../hooks/README.md)
|
|
22
|
-
- [Components](../components/README.md)
|