@umituz/react-native-subscription 2.41.7 → 2.41.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/credits/application/RefundCreditsCommand.ts +2 -1
- package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +7 -5
- package/src/domains/credits/presentation/useCredits.ts +4 -3
- package/src/domains/paywall/hooks/usePaywallOrchestrator.ts +58 -33
- package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +12 -4
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +9 -5
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.tsx +3 -2
- package/src/domains/subscription/presentation/useSubscriptionFlow.ts +1 -19
- package/src/domains/subscription/presentation/useSubscriptionStatus.ts +2 -2
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +2 -2
- package/src/index.ts +81 -14
- package/src/shared/infrastructure/react-query/queryConfig.ts +40 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-subscription",
|
|
3
|
-
"version": "2.41.
|
|
3
|
+
"version": "2.41.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",
|
|
@@ -41,7 +41,8 @@ export async function refundCreditsOperation(
|
|
|
41
41
|
|
|
42
42
|
const data = docSnap.data();
|
|
43
43
|
const current = data.credits as number;
|
|
44
|
-
|
|
44
|
+
// If creditLimit is null, use the refund amount as the limit to prevent unlimited credits
|
|
45
|
+
const creditLimit = (data.creditLimit as number) ?? (current + amount);
|
|
45
46
|
const updated = Math.min(current + amount, creditLimit);
|
|
46
47
|
|
|
47
48
|
tx.update(creditsRef, {
|
|
@@ -63,11 +63,13 @@ export const useDeductCredit = ({
|
|
|
63
63
|
}
|
|
64
64
|
return false;
|
|
65
65
|
} catch (error) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
if (__DEV__) {
|
|
67
|
+
console.error('[useDeductCredit] Unexpected error during credit refund', {
|
|
68
|
+
amount,
|
|
69
|
+
userId,
|
|
70
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
71
73
|
return false;
|
|
72
74
|
}
|
|
73
75
|
}, [userId, repository, queryClient]);
|
|
@@ -2,7 +2,7 @@ import { useQuery, useQueryClient } from "@umituz/react-native-design-system/tan
|
|
|
2
2
|
import { useCallback, useMemo, useEffect } from "react";
|
|
3
3
|
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
4
4
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
5
|
-
import {
|
|
5
|
+
import { SHORT_CACHE_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
|
|
6
6
|
import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
|
|
7
7
|
import {
|
|
8
8
|
getCreditsRepository,
|
|
@@ -13,6 +13,7 @@ import { calculateSafePercentage, canAffordAmount } from "../utils/creditValidat
|
|
|
13
13
|
import { isAuthenticated } from "../../subscription/utils/authGuards";
|
|
14
14
|
import { creditsQueryKeys } from "./creditsQueryKeys";
|
|
15
15
|
import type { UseCreditsResult, CreditsLoadStatus } from "./useCredits.types";
|
|
16
|
+
import type { UserCredits } from "../core/Credits";
|
|
16
17
|
|
|
17
18
|
const deriveLoadStatus = (
|
|
18
19
|
queryStatus: "pending" | "error" | "success",
|
|
@@ -32,7 +33,7 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
32
33
|
const hasUser = isAuthenticated(userId);
|
|
33
34
|
const queryEnabled = hasUser && isConfigured;
|
|
34
35
|
|
|
35
|
-
const { data, status, error, refetch } = useQuery({
|
|
36
|
+
const { data, status, error, refetch } = useQuery<UserCredits | null, Error>({
|
|
36
37
|
queryKey: creditsQueryKeys.user(userId),
|
|
37
38
|
queryFn: async () => {
|
|
38
39
|
if (!hasUser || !isConfigured) return null;
|
|
@@ -47,7 +48,7 @@ export const useCredits = (): UseCreditsResult => {
|
|
|
47
48
|
return result.data ?? null;
|
|
48
49
|
},
|
|
49
50
|
enabled: queryEnabled,
|
|
50
|
-
...
|
|
51
|
+
...SHORT_CACHE_CONFIG,
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
const queryClient = useQueryClient();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useCallback, useRef } from "react";
|
|
2
2
|
import { usePremium } from "../../subscription/presentation/usePremium";
|
|
3
|
-
import {
|
|
3
|
+
import { useSubscriptionFlowStore } from "../../subscription/presentation/useSubscriptionFlow";
|
|
4
4
|
import { usePaywallVisibility } from "../../subscription/presentation/usePaywallVisibility";
|
|
5
5
|
import { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../entities/types";
|
|
6
6
|
|
|
@@ -36,34 +36,39 @@ export function usePaywallOrchestrator({
|
|
|
36
36
|
bestValueIdentifier = "yearly",
|
|
37
37
|
creditsLabel,
|
|
38
38
|
}: PaywallOrchestratorOptions) {
|
|
39
|
-
const {
|
|
40
|
-
isPremium,
|
|
41
|
-
packages,
|
|
42
|
-
purchasePackage,
|
|
43
|
-
restorePurchase
|
|
39
|
+
const {
|
|
40
|
+
isPremium,
|
|
41
|
+
packages,
|
|
42
|
+
purchasePackage,
|
|
43
|
+
restorePurchase
|
|
44
44
|
} = usePremium();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
|
|
46
|
+
// Selectors for stable references and fine-grained updates
|
|
47
|
+
const isOnboardingComplete = useSubscriptionFlowStore((state) => state.isOnboardingComplete);
|
|
48
|
+
const showPostOnboardingPaywall = useSubscriptionFlowStore((state) => state.showPostOnboardingPaywall);
|
|
49
|
+
const paywallShown = useSubscriptionFlowStore((state) => state.paywallShown);
|
|
50
|
+
const isAuthModalOpen = useSubscriptionFlowStore((state) => state.isAuthModalOpen);
|
|
51
|
+
const showFeedback = useSubscriptionFlowStore((state) => state.showFeedback);
|
|
52
|
+
const markPaywallShown = useSubscriptionFlowStore((state) => state.markPaywallShown);
|
|
53
|
+
const closePostOnboardingPaywall = useSubscriptionFlowStore((state) => state.closePostOnboardingPaywall);
|
|
54
|
+
const setShowFeedback = useSubscriptionFlowStore((state) => state.setShowFeedback);
|
|
55
|
+
|
|
53
56
|
const { showPaywall, closePaywall } = usePaywallVisibility();
|
|
54
57
|
const purchasedRef = useRef(false);
|
|
58
|
+
const hasNavigatedRef = useRef(false);
|
|
55
59
|
|
|
56
60
|
const handleClose = useCallback(async () => {
|
|
57
61
|
await closePostOnboardingPaywall();
|
|
58
62
|
closePaywall();
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
// Trigger feedback if user declined and isn't premium
|
|
61
65
|
if (!isPremium && !purchasedRef.current) {
|
|
62
66
|
setTimeout(() => setShowFeedback(true), 300);
|
|
63
67
|
}
|
|
64
|
-
|
|
68
|
+
|
|
65
69
|
purchasedRef.current = false;
|
|
66
|
-
|
|
70
|
+
hasNavigatedRef.current = false;
|
|
71
|
+
|
|
67
72
|
if (navigation.canGoBack()) {
|
|
68
73
|
navigation.goBack();
|
|
69
74
|
}
|
|
@@ -73,9 +78,9 @@ export function usePaywallOrchestrator({
|
|
|
73
78
|
purchasedRef.current = true;
|
|
74
79
|
await markPaywallShown();
|
|
75
80
|
await closePostOnboardingPaywall();
|
|
76
|
-
|
|
81
|
+
|
|
77
82
|
onPurchaseSuccess?.();
|
|
78
|
-
|
|
83
|
+
|
|
79
84
|
if (navigation.canGoBack()) {
|
|
80
85
|
navigation.goBack();
|
|
81
86
|
}
|
|
@@ -84,16 +89,24 @@ export function usePaywallOrchestrator({
|
|
|
84
89
|
useEffect(() => {
|
|
85
90
|
if (!isNavReady || !isLocalizationReady) return;
|
|
86
91
|
|
|
87
|
-
const shouldShowPostOnboarding =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
!
|
|
91
|
-
!
|
|
92
|
+
const shouldShowPostOnboarding =
|
|
93
|
+
isOnboardingComplete &&
|
|
94
|
+
showPostOnboardingPaywall &&
|
|
95
|
+
!paywallShown &&
|
|
96
|
+
!isAuthModalOpen &&
|
|
92
97
|
!isPremium;
|
|
93
98
|
|
|
94
|
-
const shouldShowManual = showPaywall && !isPremium && !
|
|
99
|
+
const shouldShowManual = showPaywall && !isPremium && !isAuthModalOpen;
|
|
95
100
|
|
|
96
101
|
if (shouldShowPostOnboarding || shouldShowManual) {
|
|
102
|
+
// Guard against double navigation in same render cycle
|
|
103
|
+
if (hasNavigatedRef.current) return;
|
|
104
|
+
hasNavigatedRef.current = true;
|
|
105
|
+
|
|
106
|
+
if (__DEV__) console.log('[usePaywallOrchestrator] 🚀 Navigating to Paywall', {
|
|
107
|
+
source: shouldShowPostOnboarding ? "onboarding" : "manual"
|
|
108
|
+
});
|
|
109
|
+
|
|
97
110
|
navigation.navigate("PaywallScreen", {
|
|
98
111
|
onClose: handleClose,
|
|
99
112
|
translations,
|
|
@@ -117,14 +130,17 @@ export function usePaywallOrchestrator({
|
|
|
117
130
|
if (showPaywall) {
|
|
118
131
|
closePaywall();
|
|
119
132
|
}
|
|
133
|
+
} else {
|
|
134
|
+
// Reset navigation flag if conditions no longer met
|
|
135
|
+
hasNavigatedRef.current = false;
|
|
120
136
|
}
|
|
121
137
|
}, [
|
|
122
138
|
isNavReady,
|
|
123
139
|
isLocalizationReady,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
140
|
+
isOnboardingComplete,
|
|
141
|
+
showPostOnboardingPaywall,
|
|
142
|
+
paywallShown,
|
|
143
|
+
isAuthModalOpen,
|
|
128
144
|
isPremium,
|
|
129
145
|
showPaywall,
|
|
130
146
|
navigation,
|
|
@@ -144,12 +160,21 @@ export function usePaywallOrchestrator({
|
|
|
144
160
|
onAuthRequired
|
|
145
161
|
]);
|
|
146
162
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
163
|
+
const completeOnboarding = useSubscriptionFlowStore((state) => state.completeOnboarding);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
isPremium,
|
|
167
|
+
packages,
|
|
168
|
+
flowState: {
|
|
169
|
+
isOnboardingComplete,
|
|
170
|
+
showPostOnboardingPaywall,
|
|
171
|
+
paywallShown,
|
|
172
|
+
isAuthModalOpen,
|
|
173
|
+
showFeedback
|
|
174
|
+
},
|
|
151
175
|
markPaywallShown,
|
|
152
176
|
closePostOnboardingPaywall,
|
|
177
|
+
completeOnboarding,
|
|
153
178
|
setShowFeedback
|
|
154
179
|
};
|
|
155
180
|
}
|
|
@@ -85,20 +85,28 @@ export async function processCustomerInfo(
|
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
if (renewalResult.isRenewal) {
|
|
88
|
+
if (!renewalResult.productId || !renewalResult.newExpirationDate) {
|
|
89
|
+
console.error('[CustomerInfoHandler] Invalid renewal state: missing productId or expirationDate');
|
|
90
|
+
return renewalState;
|
|
91
|
+
}
|
|
88
92
|
await handleRenewal(
|
|
89
93
|
userId,
|
|
90
|
-
renewalResult.productId
|
|
91
|
-
renewalResult.newExpirationDate
|
|
94
|
+
renewalResult.productId,
|
|
95
|
+
renewalResult.newExpirationDate,
|
|
92
96
|
customerInfo,
|
|
93
97
|
config.onRenewalDetected
|
|
94
98
|
);
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
if (renewalResult.isPlanChange) {
|
|
102
|
+
if (!renewalResult.productId || !renewalResult.previousProductId) {
|
|
103
|
+
console.error('[CustomerInfoHandler] Invalid plan change state: missing productId(s)');
|
|
104
|
+
return renewalState;
|
|
105
|
+
}
|
|
98
106
|
await handlePlanChange(
|
|
99
107
|
userId,
|
|
100
|
-
renewalResult.productId
|
|
101
|
-
renewalResult.previousProductId
|
|
108
|
+
renewalResult.productId,
|
|
109
|
+
renewalResult.previousProductId,
|
|
102
110
|
renewalResult.isUpgrade,
|
|
103
111
|
customerInfo,
|
|
104
112
|
config.onPlanChanged
|
|
@@ -49,10 +49,12 @@ export async function syncPremiumStatus(
|
|
|
49
49
|
}
|
|
50
50
|
return { success: true };
|
|
51
51
|
} catch (error) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
if (__DEV__) {
|
|
53
|
+
console.error('[PremiumStatusSyncer] Premium status callback failed:', {
|
|
54
|
+
userId,
|
|
55
|
+
error: error instanceof Error ? error.message : String(error)
|
|
56
|
+
});
|
|
57
|
+
}
|
|
56
58
|
|
|
57
59
|
return {
|
|
58
60
|
success: false,
|
|
@@ -85,6 +87,8 @@ export async function notifyRestoreCompleted(
|
|
|
85
87
|
try {
|
|
86
88
|
await config.onRestoreCompleted({ userId, isPremium, customerInfo });
|
|
87
89
|
} catch (error) {
|
|
88
|
-
|
|
90
|
+
if (__DEV__) {
|
|
91
|
+
console.error('[PremiumStatusSyncer] Restore callback failed:', error instanceof Error ? error.message : String(error));
|
|
92
|
+
}
|
|
89
93
|
}
|
|
90
94
|
}
|
|
@@ -104,7 +104,8 @@ const ManagedSubscriptionFlowInner = React.memo<ManagedSubscriptionFlowProps>(({
|
|
|
104
104
|
|
|
105
105
|
const {
|
|
106
106
|
flowState,
|
|
107
|
-
setShowFeedback
|
|
107
|
+
setShowFeedback,
|
|
108
|
+
completeOnboarding
|
|
108
109
|
} = usePaywallOrchestrator({
|
|
109
110
|
navigation,
|
|
110
111
|
isNavReady,
|
|
@@ -165,7 +166,7 @@ const ManagedSubscriptionFlowInner = React.memo<ManagedSubscriptionFlowProps>(({
|
|
|
165
166
|
return (
|
|
166
167
|
<OnboardingScreen
|
|
167
168
|
slides={onboarding.slides}
|
|
168
|
-
onComplete={
|
|
169
|
+
onComplete={completeOnboarding}
|
|
169
170
|
showSkipButton={onboarding.showSkipButton ?? true}
|
|
170
171
|
showBackButton={onboarding.showBackButton ?? true}
|
|
171
172
|
showProgressBar={onboarding.showProgressBar ?? true}
|
|
@@ -105,6 +105,7 @@ export const useSubscriptionFlowStore = createStore<SubscriptionFlowState, Subsc
|
|
|
105
105
|
status: SubscriptionFlowStatus.INITIALIZING,
|
|
106
106
|
syncStatus: SyncStatus.IDLE,
|
|
107
107
|
syncError: null,
|
|
108
|
+
isInitialized: false, // Reset isInitialized to allow fresh initialization
|
|
108
109
|
isOnboardingComplete: false,
|
|
109
110
|
showPostOnboardingPaywall: false,
|
|
110
111
|
showFeedback: false,
|
|
@@ -114,22 +115,3 @@ export const useSubscriptionFlowStore = createStore<SubscriptionFlowState, Subsc
|
|
|
114
115
|
},
|
|
115
116
|
}),
|
|
116
117
|
});
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Hook for backward compatibility and easier consumption.
|
|
120
|
-
* Provides a unified object structure matching the previous implementation.
|
|
121
|
-
*/
|
|
122
|
-
export function useSubscriptionFlow(_userId?: string) {
|
|
123
|
-
const store = useSubscriptionFlowStore();
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
state: store,
|
|
127
|
-
completeOnboarding: store.completeOnboarding,
|
|
128
|
-
closePostOnboardingPaywall: store.closePostOnboardingPaywall,
|
|
129
|
-
closeFeedback: store.closeFeedback,
|
|
130
|
-
setAuthModalOpen: store.setAuthModalOpen,
|
|
131
|
-
markPaywallShown: store.markPaywallShown,
|
|
132
|
-
setShowFeedback: store.setShowFeedback,
|
|
133
|
-
resetFlow: store.resetFlow,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
@@ -6,7 +6,7 @@ import { initializationState } from "../infrastructure/state/initializationState
|
|
|
6
6
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
7
7
|
import { SubscriptionStatusResult } from "./useSubscriptionStatus.types";
|
|
8
8
|
import { isAuthenticated } from "../utils/authGuards";
|
|
9
|
-
import {
|
|
9
|
+
import { SHORT_CACHE_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
|
|
10
10
|
import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
|
|
11
11
|
|
|
12
12
|
export const subscriptionStatusQueryKeys = {
|
|
@@ -43,7 +43,7 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
43
43
|
return SubscriptionManager.checkPremiumStatus();
|
|
44
44
|
},
|
|
45
45
|
enabled: queryEnabled,
|
|
46
|
-
...
|
|
46
|
+
...SHORT_CACHE_CONFIG,
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
usePreviousUserCleanup(userId, queryClient, subscriptionStatusQueryKeys.user);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useQuery } from "@umituz/react-native-design-system/tanstack";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
4
|
-
import {
|
|
4
|
+
import { MEDIUM_CACHE_CONFIG } from "../../../../shared/infrastructure/react-query/queryConfig";
|
|
5
5
|
import type {
|
|
6
6
|
CreditLog,
|
|
7
7
|
TransactionRepositoryConfig,
|
|
@@ -56,7 +56,7 @@ export function useTransactionHistory({
|
|
|
56
56
|
return result.data ?? [];
|
|
57
57
|
},
|
|
58
58
|
enabled: hasUser,
|
|
59
|
-
...
|
|
59
|
+
...MEDIUM_CACHE_CONFIG,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
const transactions = data ?? [];
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
// Domain Layer - Constants & Types
|
|
2
|
-
export
|
|
2
|
+
export {
|
|
3
|
+
USER_TIER,
|
|
4
|
+
SUBSCRIPTION_STATUS,
|
|
5
|
+
PERIOD_TYPE,
|
|
6
|
+
PACKAGE_TYPE,
|
|
7
|
+
PLATFORM,
|
|
8
|
+
PURCHASE_SOURCE,
|
|
9
|
+
PURCHASE_TYPE,
|
|
10
|
+
ANONYMOUS_CACHE_KEY,
|
|
11
|
+
} from "./domains/subscription/core/SubscriptionConstants";
|
|
12
|
+
export type {
|
|
13
|
+
UserTierType,
|
|
14
|
+
SubscriptionStatusType,
|
|
15
|
+
PeriodType,
|
|
16
|
+
PackageType,
|
|
17
|
+
Platform,
|
|
18
|
+
PurchaseSource,
|
|
19
|
+
PurchaseType,
|
|
20
|
+
} from "./domains/subscription/core/SubscriptionConstants";
|
|
3
21
|
export type { SubscriptionMetadata } from "./domains/subscription/core/types/SubscriptionMetadata";
|
|
4
22
|
export type { PremiumStatus } from "./domains/subscription/core/types/PremiumStatus";
|
|
5
23
|
export type { CreditInfo } from "./domains/subscription/core/types/CreditInfo";
|
|
@@ -45,20 +63,33 @@ export { useDeductCredit } from "./domains/credits/presentation/deduct-credit/us
|
|
|
45
63
|
export { useFeatureGate } from "./domains/subscription/presentation/useFeatureGate";
|
|
46
64
|
export { usePaywallVisibility, paywallControl } from "./domains/subscription/presentation/usePaywallVisibility";
|
|
47
65
|
export { usePremium } from "./domains/subscription/presentation/usePremium";
|
|
48
|
-
export {
|
|
66
|
+
export { useSubscriptionFlowStore } from "./domains/subscription/presentation/useSubscriptionFlow";
|
|
49
67
|
export type { SubscriptionFlowState, SubscriptionFlowActions, SubscriptionFlowStore } from "./domains/subscription/presentation/useSubscriptionFlow";
|
|
50
68
|
export { useSubscriptionStatus } from "./domains/subscription/presentation/useSubscriptionStatus";
|
|
51
|
-
export
|
|
52
|
-
export
|
|
53
|
-
export
|
|
69
|
+
export type { SubscriptionStatusResult } from "./domains/subscription/presentation/useSubscriptionStatus.types";
|
|
70
|
+
export { usePaywallFeedback } from "./presentation/hooks/feedback/usePaywallFeedback";
|
|
71
|
+
export {
|
|
72
|
+
usePaywallFeedbackSubmit,
|
|
73
|
+
useSettingsFeedbackSubmit,
|
|
74
|
+
} from "./presentation/hooks/feedback/useFeedbackSubmit";
|
|
75
|
+
export type {
|
|
76
|
+
UsePaywallFeedbackSubmitOptions,
|
|
77
|
+
SettingsFeedbackData,
|
|
78
|
+
UseSettingsFeedbackSubmitOptions,
|
|
79
|
+
} from "./presentation/hooks/feedback/useFeedbackSubmit";
|
|
54
80
|
|
|
55
81
|
// Presentation Layer - Components
|
|
56
|
-
export
|
|
82
|
+
export { PremiumDetailsCard } from "./domains/subscription/presentation/components/details/PremiumDetailsCard";
|
|
83
|
+
export type {
|
|
84
|
+
PremiumDetailsTranslations,
|
|
85
|
+
PremiumDetailsCardProps,
|
|
86
|
+
} from "./domains/subscription/presentation/components/details/PremiumDetailsCardTypes";
|
|
57
87
|
export { PremiumStatusBadge } from "./domains/subscription/presentation/components/details/PremiumStatusBadge";
|
|
58
88
|
export type { PremiumStatusBadgeProps } from "./domains/subscription/presentation/components/details/PremiumStatusBadge.types";
|
|
59
|
-
export
|
|
60
|
-
export
|
|
61
|
-
export
|
|
89
|
+
export { SubscriptionSection } from "./domains/subscription/presentation/components/sections/SubscriptionSection";
|
|
90
|
+
export type { SubscriptionSectionProps } from "./domains/subscription/presentation/components/sections/SubscriptionSection.types";
|
|
91
|
+
export { PaywallFeedbackScreen } from "./domains/subscription/presentation/components/feedback/PaywallFeedbackScreen";
|
|
92
|
+
export type { PaywallFeedbackScreenProps } from "./domains/subscription/presentation/components/feedback/PaywallFeedbackScreen.types";
|
|
62
93
|
export type {
|
|
63
94
|
SubscriptionDetailConfig,
|
|
64
95
|
SubscriptionDetailTranslations,
|
|
@@ -79,11 +110,47 @@ export type {
|
|
|
79
110
|
} from "./domains/credits/core/Credits";
|
|
80
111
|
|
|
81
112
|
// Utils
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
export
|
|
113
|
+
export {
|
|
114
|
+
getCreditAllocation,
|
|
115
|
+
createCreditAmountsFromPackages,
|
|
116
|
+
} from "./utils/creditMapper";
|
|
117
|
+
export {
|
|
118
|
+
isCreditPackage,
|
|
119
|
+
detectPackageType,
|
|
120
|
+
} from "./utils/packageTypeDetector";
|
|
121
|
+
export type { SubscriptionPackageType } from "./utils/packageTypeDetector";
|
|
122
|
+
export {
|
|
123
|
+
formatPrice,
|
|
124
|
+
getBillingPeriodSuffix,
|
|
125
|
+
formatPriceWithPeriod,
|
|
126
|
+
} from "./utils/priceUtils";
|
|
127
|
+
export type { UserTierInfo, PremiumStatusFetcher } from "./utils/types";
|
|
128
|
+
export type { DateLike } from "./utils/dateUtils.core";
|
|
129
|
+
export {
|
|
130
|
+
isNow,
|
|
131
|
+
isPast,
|
|
132
|
+
isFuture,
|
|
133
|
+
isValidDate,
|
|
134
|
+
toSafeDate,
|
|
135
|
+
formatISO,
|
|
136
|
+
now,
|
|
137
|
+
} from "./utils/dateUtils.core";
|
|
138
|
+
export {
|
|
139
|
+
daysBetween,
|
|
140
|
+
daysUntil,
|
|
141
|
+
isSameDay,
|
|
142
|
+
isToday,
|
|
143
|
+
} from "./utils/dateUtils.compare";
|
|
144
|
+
export {
|
|
145
|
+
formatRelative,
|
|
146
|
+
formatShort,
|
|
147
|
+
formatLong,
|
|
148
|
+
} from "./utils/dateUtils.format";
|
|
149
|
+
export {
|
|
150
|
+
addDays,
|
|
151
|
+
addMonths,
|
|
152
|
+
addYears,
|
|
153
|
+
} from "./utils/dateUtils.math";
|
|
87
154
|
export { getAppVersion, validatePlatform } from "./utils/appUtils";
|
|
88
155
|
export { toDate, toISOString, toTimestamp, getCurrentISOString } from "./shared/utils/dateConverter";
|
|
89
156
|
|
|
@@ -1,7 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Query cache configurations for optimal performance
|
|
3
|
+
* Uses event-based invalidation via subscriptionEventBus for real-time updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Short-lived cache for frequently changing data (credits, subscription status)
|
|
8
|
+
* Events automatically invalidate the cache, so we can safely cache for 60s
|
|
9
|
+
*/
|
|
10
|
+
export const SHORT_CACHE_CONFIG = {
|
|
11
|
+
gcTime: 1000 * 60, // 1 minute - keep in memory for 1 minute
|
|
12
|
+
staleTime: 1000 * 30, // 30 seconds - consider stale after 30s
|
|
13
|
+
refetchOnMount: false, // Don't refetch on mount if cache exists
|
|
14
|
+
refetchOnWindowFocus: false, // Don't refetch on app focus
|
|
15
|
+
refetchOnReconnect: true, // Refetch on reconnect only
|
|
7
16
|
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Medium cache for relatively stable data (packages, transaction history)
|
|
20
|
+
*/
|
|
21
|
+
export const MEDIUM_CACHE_CONFIG = {
|
|
22
|
+
gcTime: 1000 * 60 * 5, // 5 minutes
|
|
23
|
+
staleTime: 1000 * 60 * 2, // 2 minutes
|
|
24
|
+
refetchOnMount: false,
|
|
25
|
+
refetchOnWindowFocus: false,
|
|
26
|
+
refetchOnReconnect: true,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Long cache for rarely changing data (config, metadata)
|
|
31
|
+
*/
|
|
32
|
+
export const LONG_CACHE_CONFIG = {
|
|
33
|
+
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
34
|
+
staleTime: 1000 * 60 * 10, // 10 minutes
|
|
35
|
+
refetchOnMount: false,
|
|
36
|
+
refetchOnWindowFocus: false,
|
|
37
|
+
refetchOnReconnect: true,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** @deprecated Use SHORT_CACHE_CONFIG instead */
|
|
41
|
+
export const NO_CACHE_QUERY_CONFIG = SHORT_CACHE_CONFIG;
|