@umituz/react-native-subscription 2.44.1 → 2.45.1

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.
@@ -6,208 +6,112 @@
6
6
  * Collects user feedback after they decline the paywall.
7
7
  */
8
8
 
9
- import React, { useMemo, useCallback } from "react";
10
- import { View, ScrollView, TouchableOpacity } from "react-native";
11
- import { AtomicText, AtomicIcon } from "@umituz/react-native-design-system/atoms";
9
+ import React, { useMemo } from "react";
10
+ import { View, ScrollView } from "react-native";
12
11
  import { useSafeAreaInsets } from "@umituz/react-native-design-system/safe-area";
13
- import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
14
12
  import { usePaywallFeedback } from "../../../../../presentation/hooks/feedback/usePaywallFeedback";
15
- import { FeedbackOption } from "./FeedbackOption";
16
13
  import type { PaywallFeedbackScreenProps, PaywallFeedbackTranslations } from "./PaywallFeedbackScreen.types";
14
+ import {
15
+ FeedbackCloseButton,
16
+ FeedbackHeader,
17
+ FeedbackOptionsList,
18
+ FeedbackSubmitButton,
19
+ } from "./PaywallFeedbackScreen.parts";
17
20
 
18
21
  // Re-export types for convenience
19
22
  export type { PaywallFeedbackTranslations, PaywallFeedbackScreenProps };
20
23
 
21
- const FEEDBACK_OPTION_IDS = [
22
- "too_expensive",
23
- "no_need",
24
- "trying_out",
25
- "technical_issues",
26
- "other",
27
- ] as const;
28
-
29
24
  export const PaywallFeedbackScreen: React.FC<PaywallFeedbackScreenProps> = React.memo(({
30
- translations,
31
- onClose,
32
- onSubmit,
25
+ translations,
26
+ onClose,
27
+ onSubmit,
33
28
  }) => {
34
- const tokens = useAppDesignTokens();
35
- const insets = useSafeAreaInsets();
36
-
37
- const {
38
- selectedReason,
39
- otherText,
40
- setOtherText,
41
- selectReason,
42
- handleSubmit,
43
- handleSkip,
44
- canSubmit,
45
- } = usePaywallFeedback({ onSubmit, onClose });
46
-
47
- const screenStyles = useMemo(() => createScreenStyles(tokens, insets), [tokens, insets]);
29
+ const insets = useSafeAreaInsets();
48
30
 
49
- const handleSkipPress = useCallback(() => {
50
- handleSkip();
51
- }, [handleSkip]);
31
+ const {
32
+ selectedReason,
33
+ otherText,
34
+ setOtherText,
35
+ selectReason,
36
+ handleSubmit,
37
+ handleSkip,
38
+ canSubmit,
39
+ } = usePaywallFeedback({ onSubmit, onClose });
52
40
 
53
- return (
54
- <View style={[screenStyles.container, { backgroundColor: tokens.colors.backgroundPrimary }]}>
55
- {/* Close button */}
56
- <TouchableOpacity
57
- onPress={handleSkipPress}
58
- style={[screenStyles.closeBtn, { backgroundColor: tokens.colors.surfaceSecondary, top: Math.max(insets.top, 12) }]}
59
- hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
60
- >
61
- <AtomicIcon name="close-outline" size="md" customColor={tokens.colors.textPrimary} />
62
- </TouchableOpacity>
41
+ const screenStyles = useMemo(() => createScreenStyles(insets), [insets]);
63
42
 
64
- {/* Scrollable content */}
65
- <ScrollView
66
- style={screenStyles.scrollContainer}
67
- contentContainerStyle={screenStyles.scrollContent}
68
- showsVerticalScrollIndicator={false}
69
- >
70
- {/* Header */}
71
- <View style={screenStyles.header}>
72
- <AtomicText
73
- type="headlineMedium"
74
- style={[screenStyles.title, { color: tokens.colors.textPrimary }]}
75
- >
76
- {translations.title}
77
- </AtomicText>
78
- {translations.subtitle && (
79
- <AtomicText
80
- type="bodyMedium"
81
- style={[screenStyles.subtitle, { color: tokens.colors.textSecondary }]}
82
- >
83
- {translations.subtitle}
84
- </AtomicText>
85
- )}
86
- </View>
43
+ return (
44
+ <View style={[screenStyles.container, { backgroundColor: 'white', opacity: 1 }]}>
45
+ {/* Close button */}
46
+ <FeedbackCloseButton
47
+ onPress={handleSkip}
48
+ topInset={insets.top}
49
+ backgroundColor="rgba(0,0,0,0.05)"
50
+ iconColor="#000"
51
+ />
87
52
 
88
- {/* Feedback options */}
89
- <View style={screenStyles.optionsContainer}>
90
- {FEEDBACK_OPTION_IDS.map((optionId) => {
91
- const isSelected = selectedReason === optionId;
92
- const isOther = optionId === "other";
93
- const showInput = isSelected && isOther;
53
+ {/* Scrollable content */}
54
+ <ScrollView
55
+ style={screenStyles.scrollContainer}
56
+ contentContainerStyle={screenStyles.scrollContent}
57
+ showsVerticalScrollIndicator={false}
58
+ >
59
+ {/* Header */}
60
+ <FeedbackHeader
61
+ title={translations.title}
62
+ subtitle={translations.subtitle}
63
+ titleColor="#000"
64
+ subtitleColor="#666"
65
+ style={screenStyles.header}
66
+ />
94
67
 
95
- return (
96
- <FeedbackOption
97
- key={optionId}
98
- isSelected={isSelected}
99
- text={translations.reasons[optionId]}
100
- showInput={showInput}
101
- placeholder={translations.otherPlaceholder}
102
- inputValue={otherText}
103
- onSelect={() => selectReason(optionId)}
104
- onChangeText={setOtherText}
105
- />
106
- );
107
- })}
108
- </View>
109
- </ScrollView>
110
-
111
- {/* Sticky footer - Submit button */}
112
- <View style={[screenStyles.footer, { paddingBottom: Math.max(insets.bottom, 16) }]}>
113
- <TouchableOpacity
114
- style={[
115
- screenStyles.submitButton,
116
- {
117
- backgroundColor: canSubmit ? tokens.colors.primary : tokens.colors.surfaceSecondary,
118
- opacity: canSubmit ? 1 : 0.6,
119
- }
120
- ]}
121
- onPress={handleSubmit}
122
- disabled={!canSubmit}
123
- activeOpacity={0.8}
124
- >
125
- <AtomicText
126
- type="titleLarge"
127
- style={[
128
- screenStyles.submitText,
129
- { color: canSubmit ? tokens.colors.onPrimary : tokens.colors.textDisabled }
130
- ]}
131
- >
132
- {translations.submit}
133
- </AtomicText>
134
- </TouchableOpacity>
135
- </View>
68
+ {/* Feedback options */}
69
+ <View style={screenStyles.optionsContainer}>
70
+ <FeedbackOptionsList
71
+ translations={translations}
72
+ selectedReason={selectedReason}
73
+ otherText={otherText}
74
+ onSelectReason={selectReason}
75
+ onSetOtherText={setOtherText}
76
+ />
136
77
  </View>
137
- );
78
+ </ScrollView>
79
+
80
+ {/* Submit button */}
81
+ <FeedbackSubmitButton
82
+ title={translations.submit}
83
+ canSubmit={canSubmit}
84
+ backgroundColor="#007AFF"
85
+ textColor="#FFF"
86
+ onPress={handleSubmit}
87
+ bottomInset={insets.bottom}
88
+ />
89
+ </View>
90
+ );
138
91
  });
