@umituz/react-native-subscription 2.14.9 → 2.14.11
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/paywall/components/PaywallTabBar.tsx +1 -1
- package/src/domains/wallet/domain/entities/CreditCost.ts +45 -0
- package/src/domains/wallet/domain/errors/WalletError.ts +169 -0
- package/src/domains/wallet/domain/types/credit-cost.types.ts +86 -0
- package/src/domains/wallet/domain/types/index.ts +33 -0
- package/src/domains/wallet/domain/types/transaction.types.ts +49 -0
- package/src/domains/wallet/domain/types/wallet.types.ts +50 -0
- package/src/domains/wallet/index.ts +116 -0
- package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +166 -0
- package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +131 -0
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +119 -0
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +150 -0
- package/src/domains/wallet/presentation/components/TransactionList.tsx +127 -0
- package/src/domains/wallet/presentation/components/index.ts +23 -0
- package/src/domains/wallet/presentation/hooks/useProductMetadata.ts +88 -0
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +87 -0
- package/src/domains/wallet/presentation/hooks/useWallet.ts +93 -0
- package/src/domains/wallet/presentation/screens/WalletScreen.tsx +147 -0
- package/src/index.ts +12 -1
- package/src/presentation/components/feedback/PaywallFeedbackModal.tsx +12 -14
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useProductMetadata Hook
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hook for fetching product metadata.
|
|
5
|
+
* Generic and reusable - uses config from ProductMetadataService.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useQuery } from "@tanstack/react-query";
|
|
9
|
+
import type {
|
|
10
|
+
ProductMetadata,
|
|
11
|
+
ProductMetadataConfig,
|
|
12
|
+
ProductType,
|
|
13
|
+
} from "../../domain/types/wallet.types";
|
|
14
|
+
import { ProductMetadataService } from "../../infrastructure/services/ProductMetadataService";
|
|
15
|
+
|
|
16
|
+
const CACHE_CONFIG = {
|
|
17
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
18
|
+
gcTime: 30 * 60 * 1000, // 30 minutes
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const productMetadataQueryKeys = {
|
|
22
|
+
all: ["productMetadata"] as const,
|
|
23
|
+
byType: (type: ProductType) => ["productMetadata", type] as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface UseProductMetadataParams {
|
|
27
|
+
config: ProductMetadataConfig;
|
|
28
|
+
type?: ProductType;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface UseProductMetadataResult {
|
|
33
|
+
products: ProductMetadata[];
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refetch: () => void;
|
|
37
|
+
creditsPackages: ProductMetadata[];
|
|
38
|
+
subscriptionPackages: ProductMetadata[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useProductMetadata({
|
|
42
|
+
config,
|
|
43
|
+
type,
|
|
44
|
+
enabled = true,
|
|
45
|
+
}: UseProductMetadataParams): UseProductMetadataResult {
|
|
46
|
+
const service = new ProductMetadataService(config);
|
|
47
|
+
|
|
48
|
+
const queryKey = type
|
|
49
|
+
? productMetadataQueryKeys.byType(type)
|
|
50
|
+
: productMetadataQueryKeys.all;
|
|
51
|
+
|
|
52
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
53
|
+
queryKey,
|
|
54
|
+
queryFn: async () => {
|
|
55
|
+
if (type) {
|
|
56
|
+
return service.getByType(type);
|
|
57
|
+
}
|
|
58
|
+
return service.getAll();
|
|
59
|
+
},
|
|
60
|
+
enabled,
|
|
61
|
+
staleTime: CACHE_CONFIG.staleTime,
|
|
62
|
+
gcTime: CACHE_CONFIG.gcTime,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const products = data ?? [];
|
|
66
|
+
|
|
67
|
+
const creditsPackages = products.filter((p) => p.type === "credits");
|
|
68
|
+
const subscriptionPackages = products.filter((p) => p.type === "subscription");
|
|
69
|
+
|
|
70
|
+
if (__DEV__) {
|
|
71
|
+
console.log("[useProductMetadata] State", {
|
|
72
|
+
enabled,
|
|
73
|
+
isLoading,
|
|
74
|
+
count: products.length,
|
|
75
|
+
credits: creditsPackages.length,
|
|
76
|
+
subscriptions: subscriptionPackages.length,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
products,
|
|
82
|
+
isLoading,
|
|
83
|
+
error: error as Error | null,
|
|
84
|
+
refetch,
|
|
85
|
+
creditsPackages,
|
|
86
|
+
subscriptionPackages,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useTransactionHistory Hook
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hook for fetching credit transaction history.
|
|
5
|
+
* Generic and reusable - uses config from TransactionRepository.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useQuery } from "@tanstack/react-query";
|
|
9
|
+
import type {
|
|
10
|
+
CreditLog,
|
|
11
|
+
TransactionRepositoryConfig,
|
|
12
|
+
} from "../../domain/types/transaction.types";
|
|
13
|
+
import { TransactionRepository } from "../../infrastructure/repositories/TransactionRepository";
|
|
14
|
+
|
|
15
|
+
const CACHE_CONFIG = {
|
|
16
|
+
staleTime: 60 * 1000, // 1 minute
|
|
17
|
+
gcTime: 5 * 60 * 1000, // 5 minutes
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const transactionQueryKeys = {
|
|
21
|
+
all: ["transactions"] as const,
|
|
22
|
+
user: (userId: string) => ["transactions", userId] as const,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export interface UseTransactionHistoryParams {
|
|
26
|
+
userId: string | undefined;
|
|
27
|
+
config: TransactionRepositoryConfig;
|
|
28
|
+
limit?: number;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface UseTransactionHistoryResult {
|
|
33
|
+
transactions: CreditLog[];
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
error: Error | null;
|
|
36
|
+
refetch: () => void;
|
|
37
|
+
isEmpty: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useTransactionHistory({
|
|
41
|
+
userId,
|
|
42
|
+
config,
|
|
43
|
+
limit = 50,
|
|
44
|
+
enabled = true,
|
|
45
|
+
}: UseTransactionHistoryParams): UseTransactionHistoryResult {
|
|
46
|
+
const repository = new TransactionRepository(config);
|
|
47
|
+
|
|
48
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
49
|
+
queryKey: [...transactionQueryKeys.user(userId ?? ""), limit],
|
|
50
|
+
queryFn: async () => {
|
|
51
|
+
if (!userId) return [];
|
|
52
|
+
|
|
53
|
+
const result = await repository.getTransactions({
|
|
54
|
+
userId,
|
|
55
|
+
limit,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!result.success) {
|
|
59
|
+
throw new Error(result.error?.message || "Failed to fetch history");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return result.data ?? [];
|
|
63
|
+
},
|
|
64
|
+
enabled: enabled && !!userId,
|
|
65
|
+
staleTime: CACHE_CONFIG.staleTime,
|
|
66
|
+
gcTime: CACHE_CONFIG.gcTime,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const transactions = data ?? [];
|
|
70
|
+
|
|
71
|
+
if (__DEV__) {
|
|
72
|
+
console.log("[useTransactionHistory] State", {
|
|
73
|
+
userId,
|
|
74
|
+
enabled,
|
|
75
|
+
isLoading,
|
|
76
|
+
count: transactions.length,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
transactions,
|
|
82
|
+
isLoading,
|
|
83
|
+
error: error as Error | null,
|
|
84
|
+
refetch,
|
|
85
|
+
isEmpty: transactions.length === 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useWallet Hook
|
|
3
|
+
*
|
|
4
|
+
* Orchestration hook for wallet functionality.
|
|
5
|
+
* Combines balance, transactions, and purchase state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useCallback, useMemo } from "react";
|
|
9
|
+
import {
|
|
10
|
+
useCredits,
|
|
11
|
+
type UseCreditsParams,
|
|
12
|
+
} from "../../../../presentation/hooks/useCredits";
|
|
13
|
+
import {
|
|
14
|
+
useTransactionHistory,
|
|
15
|
+
type UseTransactionHistoryParams,
|
|
16
|
+
} from "./useTransactionHistory";
|
|
17
|
+
import type { CreditLog } from "../../domain/types/transaction.types";
|
|
18
|
+
|
|
19
|
+
export interface UseWalletParams {
|
|
20
|
+
userId: string | undefined;
|
|
21
|
+
transactionConfig: UseTransactionHistoryParams["config"];
|
|
22
|
+
transactionLimit?: number;
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface UseWalletResult {
|
|
27
|
+
balance: number;
|
|
28
|
+
textCredits: number;
|
|
29
|
+
imageCredits: number;
|
|
30
|
+
balanceLoading: boolean;
|
|
31
|
+
transactions: CreditLog[];
|
|
32
|
+
transactionsLoading: boolean;
|
|
33
|
+
hasCredits: boolean;
|
|
34
|
+
refetchBalance: () => void;
|
|
35
|
+
refetchTransactions: () => void;
|
|
36
|
+
refetchAll: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useWallet({
|
|
40
|
+
userId,
|
|
41
|
+
transactionConfig,
|
|
42
|
+
transactionLimit = 50,
|
|
43
|
+
enabled = true,
|
|
44
|
+
}: UseWalletParams): UseWalletResult {
|
|
45
|
+
const creditsParams: UseCreditsParams = {
|
|
46
|
+
userId,
|
|
47
|
+
enabled,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const transactionParams: UseTransactionHistoryParams = {
|
|
51
|
+
userId,
|
|
52
|
+
config: transactionConfig,
|
|
53
|
+
limit: transactionLimit,
|
|
54
|
+
enabled,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const {
|
|
58
|
+
credits,
|
|
59
|
+
isLoading: balanceLoading,
|
|
60
|
+
refetch: refetchBalance,
|
|
61
|
+
hasTextCredits,
|
|
62
|
+
hasImageCredits,
|
|
63
|
+
} = useCredits(creditsParams);
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
transactions,
|
|
67
|
+
isLoading: transactionsLoading,
|
|
68
|
+
refetch: refetchTransactions,
|
|
69
|
+
} = useTransactionHistory(transactionParams);
|
|
70
|
+
|
|
71
|
+
const balance = useMemo(() => {
|
|
72
|
+
if (!credits) return 0;
|
|
73
|
+
return credits.textCredits + credits.imageCredits;
|
|
74
|
+
}, [credits]);
|
|
75
|
+
|
|
76
|
+
const refetchAll = useCallback(() => {
|
|
77
|
+
refetchBalance();
|
|
78
|
+
refetchTransactions();
|
|
79
|
+
}, [refetchBalance, refetchTransactions]);
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
balance,
|
|
83
|
+
textCredits: credits?.textCredits ?? 0,
|
|
84
|
+
imageCredits: credits?.imageCredits ?? 0,
|
|
85
|
+
balanceLoading,
|
|
86
|
+
transactions,
|
|
87
|
+
transactionsLoading,
|
|
88
|
+
hasCredits: hasTextCredits || hasImageCredits,
|
|
89
|
+
refetchBalance,
|
|
90
|
+
refetchTransactions,
|
|
91
|
+
refetchAll,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet Screen
|
|
3
|
+
*
|
|
4
|
+
* Generic wallet screen composition.
|
|
5
|
+
* Props-driven for full customization across apps.
|
|
6
|
+
* No business logic - pure presentation.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { View, StyleSheet, ActivityIndicator, TouchableOpacity } from "react-native";
|
|
11
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
12
|
+
import {
|
|
13
|
+
useAppDesignTokens,
|
|
14
|
+
AtomicText,
|
|
15
|
+
AtomicIcon,
|
|
16
|
+
ScreenLayout,
|
|
17
|
+
} from "@umituz/react-native-design-system";
|
|
18
|
+
import {
|
|
19
|
+
BalanceCard,
|
|
20
|
+
type BalanceCardTranslations,
|
|
21
|
+
} from "../components/BalanceCard";
|
|
22
|
+
import {
|
|
23
|
+
TransactionList,
|
|
24
|
+
type TransactionListTranslations,
|
|
25
|
+
} from "../components/TransactionList";
|
|
26
|
+
import type { CreditLog } from "../../domain/types/transaction.types";
|
|
27
|
+
|
|
28
|
+
export interface WalletScreenTranslations
|
|
29
|
+
extends BalanceCardTranslations,
|
|
30
|
+
TransactionListTranslations {
|
|
31
|
+
screenTitle: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface WalletScreenConfig {
|
|
35
|
+
balance: number;
|
|
36
|
+
balanceLoading: boolean;
|
|
37
|
+
transactions: CreditLog[];
|
|
38
|
+
transactionsLoading: boolean;
|
|
39
|
+
translations: WalletScreenTranslations;
|
|
40
|
+
onBack?: () => void;
|
|
41
|
+
dateFormatter?: (timestamp: number) => string;
|
|
42
|
+
maxTransactionHeight?: number;
|
|
43
|
+
balanceIconName?: string;
|
|
44
|
+
footer?: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WalletScreenProps {
|
|
48
|
+
config: WalletScreenConfig;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const WalletScreen: React.FC<WalletScreenProps> = ({ config }) => {
|
|
52
|
+
const tokens = useAppDesignTokens();
|
|
53
|
+
const insets = useSafeAreaInsets();
|
|
54
|
+
|
|
55
|
+
const renderHeader = () => (
|
|
56
|
+
<View style={[styles.header, { paddingTop: insets.top + 12 }]}>
|
|
57
|
+
{config.onBack && (
|
|
58
|
+
<TouchableOpacity
|
|
59
|
+
onPress={config.onBack}
|
|
60
|
+
style={styles.backButton}
|
|
61
|
+
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
62
|
+
>
|
|
63
|
+
<AtomicIcon
|
|
64
|
+
name="ArrowLeft"
|
|
65
|
+
size="lg"
|
|
66
|
+
customColor={tokens.colors.textPrimary}
|
|
67
|
+
/>
|
|
68
|
+
</TouchableOpacity>
|
|
69
|
+
)}
|
|
70
|
+
<AtomicText
|
|
71
|
+
type="titleLarge"
|
|
72
|
+
style={{ color: tokens.colors.textPrimary, fontWeight: "700" }}
|
|
73
|
+
>
|
|
74
|
+
{config.translations.screenTitle}
|
|
75
|
+
</AtomicText>
|
|
76
|
+
</View>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const renderBalance = () => {
|
|
80
|
+
if (config.balanceLoading) {
|
|
81
|
+
return (
|
|
82
|
+
<View style={styles.loadingContainer}>
|
|
83
|
+
<ActivityIndicator size="large" color={tokens.colors.primary} />
|
|
84
|
+
<AtomicText
|
|
85
|
+
type="bodyMedium"
|
|
86
|
+
style={[styles.loadingText, { color: tokens.colors.textSecondary }]}
|
|
87
|
+
>
|
|
88
|
+
{config.translations.loading}
|
|
89
|
+
</AtomicText>
|
|
90
|
+
</View>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<BalanceCard
|
|
96
|
+
balance={config.balance}
|
|
97
|
+
translations={config.translations}
|
|
98
|
+
iconName={config.balanceIconName}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<ScreenLayout
|
|
105
|
+
scrollable={true}
|
|
106
|
+
edges={["bottom"]}
|
|
107
|
+
backgroundColor={tokens.colors.backgroundPrimary}
|
|
108
|
+
contentContainerStyle={styles.content}
|
|
109
|
+
footer={config.footer}
|
|
110
|
+
>
|
|
111
|
+
{renderHeader()}
|
|
112
|
+
{renderBalance()}
|
|
113
|
+
<TransactionList
|
|
114
|
+
transactions={config.transactions}
|
|
115
|
+
loading={config.transactionsLoading}
|
|
116
|
+
translations={config.translations}
|
|
117
|
+
maxHeight={config.maxTransactionHeight}
|
|
118
|
+
dateFormatter={config.dateFormatter}
|
|
119
|
+
/>
|
|
120
|
+
</ScreenLayout>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const styles = StyleSheet.create({
|
|
125
|
+
content: {
|
|
126
|
+
paddingBottom: 24,
|
|
127
|
+
},
|
|
128
|
+
header: {
|
|
129
|
+
flexDirection: "row",
|
|
130
|
+
alignItems: "center",
|
|
131
|
+
paddingHorizontal: 16,
|
|
132
|
+
paddingBottom: 12,
|
|
133
|
+
},
|
|
134
|
+
backButton: {
|
|
135
|
+
marginRight: 16,
|
|
136
|
+
},
|
|
137
|
+
loadingContainer: {
|
|
138
|
+
padding: 40,
|
|
139
|
+
alignItems: "center",
|
|
140
|
+
justifyContent: "center",
|
|
141
|
+
gap: 12,
|
|
142
|
+
},
|
|
143
|
+
loadingText: {
|
|
144
|
+
fontSize: 14,
|
|
145
|
+
fontWeight: "500",
|
|
146
|
+
},
|
|
147
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// =============================================================================
|
|
9
|
-
// DOMAIN
|
|
9
|
+
// WALLET DOMAIN (Complete)
|
|
10
10
|
// =============================================================================
|
|
11
11
|
|
|
12
|
+
export * from "./domains/wallet";
|
|
13
|
+
|
|
12
14
|
// =============================================================================
|
|
13
15
|
// DOMAIN LAYER - Subscription Status
|
|
14
16
|
// =============================================================================
|
|
@@ -231,6 +233,9 @@ export type {
|
|
|
231
233
|
|
|
232
234
|
export { DEFAULT_CREDITS_CONFIG } from "./domain/entities/Credits";
|
|
233
235
|
|
|
236
|
+
// CreditCost, Transaction types, Wallet types, Credit-cost types
|
|
237
|
+
// are now exported from "./domains/wallet"
|
|
238
|
+
|
|
234
239
|
// =============================================================================
|
|
235
240
|
// CREDITS SYSTEM - Repository
|
|
236
241
|
// =============================================================================
|
|
@@ -240,6 +245,9 @@ export {
|
|
|
240
245
|
createCreditsRepository,
|
|
241
246
|
} from "./infrastructure/repositories/CreditsRepository";
|
|
242
247
|
|
|
248
|
+
// TransactionRepository and ProductMetadataService
|
|
249
|
+
// are now exported from "./domains/wallet"
|
|
250
|
+
|
|
243
251
|
// =============================================================================
|
|
244
252
|
// CREDITS SYSTEM - Configuration (Module-Level Provider)
|
|
245
253
|
// =============================================================================
|
|
@@ -316,6 +324,9 @@ export {
|
|
|
316
324
|
|
|
317
325
|
export { useDevTestCallbacks } from "./presentation/hooks/useDevTestCallbacks";
|
|
318
326
|
|
|
327
|
+
// Wallet hooks, components, and screens
|
|
328
|
+
// are now exported from "./domains/wallet"
|
|
329
|
+
|
|
319
330
|
// =============================================================================
|
|
320
331
|
// CREDITS SYSTEM - Utilities
|
|
321
332
|
// =============================================================================
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
View,
|
|
9
9
|
Modal,
|
|
10
10
|
TouchableOpacity,
|
|
11
|
-
|
|
11
|
+
Pressable,
|
|
12
12
|
TextInput,
|
|
13
13
|
KeyboardAvoidingView,
|
|
14
14
|
} from "react-native";
|
|
@@ -75,14 +75,13 @@ export const PaywallFeedbackModal: React.FC<PaywallFeedbackModalProps> = React.m
|
|
|
75
75
|
animationType="fade"
|
|
76
76
|
onRequestClose={handleSkip}
|
|
77
77
|
>
|
|
78
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
>
|
|
84
|
-
<
|
|
85
|
-
<View style={styles.container}>
|
|
78
|
+
<Pressable onPress={handleSkip} style={styles.overlay}>
|
|
79
|
+
<KeyboardAvoidingView
|
|
80
|
+
behavior="padding"
|
|
81
|
+
style={styles.keyboardView}
|
|
82
|
+
>
|
|
83
|
+
<Pressable onPress={(e) => e.stopPropagation()}>
|
|
84
|
+
<View style={styles.container}>
|
|
86
85
|
<View style={styles.header}>
|
|
87
86
|
<AtomicText type="headlineMedium" style={styles.title}>
|
|
88
87
|
{displayTitle}
|
|
@@ -159,11 +158,10 @@ export const PaywallFeedbackModal: React.FC<PaywallFeedbackModalProps> = React.m
|
|
|
159
158
|
{displaySubmitText}
|
|
160
159
|
</AtomicText>
|
|
161
160
|
</TouchableOpacity>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
</TouchableWithoutFeedback>
|
|
161
|
+
</View>
|
|
162
|
+
</Pressable>
|
|
163
|
+
</KeyboardAvoidingView>
|
|
164
|
+
</Pressable>
|
|
167
165
|
</Modal>
|
|
168
166
|
);
|
|
169
167
|
});
|