@umituz/react-native-subscription 2.24.13 → 2.24.15

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.24.13",
3
+ "version": "2.24.15",
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",
@@ -12,7 +12,7 @@ import {
12
12
  TextInput,
13
13
  KeyboardAvoidingView,
14
14
  } from "react-native";
15
- import { AtomicText } from "@umituz/react-native-design-system";
15
+ import { AtomicText, BaseModal } from "@umituz/react-native-design-system";
16
16
  import { useAppDesignTokens } from "@umituz/react-native-design-system";
17
17
  import { useLocalization } from "@umituz/react-native-localization";
18
18
  import { usePaywallFeedback } from "../../hooks/feedback/usePaywallFeedback";
@@ -54,7 +54,7 @@ export const PaywallFeedbackModal: React.FC<PaywallFeedbackModalProps> = React.m
54
54
  setOtherText,
55
55
  selectReason,
56
56
  handleSubmit,
57
- handleSkip,
57
+ handleSkip, // BaseModal's onClose will handle skipping
58
58
  canSubmit,
59
59
  } = usePaywallFeedback({ onSubmit, onClose });
60
60
 
@@ -68,101 +68,107 @@ export const PaywallFeedbackModal: React.FC<PaywallFeedbackModalProps> = React.m
68
68
  const displaySubmitText = submitText || t("paywall.feedback.submit");
69
69
  const displayOtherPlaceholder = otherPlaceholder || t("paywall.feedback.otherPlaceholder");
70
70
 