139
92
 
140
93
  PaywallFeedbackScreen.displayName = "PaywallFeedbackScreen";
141
94
 
142
- const createScreenStyles = (
143
- tokens: {
144
- colors: { backgroundPrimary: string; border: string; onPrimary: string; textDisabled: string; surfaceSecondary: string; primary: string };
145
- spacing: { xl: number; sm: number; md: number; lg: number };
146
- },
147
- _insets: { top: number; bottom: number }
148
- ) => ({
149
- container: {
150
- flex: 1,
151
- opacity: 1,
152
- },
153
- closeBtn: {
154
- position: 'absolute' as const,
155
- top: 12,
156
- right: 12,
157
- width: 40,
158
- height: 40,
159
- borderRadius: 20,
160
- zIndex: 1000,
161
- justifyContent: 'center' as const,
162
- alignItems: 'center' as const,
163
- },
164
- scrollContainer: {
165
- flex: 1,
166
- },
167
- scrollContent: {
168
- paddingTop: 80,
169
- paddingBottom: 120,
170
- },
171
- header: {
172
- paddingHorizontal: tokens.spacing.xl,
173
- marginBottom: tokens.spacing.xl + 8,
174
- },
175
- title: {
176
- marginBottom: tokens.spacing.md + 4,
177
- },
178
- subtitle: {
179
- lineHeight: 24,
180
- },
181
- optionsContainer: {
182
- paddingHorizontal: tokens.spacing.xl,
183
- gap: tokens.spacing.md,
184
- },
185
- footer: {
186
- position: 'absolute' as const,
187
- bottom: 0,
188
- left: 0,
189
- right: 0,
190
- paddingHorizontal: tokens.spacing.xl,
191
- paddingTop: tokens.spacing.lg,
192
- paddingBottom: tokens.spacing.lg,
193
- backgroundColor: tokens.colors.backgroundPrimary,
194
- borderTopWidth: 1,
195
- borderTopColor: tokens.colors.border,
196
- opacity: 1,
197
- },
198
- submitButton: {
199
- borderRadius: 16,
200
- paddingVertical: 18,
201
- alignItems: 'center' as const,
202
- shadowColor: "#000",
203
- shadowOffset: { width: 0, height: 2 },
204
- shadowOpacity: 0.1,
205
- shadowRadius: 4,
206
- elevation: 3,
207
- },
208
- submitText: {
209
- fontWeight: "700" as const,
210
- fontSize: 17,
211
- letterSpacing: 0.3,
212
- },
95
+ // ============================================================================
96
+ // STYLES
97
+ // ============================================================================
98
+
99
+ const createScreenStyles = (insets: { top: number; bottom: number }) => ({
100
+ container: {
101
+ flex: 1,
102
+ },
103
+ scrollContainer: {
104
+ flex: 1,
105
+ },
106
+ scrollContent: {
107
+ paddingTop: 80,
108
+ paddingBottom: 120,
109
+ },
110
+ header: {
111
+ paddingHorizontal: 24,
112
+ marginBottom: 32,
113
+ },
114
+ optionsContainer: {
115
+ paddingHorizontal: 24,
116
+ },
213
117
  });