@umituz/react-native-settings 4.23.84 → 4.23.86
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 +3 -3
- package/src/domains/about/presentation/hooks/useAboutInfo.ts +1 -3
- package/src/domains/appearance/presentation/components/ColorPicker.tsx +0 -1
- package/src/domains/appearance/presentation/components/ThemeModeSection.tsx +0 -1
- package/src/domains/appearance/presentation/components/ThemeOption.tsx +0 -1
- package/src/domains/faqs/presentation/screens/FAQScreen.tsx +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +1 -1
- package/src/domains/feedback/presentation/components/FeedbackForm.tsx +1 -2
- package/src/domains/feedback/presentation/components/FeedbackModal.tsx +12 -12
- package/src/domains/feedback/presentation/components/SupportSection.tsx +0 -1
- package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +1 -6
- package/src/domains/localization/infrastructure/components/useLanguageSwitcher.ts +0 -1
- package/src/domains/localization/infrastructure/hooks/useLanguageSelection.ts +0 -4
- package/src/domains/localization/infrastructure/hooks/useTranslation.ts +0 -1
- package/src/domains/localization/infrastructure/storage/LanguageInitializer.ts +0 -1
- package/src/domains/localization/infrastructure/storage/LanguageSwitcher.ts +0 -4
- package/src/domains/localization/infrastructure/storage/LocalizationStore.ts +0 -5
- package/src/domains/localization/presentation/components/LanguageItem.tsx +0 -1
- package/src/domains/localization/presentation/providers/LocalizationManager.tsx +0 -1
- package/src/domains/localization/presentation/screens/LanguageSelectionScreen.tsx +0 -3
- package/src/domains/notifications/infrastructure/services/NotificationService.ts +0 -1
- package/src/domains/notifications/infrastructure/utils/dev.ts +3 -6
- package/src/domains/notifications/quietHours/infrastructure/hooks/useQuietHoursActions.ts +0 -1
- package/src/domains/notifications/reminders/infrastructure/hooks/useReminderActions.ts +0 -6
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +1 -1
- package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +1 -6
- package/src/domains/rating/application/services/RatingService.ts +13 -63
- package/src/domains/rating/infrastructure/storage/RatingStorage.ts +14 -36
- package/src/domains/rating/presentation/hooks/useAppRating.tsx +0 -1
- package/src/infrastructure/utils/dateUtils.ts +61 -0
- package/src/infrastructure/utils/memoComparisonUtils.ts +66 -0
- package/src/infrastructure/utils/sanitizers.ts +49 -0
- package/src/infrastructure/utils/translationHelpers.ts +81 -0
- package/src/infrastructure/utils/validators.ts +59 -0
- package/src/presentation/components/SettingsItemCard.tsx +129 -172
- package/src/presentation/components/settings/SettingsItemCardContent.tsx +70 -0
- package/src/presentation/components/settings/SettingsItemCardRightElement.tsx +42 -0
- package/src/presentation/components/settings/SettingsItemCardSection.tsx +29 -0
- package/src/presentation/hooks/useSettingsScreenConfig.ts +19 -54
- package/src/presentation/navigation/SettingsStackNavigator.tsx +1 -24
- package/src/presentation/navigation/hooks/useSettingsScreens.ts +76 -90
- package/src/presentation/screens/components/GamificationSettingsItem.tsx +20 -17
- package/src/presentation/screens/components/SettingsContent.tsx +29 -0
- package/src/presentation/screens/components/SubscriptionSettingsItem.tsx +13 -4
- package/src/presentation/screens/components/VideoTutorialSettingsItem.tsx +47 -0
- package/src/presentation/screens/components/WalletSettingsItem.tsx +13 -4
- package/src/presentation/screens/components/sections/CustomSettingsList.tsx +12 -5
- package/src/presentation/screens/components/sections/FeatureSettingsSection.tsx +12 -11
- package/src/presentation/screens/components/sections/IdentitySettingsSection.tsx +4 -1
- package/src/presentation/screens/components/sections/ProfileSectionLoader.tsx +16 -10
- package/src/presentation/screens/components/sections/SupportSettingsSection.tsx +9 -4
- package/src/presentation/screens/hooks/useFeatureDetection.ts +2 -0
- package/src/presentation/screens/types/SettingsConfig.ts +8 -1
- package/src/presentation/screens/types/UserFeatureConfig.ts +20 -0
- package/src/presentation/screens/types/index.ts +1 -0
- package/src/presentation/screens/utils/normalizeConfig.ts +7 -1
- package/src/presentation/utils/accountConfigUtils.ts +67 -0
- package/src/presentation/utils/screenFactory.ts +87 -0
- package/src/presentation/utils/userProfileUtils.ts +51 -0
|
@@ -1,195 +1,152 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, Pressable, StyleSheet, type StyleProp, type ViewStyle } from "react-native";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
AtomicSwitch,
|
|
8
|
-
type IconName,
|
|
9
|
-
withAlpha,
|
|
4
|
+
useAppDesignTokens,
|
|
5
|
+
type IconName,
|
|
6
|
+
withAlpha,
|
|
10
7
|
} from "@umituz/react-native-design-system";
|
|
8
|
+
import { validateTitle, validateDescription, validateSwitchProps } from "../../infrastructure/utils/validators";
|
|
9
|
+
import { sanitizeTitle, sanitizeDescription } from "../../infrastructure/utils/sanitizers";
|
|
10
|
+
import { SettingsItemCardRightElement } from "./settings/SettingsItemCardRightElement";
|
|
11
|
+
import { SettingsItemCardContent } from "./settings/SettingsItemCardContent";
|
|
12
|
+
import { SettingsItemCardSection } from "./settings/SettingsItemCardSection";
|
|
11
13
|
|
|
12
14
|
export interface SettingsItemCardProps {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
title: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
icon: IconName;
|
|
18
|
+
onPress?: () => void;
|
|
19
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
20
|
+
sectionTitle?: string;
|
|
21
|
+
rightIcon?: IconName;
|
|
22
|
+
iconBgColor?: string;
|
|
23
|
+
iconColor?: string;
|
|
24
|
+
showChevron?: boolean;
|
|
25
|
+
showSwitch?: boolean;
|
|
26
|
+
switchValue?: boolean;
|
|
27
|
+
onSwitchChange?: (value: boolean) => void;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
noBackground?: boolean;
|
|
30
|
+
hideMargin?: boolean;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
32
33
|
export const SettingsItemCard: React.FC<SettingsItemCardProps> = ({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
title,
|
|
35
|
+
description,
|
|
36
|
+
icon,
|
|
37
|
+
onPress,
|
|
38
|
+
containerStyle: propContainerStyle,
|
|
39
|
+
sectionTitle,
|
|
40
|
+
rightIcon = "chevron-forward",
|
|
41
|
+
iconBgColor,
|
|
42
|
+
iconColor,
|
|
43
|
+
showChevron,
|
|
44
|
+
showSwitch = false,
|
|
45
|
+
switchValue,
|
|
46
|
+
onSwitchChange,
|
|
47
|
+
disabled = false,
|
|
48
|
+
noBackground = false,
|
|
49
|
+
hideMargin = false,
|
|
49
50
|
}) => {
|
|
51
|
+
const tokens = useAppDesignTokens();
|
|
52
|
+
const colors = tokens.colors;
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
// Sanitize props (before hooks)
|
|
55
|
+
const sanitizedTitle = sanitizeTitle(title);
|
|
56
|
+
const sanitizedDescription = sanitizeDescription(description);
|
|
57
|
+
const sanitizedSectionTitle = sectionTitle?.trim().slice(0, 100);
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
if (description && typeof description !== 'string') {
|
|
60
|
-
console.warn('[SettingsItemCard] Invalid description prop:', description);
|
|
61
|
-
}
|
|
62
|
-
if (showSwitch && !onSwitchChange) {
|
|
63
|
-
console.warn('[SettingsItemCard] Switch shown but no onSwitchChange provided. Switch changes will be ignored.');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
59
|
+
const defaultIconBg = iconBgColor || withAlpha(colors.primary, 0.15);
|
|
60
|
+
const defaultIconColor = iconColor || colors.primary;
|
|
61
|
+
const isClickable = !!onPress && !showSwitch;
|
|
62
|
+
const shouldShowChevron = !showSwitch && (showChevron ?? isClickable);
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
// All hooks must be called before any early returns
|
|
65
|
+
const rightElement = React.useMemo(
|
|
66
|
+
() => (
|
|
67
|
+
<SettingsItemCardRightElement
|
|
68
|
+
showSwitch={showSwitch}
|
|
69
|
+
switchValue={switchValue}
|
|
70
|
+
onSwitchChange={onSwitchChange}
|
|
71
|
+
disabled={disabled}
|
|
72
|
+
shouldShowChevron={shouldShowChevron}
|
|
73
|
+
rightIcon={rightIcon}
|
|
74
|
+
/>
|
|
75
|
+
),
|
|
76
|
+
[showSwitch, switchValue, onSwitchChange, disabled, shouldShowChevron, rightIcon]
|
|
77
|
+
);
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
const content = React.useMemo(
|
|
80
|
+
() => (
|
|
81
|
+
<SettingsItemCardContent
|
|
82
|
+
icon={icon}
|
|
83
|
+
title={sanitizedTitle}
|
|
84
|
+
description={sanitizedDescription}
|
|
85
|
+
iconBgColor={defaultIconBg}
|
|
86
|
+
iconColor={defaultIconColor}
|
|
87
|
+
disabled={disabled}
|
|
88
|
+
rightElement={rightElement}
|
|
89
|
+
/>
|
|
90
|
+
),
|
|
91
|
+
[icon, sanitizedTitle, sanitizedDescription, defaultIconBg, defaultIconColor, disabled, rightElement]
|
|
92
|
+
);
|
|
75
93
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
if (shouldShowChevron) {
|
|
91
|
-
return <AtomicIcon name={rightIcon} size="sm" color="textSecondary" />;
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
};
|
|
94
|
+
const containerStyle = React.useMemo(
|
|
95
|
+
() => [
|
|
96
|
+
styles.sectionContainer,
|
|
97
|
+
{
|
|
98
|
+
backgroundColor: noBackground ? "transparent" : colors.surface,
|
|
99
|
+
borderRadius: noBackground ? 0 : tokens.borders.radius.lg,
|
|
100
|
+
},
|
|
101
|
+
hideMargin && { marginBottom: 0 },
|
|
102
|
+
propContainerStyle,
|
|
103
|
+
],
|
|
104
|
+
[noBackground, colors.surface, tokens.borders.radius.lg, hideMargin, propContainerStyle]
|
|
105
|
+
);
|
|
95
106
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
</View>
|
|
101
|
-
<View style={styles.textContainer}>
|
|
102
|
-
<AtomicText
|
|
103
|
-
type="bodyLarge"
|
|
104
|
-
color={disabled ? "onSurfaceVariant" : "onSurface"}
|
|
105
|
-
numberOfLines={1}
|
|
106
|
-
style={{ marginBottom: sanitizedDescription ? tokens.spacing.xs : 0, opacity: disabled ? 0.6 : 1 }}
|
|
107
|
-
>
|
|
108
|
-
{sanitizedTitle}
|
|
109
|
-
</AtomicText>
|
|
110
|
-
{!!sanitizedDescription && (
|
|
111
|
-
<AtomicText type="bodyMedium" color="textSecondary" numberOfLines={2}>
|
|
112
|
-
{sanitizedDescription}
|
|
113
|
-
</AtomicText>
|
|
114
|
-
)}
|
|
115
|
-
</View>
|
|
116
|
-
{renderRightElement()}
|
|
117
|
-
</View>
|
|
118
|
-
);
|
|
107
|
+
// Validate after hooks (silent validation, no render)
|
|
108
|
+
const titleWarning = validateTitle(title);
|
|
109
|
+
const descWarning = validateDescription(description);
|
|
110
|
+
const switchWarning = validateSwitchProps(showSwitch, onSwitchChange);
|
|
119
111
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
styles.sectionContainer,
|
|
124
|
-
{
|
|
125
|
-
backgroundColor: noBackground ? "transparent" : colors.surface,
|
|
126
|
-
borderRadius: noBackground ? 0 : tokens.borders.radius.lg,
|
|
127
|
-
},
|
|
128
|
-
hideMargin && { marginBottom: 0 },
|
|
129
|
-
containerStyle,
|
|
130
|
-
]}
|
|
131
|
-
>
|
|
112
|
+
if (titleWarning || descWarning || switchWarning) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
132
115
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{renderContent()}
|
|
152
|
-
</Pressable>
|
|
153
|
-
) : (
|
|
154
|
-
<View style={styles.itemContainer}>
|
|
155
|
-
{renderContent()}
|
|
156
|
-
</View>
|
|
157
|
-
)}
|
|
116
|
+
return (
|
|
117
|
+
<View style={containerStyle}>
|
|
118
|
+
<SettingsItemCardSection sectionTitle={sanitizedSectionTitle} />
|
|
119
|
+
{isClickable ? (
|
|
120
|
+
<Pressable
|
|
121
|
+
style={({ pressed }) => [
|
|
122
|
+
styles.itemContainer,
|
|
123
|
+
{
|
|
124
|
+
backgroundColor: pressed ? withAlpha(colors.primary, 0.08) : "transparent",
|
|
125
|
+
},
|
|
126
|
+
]}
|
|
127
|
+
onPress={onPress}
|
|
128
|
+
>
|
|
129
|
+
{content}
|
|
130
|
+
</Pressable>
|
|
131
|
+
) : (
|
|
132
|
+
<View style={styles.itemContainer}>
|
|
133
|
+
{content}
|
|
158
134
|
</View>
|
|
159
|
-
|
|
135
|
+
)}
|
|
136
|
+
</View>
|
|
137
|
+
);
|
|
160
138
|
};
|
|
161
139
|
|
|
162
140
|
const styles = StyleSheet.create({
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
alignItems: "center",
|
|
175
|
-
paddingHorizontal: 16,
|
|
176
|
-
paddingVertical: 16,
|
|
177
|
-
minHeight: 72,
|
|
178
|
-
},
|
|
179
|
-
content: {
|
|
180
|
-
flex: 1,
|
|
181
|
-
flexDirection: "row",
|
|
182
|
-
alignItems: "center",
|
|
183
|
-
},
|
|
184
|
-
iconContainer: {
|
|
185
|
-
width: 48,
|
|
186
|
-
height: 48,
|
|
187
|
-
justifyContent: "center",
|
|
188
|
-
alignItems: "center",
|
|
189
|
-
marginRight: 16,
|
|
190
|
-
},
|
|
191
|
-
textContainer: {
|
|
192
|
-
flex: 1,
|
|
193
|
-
marginRight: 8,
|
|
194
|
-
},
|
|
141
|
+
sectionContainer: {
|
|
142
|
+
marginBottom: 16,
|
|
143
|
+
overflow: "hidden",
|
|
144
|
+
},
|
|
145
|
+
itemContainer: {
|
|
146
|
+
flexDirection: "row",
|
|
147
|
+
alignItems: "center",
|
|
148
|
+
paddingHorizontal: 16,
|
|
149
|
+
paddingVertical: 16,
|
|
150
|
+
minHeight: 72,
|
|
151
|
+
},
|
|
195
152
|
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { useAppDesignTokens, AtomicIcon, AtomicText, type IconName } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
export interface SettingsItemCardContentProps {
|
|
6
|
+
icon: IconName;
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
iconBgColor: string;
|
|
10
|
+
iconColor: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
rightElement: React.ReactElement;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const SettingsItemCardContent: React.FC<SettingsItemCardContentProps> = React.memo(({
|
|
16
|
+
icon,
|
|
17
|
+
title,
|
|
18
|
+
description,
|
|
19
|
+
iconBgColor,
|
|
20
|
+
iconColor,
|
|
21
|
+
disabled,
|
|
22
|
+
rightElement,
|
|
23
|
+
}) => {
|
|
24
|
+
const tokens = useAppDesignTokens();
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<View style={styles.content}>
|
|
28
|
+
<View style={[styles.iconContainer, { backgroundColor: iconBgColor, borderRadius: tokens.borders.radius.md }]}>
|
|
29
|
+
<AtomicIcon name={icon} size="lg" customColor={iconColor} />
|
|
30
|
+
</View>
|
|
31
|
+
<View style={styles.textContainer}>
|
|
32
|
+
<AtomicText
|
|
33
|
+
type="bodyLarge"
|
|
34
|
+
color={disabled ? "onSurfaceVariant" : "onSurface"}
|
|
35
|
+
numberOfLines={1}
|
|
36
|
+
style={{ marginBottom: description ? tokens.spacing.xs : 0, opacity: disabled ? 0.6 : 1 }}
|
|
37
|
+
>
|
|
38
|
+
{title}
|
|
39
|
+
</AtomicText>
|
|
40
|
+
{!!description && (
|
|
41
|
+
<AtomicText type="bodyMedium" color="textSecondary" numberOfLines={2}>
|
|
42
|
+
{description}
|
|
43
|
+
</AtomicText>
|
|
44
|
+
)}
|
|
45
|
+
</View>
|
|
46
|
+
{rightElement}
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
SettingsItemCardContent.displayName = "SettingsItemCardContent";
|
|
52
|
+
|
|
53
|
+
const styles = {
|
|
54
|
+
content: {
|
|
55
|
+
flex: 1,
|
|
56
|
+
flexDirection: "row",
|
|
57
|
+
alignItems: "center",
|
|
58
|
+
},
|
|
59
|
+
iconContainer: {
|
|
60
|
+
width: 48,
|
|
61
|
+
height: 48,
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
marginRight: 16,
|
|
65
|
+
},
|
|
66
|
+
textContainer: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
marginRight: 8,
|
|
69
|
+
},
|
|
70
|
+
} as const;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AtomicIcon, AtomicSwitch, type IconName } from "@umituz/react-native-design-system";
|
|
3
|
+
|
|
4
|
+
export interface SettingsItemCardRightElementProps {
|
|
5
|
+
showSwitch: boolean;
|
|
6
|
+
switchValue?: boolean;
|
|
7
|
+
onSwitchChange?: (value: boolean) => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
shouldShowChevron: boolean;
|
|
10
|
+
rightIcon: IconName;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const SettingsItemCardRightElement: React.FC<SettingsItemCardRightElementProps> = React.memo(({
|
|
14
|
+
showSwitch,
|
|
15
|
+
switchValue,
|
|
16
|
+
onSwitchChange,
|
|
17
|
+
disabled,
|
|
18
|
+
shouldShowChevron,
|
|
19
|
+
rightIcon,
|
|
20
|
+
}) => {
|
|
21
|
+
const handleSwitchChange = React.useCallback((value: boolean) => {
|
|
22
|
+
if (onSwitchChange) {
|
|
23
|
+
onSwitchChange(value);
|
|
24
|
+
}
|
|
25
|
+
}, [onSwitchChange]);
|
|
26
|
+
|
|
27
|
+
if (showSwitch) {
|
|
28
|
+
return (
|
|
29
|
+
<AtomicSwitch
|
|
30
|
+
value={!!switchValue}
|
|
31
|
+
onValueChange={handleSwitchChange}
|
|
32
|
+
disabled={disabled}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (shouldShowChevron) {
|
|
37
|
+
return <AtomicIcon name={rightIcon} size="sm" color="textSecondary" />;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
SettingsItemCardRightElement.displayName = "SettingsItemCardRightElement";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { AtomicText } from "@umituz/react-native-design-system";
|
|
4
|
+
|
|
5
|
+
export interface SettingsItemCardSectionProps {
|
|
6
|
+
sectionTitle?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const SettingsItemCardSection: React.FC<SettingsItemCardSectionProps> = React.memo(({ sectionTitle }) => {
|
|
10
|
+
if (!sectionTitle) return null;
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<View style={styles.headerContainer}>
|
|
14
|
+
<AtomicText type="labelMedium" color="textSecondary" style={{ textTransform: "uppercase" }}>
|
|
15
|
+
{sectionTitle}
|
|
16
|
+
</AtomicText>
|
|
17
|
+
</View>
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
SettingsItemCardSection.displayName = "SettingsItemCardSection";
|
|
22
|
+
|
|
23
|
+
const styles = {
|
|
24
|
+
headerContainer: {
|
|
25
|
+
paddingHorizontal: 16,
|
|
26
|
+
paddingTop: 16,
|
|
27
|
+
paddingBottom: 8,
|
|
28
|
+
},
|
|
29
|
+
} as const;
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
createFAQConfig,
|
|
28
28
|
createSubscriptionConfig,
|
|
29
29
|
} from "../utils/config-creators";
|
|
30
|
+
import { createUserProfileDisplay } from "../utils/userProfileUtils";
|
|
31
|
+
import { createAccountConfig } from "../utils/accountConfigUtils";
|
|
30
32
|
import type { SettingsConfig } from "../screens/types";
|
|
31
33
|
import type { FeedbackFormData } from "../utils/config-creators";
|
|
32
34
|
import type { AppInfo, FAQData, UserProfileDisplay, AdditionalScreen } from "../navigation/types";
|
|
@@ -127,60 +129,23 @@ export const useSettingsScreenConfig = (
|
|
|
127
129
|
showAbout, showLegal, showFaqs, showRating,
|
|
128
130
|
]);
|
|
129
131
|
|
|
130
|
-
const userProfile = useMemo(()
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}, [userProfileData,
|
|
147
|
-
|
|
148
|
-
const accountConfig = useMemo((): AccountScreenConfig => {
|
|
149
|
-
const isAnonymous = user?.isAnonymous ?? true;
|
|
150
|
-
|
|
151
|
-
// Ensure t function is available before using it
|
|
152
|
-
const getTranslation = (key: string, fallback: string) =>
|
|
153
|
-
typeof t === 'function' ? t(key) : fallback;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
profile: {
|
|
157
|
-
displayName: userProfileData?.displayName || user?.displayName || getTranslation("settings.profile.anonymousName", "Anonymous"),
|
|
158
|
-
userId: userProfileData?.userId ?? user?.uid ?? undefined,
|
|
159
|
-
isAnonymous,
|
|
160
|
-
avatarUrl: userProfileData?.avatarUrl ?? user?.photoURL ?? undefined,
|
|
161
|
-
benefits: isAnonymous ? [
|
|
162
|
-
getTranslation("settings.profile.benefits.saveHistory", "Save history"),
|
|
163
|
-
getTranslation("settings.profile.benefits.syncDevices", "Sync devices"),
|
|
164
|
-
getTranslation("settings.profile.benefits.cloudSync", "Cloud sync"),
|
|
165
|
-
getTranslation("settings.profile.benefits.secureBackup", "Secure backup"),
|
|
166
|
-
] : undefined,
|
|
167
|
-
},
|
|
168
|
-
isAnonymous,
|
|
169
|
-
editProfileText: getTranslation("settings.account.editProfile", "Edit Profile"),
|
|
170
|
-
onSignIn: handleSignIn,
|
|
171
|
-
accountActions: {
|
|
172
|
-
onLogout: handleSignOut,
|
|
173
|
-
onDeleteAccount: handleDeleteAccount,
|
|
174
|
-
logoutText: getTranslation("settings.account.logout", "Log Out"),
|
|
175
|
-
logoutConfirmTitle: getTranslation("settings.account.logoutConfirmTitle", "Log Out"),
|
|
176
|
-
logoutConfirmMessage: getTranslation("settings.account.logoutConfirmMessage", "Are you sure you want to log out?"),
|
|
177
|
-
cancelText: getTranslation("common.cancel", "Cancel"),
|
|
178
|
-
deleteAccountText: getTranslation("settings.account.deleteAccount", "Delete Account"),
|
|
179
|
-
deleteConfirmTitle: getTranslation("settings.account.deleteConfirmTitle", "Delete Account"),
|
|
180
|
-
deleteConfirmMessage: getTranslation("settings.account.deleteConfirmMessage", "Are you sure you want to delete your account? This action cannot be undone."),
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
}, [user, userProfileData, handleSignIn, handleSignOut, handleDeleteAccount, t]);
|
|
132
|
+
const userProfile = useMemo(() => createUserProfileDisplay({
|
|
133
|
+
profileData: userProfileData,
|
|
134
|
+
t,
|
|
135
|
+
onSignIn: handleSignIn,
|
|
136
|
+
}), [userProfileData, t, handleSignIn]);
|
|
137
|
+
|
|
138
|
+
const accountConfig = useMemo(() => createAccountConfig({
|
|
139
|
+
displayName: userProfileData?.displayName || user?.displayName || undefined,
|
|
140
|
+
userId: userProfileData?.userId ?? user?.uid ?? undefined,
|
|
141
|
+
photoURL: user?.photoURL ?? undefined,
|
|
142
|
+
isAnonymous: user?.isAnonymous,
|
|
143
|
+
avatarUrl: userProfileData?.avatarUrl ?? undefined,
|
|
144
|
+
onSignIn: handleSignIn,
|
|
145
|
+
onLogout: handleSignOut,
|
|
146
|
+
onDeleteAccount: handleDeleteAccount,
|
|
147
|
+
t,
|
|
148
|
+
}), [user, userProfileData, handleSignIn, handleSignOut, handleDeleteAccount, t]);
|
|
184
149
|
|
|
185
150
|
const translatedFaqData = useMemo((): FAQData | undefined => {
|
|
186
151
|
if (!faqData) return undefined;
|
|
@@ -7,20 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import React from "react";
|
|
9
9
|
import {
|
|
10
|
-
useAppDesignTokens,
|
|
11
10
|
StackNavigator,
|
|
12
|
-
type StackScreen,
|
|
13
11
|
type StackNavigatorConfig,
|
|
14
12
|
} from "@umituz/react-native-design-system";
|
|
15
|
-
import { useLocalization
|
|
16
|
-
import { NotificationSettingsScreen } from "../../domains/notifications";
|
|
17
|
-
import { AccountScreen } from "@umituz/react-native-auth";
|
|
18
|
-
import { SettingsScreen } from "../screens/SettingsScreen";
|
|
19
|
-
import { AppearanceScreen } from "../screens/AppearanceScreen";
|
|
20
|
-
import { FAQScreen } from "../../domains/faqs";
|
|
21
|
-
import { AboutScreen } from "../../domains/about";
|
|
22
|
-
import { LegalScreen } from "../../domains/legal";
|
|
23
|
-
import { GamificationScreen } from "../../domains/gamification";
|
|
13
|
+
import { useLocalization } from "../../domains/localization";
|
|
24
14
|
import { useNavigationHandlers, useSettingsScreens } from "./hooks";
|
|
25
15
|
import {
|
|
26
16
|
createNotificationTranslations,
|
|
@@ -33,20 +23,7 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = (pr
|
|
|
33
23
|
const {
|
|
34
24
|
appInfo,
|
|
35
25
|
legalUrls,
|
|
36
|
-
faqData,
|
|
37
|
-
config = {},
|
|
38
|
-
showUserProfile = false,
|
|
39
|
-
userProfile,
|
|
40
|
-
accountConfig,
|
|
41
|
-
additionalScreens = [],
|
|
42
|
-
devSettings,
|
|
43
|
-
customSections = [],
|
|
44
|
-
showHeader = true,
|
|
45
|
-
showCloseButton = false,
|
|
46
|
-
onClose,
|
|
47
|
-
gamificationConfig,
|
|
48
26
|
} = props;
|
|
49
|
-
const tokens = useAppDesignTokens();
|
|
50
27
|
const { t } = useLocalization();
|
|
51
28
|
const { handlePrivacyPress, handleTermsPress, handleEulaPress, aboutConfig } =
|
|
52
29
|
useNavigationHandlers(appInfo, legalUrls);
|