71
+ // BaseModal from design system handles animations, safe areas, keyboard avoiding, and overlay backdrop
71
72
  return (
72
- <Modal
73
+ <BaseModal
73
74
  visible={visible}
74
- transparent
75
- animationType="none"
76
- onRequestClose={handleSkip}
75
+ onClose={handleSkip}
76
+ title={displayTitle}
77
+ subtitle={displaySubtitle}
77
78
  >
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}>
85
- <View style={styles.header}>
86
- <AtomicText type="headlineMedium" style={styles.title}>
87
- {displayTitle}
88
- </AtomicText>
89
- <AtomicText type="bodyMedium" style={styles.subtitle}>
90
- {displaySubtitle}
91
- </AtomicText>
92
- </View>
93
-
94
- <View style={styles.optionsContainer}>
95
- {FEEDBACK_OPTION_IDS.map((optionId, index) => {
96
- const isSelected = selectedReason === optionId;
97
- const isLast = index === FEEDBACK_OPTION_IDS.length - 1;
98
- const displayText = t(`paywall.feedback.reasons.${optionId}`);
99
-
100
- return (
101
- <View key={optionId}>
102
- <TouchableOpacity
103
- style={[
104
- styles.optionRow,
105
- isLast && styles.optionRowLast,
106
- ]}
107
- onPress={() => selectReason(optionId)}
108
- activeOpacity={0.7}
109
- >
110
- <AtomicText
111
- type="bodyMedium"
112
- style={[
113
- styles.optionText,
114
- isSelected && styles.optionTextSelected,
115
- ]}
116
- >
117
- {displayText}
118
- </AtomicText>
119
-
120
- <View
121
- style={[
122
- styles.radioButton,
123
- isSelected && styles.radioButtonSelected,
124
- ]}
125
- >
126
- {isSelected && (
127
- <View style={styles.radioButtonInner} />
128
- )}
129
- </View>
130
- </TouchableOpacity>
131
-
132
- {isSelected && optionId === "other" && (
133
- <View style={styles.inputContainer}>
134
- <TextInput
135
- style={styles.textInput}
136
- placeholder={displayOtherPlaceholder}
137
- placeholderTextColor={tokens.colors.textTertiary}
138
- multiline
139
- maxLength={200}
140
- value={otherText}
141
- onChangeText={setOtherText}
142
- autoFocus
143
- />
144
- </View>
145
- )}
146
- </View>
147
- );
148
- })}
149
- </View>
79
+ <View style={{ paddingHorizontal: tokens.spacing.md, paddingBottom: tokens.spacing.lg }}>
80
+ <View style={[styles.optionsContainer, { backgroundColor: 'transparent', padding: 0 }]}>
81
+ {FEEDBACK_OPTION_IDS.map((optionId, index) => {
82
+ const isSelected = selectedReason === optionId;
83
+ const isOther = optionId === "other";
84
+ const showInput = isSelected && isOther;
85
+ const displayText = t(`paywall.feedback.reasons.${optionId}`);
86
+
87
+ // Dynamic styles for the container
88
+ const containerStyle = {
89
+ marginBottom: tokens.spacing.sm,
90
+ backgroundColor: tokens.colors.surfaceVariant,
91
+ borderRadius: tokens.borderRadius.md,
92
+ overflow: 'hidden' as const, // Ensure children don't bleed out
93
+ };
150
94
 
95
+ return (
96
+ <View key={optionId} style={containerStyle}>
151
97
  <TouchableOpacity
152
- style={styles.submitButton}
153
- onPress={handleSubmit}
154
- disabled={!canSubmit}
155
- activeOpacity={0.8}
98
+ style={{
99
+ borderBottomWidth: showInput ? 1 : 0,
100
+ borderBottomColor: tokens.colors.surface, // Subtle separator
101
+ paddingVertical: tokens.spacing.md,
102
+ paddingHorizontal: tokens.spacing.md,
103
+ flexDirection: 'row',
104
+ alignItems: 'center',
105
+ justifyContent: 'space-between',
106
+ }}
107
+ onPress={() => selectReason(optionId)}
108
+ activeOpacity={0.7}
156
109
  >
157
- <AtomicText type="labelLarge" style={styles.submitText}>
158
- {displaySubmitText}
110
+ <AtomicText
111
+ type="bodyMedium"
112
+ style={[
113
+ styles.optionText,
114
+ isSelected && { color: tokens.colors.primary, fontWeight: '600' },
115
+ ]}
116
+ >
117
+ {displayText}
159
118
  </AtomicText>
119
+
120
+ <View
121
+ style={[
122
+ styles.radioButton,
123
+ isSelected && styles.radioButtonSelected,
124
+ ]}
125
+ >
126
+ {isSelected && (
127
+ <View style={styles.radioButtonInner} />
128
+ )}
129
+ </View>
160
130
  </TouchableOpacity>
161
- </View>
162
- </Pressable>
163
- </KeyboardAvoidingView>
164
- </Pressable>
165
- </Modal>
131
+
132
+ {showInput && (
133
+ <View style={[styles.inputContainer, { backgroundColor: 'transparent', marginTop: 0, padding: tokens.spacing.sm }]}>
134
+ <TextInput
135
+ style={[
136
+ styles.textInput,
137
+ {
138
+ minHeight: 80,
139
+ textAlignVertical: 'top',
140
+ backgroundColor: tokens.colors.surface, // Slightly different background for input
141
+ borderRadius: tokens.borderRadius.sm,
142
+ padding: tokens.spacing.sm,
143
+ }
144
+ ]}
145
+ placeholder={displayOtherPlaceholder}
146
+ placeholderTextColor={tokens.colors.textTertiary}
147
+ multiline
148
+ maxLength={200}
149
+ value={otherText}
150
+ onChangeText={setOtherText}
151
+ autoFocus
152
+ />
153
+ </View>
154
+ )}
155
+ </View>
156
+ );
157
+ })}
158
+ </View>
159
+
160
+ <TouchableOpacity
161
+ style={[styles.submitButton, { marginTop: tokens.spacing.lg }]}
162
+ onPress={handleSubmit}
163
+ disabled={!canSubmit}
164
+ activeOpacity={0.8}
165
+ >
166
+ <AtomicText type="labelLarge" style={styles.submitText}>
167
+ {displaySubmitText}
168
+ </AtomicText>
169
+ </TouchableOpacity>
170
+ </View>
171
+ </BaseModal>
166
172
  );
167
173
  });
168
174