@umituz/react-native-settings 4.23.82 → 4.23.83

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.
Files changed (22) hide show
  1. package/package.json +1 -1
  2. package/src/domains/disclaimer/presentation/screens/DisclaimerScreen.tsx +15 -1
  3. package/src/domains/faqs/presentation/screens/FAQScreen.tsx +25 -23
  4. package/src/domains/feedback/presentation/components/FeedbackForm.styles.ts +51 -0
  5. package/src/domains/feedback/presentation/components/FeedbackForm.tsx +3 -49
  6. package/src/domains/gamification/components/GamificationScreen/GamificationScreen.tsx +54 -57
  7. package/src/domains/legal/presentation/screens/LegalContentScreen.tsx +8 -8
  8. package/src/domains/legal/presentation/screens/LegalScreen.tsx +14 -3
  9. package/src/domains/notifications/index.ts +1 -1
  10. package/src/domains/notifications/presentation/screens/NotificationsScreen.tsx +53 -26
  11. package/src/domains/notifications/reminders/presentation/components/ReminderForm.constants.ts +35 -0
  12. package/src/domains/notifications/reminders/presentation/components/ReminderForm.styles.ts +22 -0
  13. package/src/domains/notifications/reminders/presentation/components/ReminderForm.tsx +13 -57
  14. package/src/domains/notifications/reminders/presentation/screens/ReminderListScreen.tsx +25 -10
  15. package/src/domains/video-tutorials/presentation/screens/VideoTutorialsScreen.tsx +17 -6
  16. package/src/presentation/components/SettingsFooter.tsx +3 -3
  17. package/src/presentation/navigation/SettingsStackNavigator.tsx +32 -174
  18. package/src/presentation/navigation/hooks/index.ts +1 -0
  19. package/src/presentation/navigation/hooks/useSettingsScreens.ts +163 -0
  20. package/src/presentation/screens/SettingsScreen.tsx +0 -1
  21. package/src/presentation/screens/components/SettingsHeader.tsx +2 -5
  22. package/src/utils/appUtils.ts +7 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.23.82",
3
+ "version": "4.23.83",
4
4
  "description": "Complete settings hub for React Native apps - consolidated package with settings, localization, about, legal, appearance, feedback, FAQs, rating, and gamification",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -14,7 +14,13 @@
14
14
 
15
15
  import React from 'react';
16
16
  import { View, StyleSheet } from 'react-native';
17
- import { useAppDesignTokens, withAlpha, ScreenLayout } from '@umituz/react-native-design-system';
17
+ import {
18
+ useAppDesignTokens,
19
+ withAlpha,
20
+ ScreenLayout,
21
+ useAppNavigation,
22
+ NavigationHeader
23
+ } from '@umituz/react-native-design-system';
18
24
  import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
19
25
  import type { IconName } from '@umituz/react-native-design-system';
20
26
  import { useLocalization } from '../../../localization';
@@ -46,12 +52,20 @@ export const DisclaimerScreen: React.FC<DisclaimerScreenProps> = ({
46
52
  const displayTitle = title || t(titleKey);
47
53
  const displayContent = content || t(contentKey);
48
54
 
55
+ const navigation = useAppNavigation();
56
+
49
57
  return (
50
58
  <ScreenLayout
51
59
  scrollable={true}
52
60
  edges={['bottom']}
53
61
  contentContainerStyle={styles.scrollContent}
54
62
  hideScrollIndicator={false}
63
+ header={
64
+ <NavigationHeader
65
+ title={displayTitle}
66
+ onBackPress={() => navigation.goBack()}
67
+ />
68
+ }
55
69
  >
56
70
  {/* Icon Header */}
57
71
  <View style={styles.iconHeader}>
@@ -6,7 +6,7 @@
6
6
 
7
7
  import React, { useMemo } from 'react';
8
8
  import { View, ScrollView, StyleSheet, ViewStyle, TextStyle, useWindowDimensions } from 'react-native';
9
- import { useAppDesignTokens, AtomicText, ScreenLayout, getContentMaxWidth } from '@umituz/react-native-design-system';
9
+ import { useAppDesignTokens, AtomicText, ScreenLayout, getContentMaxWidth, NavigationHeader, useAppNavigation } from '@umituz/react-native-design-system';
10
10
  import { FAQCategory } from '../../domain/entities/FAQEntity';
11
11
  import { useFAQSearch } from '../hooks/useFAQSearch';
12
12
  import { useFAQExpansion } from '../hooks/useFAQExpansion';
@@ -45,6 +45,7 @@ export const FAQScreen: React.FC<FAQScreenProps> = ({
45
45
  renderHeader,
46
46
  styles: customStyles,
47
47
  }) => {
48
+ const navigation = useAppNavigation();
48
49
  const tokens = useAppDesignTokens();
49
50
  const { width: windowWidth } = useWindowDimensions();
50
51
  const contentMaxWidth = useMemo(() => getContentMaxWidth(windowWidth), [windowWidth]);
@@ -67,6 +68,21 @@ export const FAQScreen: React.FC<FAQScreenProps> = ({
67
68
  [tokens]
68
69
  );
69
70
 
71
+ const handleBack = () => {
72
+ if (onBack) {
73
+ onBack();
74
+ } else {
75
+ navigation.goBack();
76
+ }
77
+ };
78
+
79
+ const header = renderHeader ? renderHeader({ onBack: handleBack }) : (
80
+ <NavigationHeader
81
+ title={headerTitle}
82
+ onBackPress={handleBack}
83
+ />
84
+ );
85
+
70
86
  const renderContent = () => {
71
87
  if (searchQuery && !hasResults) {
72
88
  return (
@@ -81,13 +97,6 @@ export const FAQScreen: React.FC<FAQScreenProps> = ({
81
97
  return (
82
98
  <View style={{ flex: 1 }}>
83
99
  <View style={[styles.header, customStyles?.header]}>
84
- <AtomicText
85
- type="headlineMedium"
86
- color="textPrimary"
87
- style={{ marginBottom: tokens.spacing.md, fontWeight: '700' }}
88
- >
89
- {headerTitle}
90
- </AtomicText>
91
100
  <FAQSearchBar
92
101
  value={searchQuery}
93
102
  onChangeText={setSearchQuery}
@@ -116,23 +125,16 @@ export const FAQScreen: React.FC<FAQScreenProps> = ({
116
125
  );
117
126
  };
118
127
 
119
- if (renderHeader && onBack) {
120
- return (
121
- <View style={{ flex: 1, backgroundColor: tokens.colors.backgroundPrimary }}>
122
- <View style={[styles.container, customStyles?.container]}>
123
- <View style={{ alignSelf: 'center', width: '100%', maxWidth: contentMaxWidth }}>
124
- {renderHeader({ onBack })}
125
- </View>
126
- {renderContent()}
127
- </View>
128
- </View>
129
- );
130
- }
131
-
132
128
  return (
133
- <ScreenLayout edges={['bottom']} scrollable={false}>
129
+ <ScreenLayout
130
+ edges={['bottom']}
131
+ scrollable={false}
132
+ header={header}
133
+ >
134
134
  <View style={[styles.container, customStyles?.container]}>
135
- {renderContent()}
135
+ <View style={{ alignSelf: 'center', width: '100%', maxWidth: contentMaxWidth, flex: 1 }}>
136
+ {renderContent()}
137
+ </View>
136
138
  </View>
137
139
  </ScreenLayout>
138
140
  );
@@ -0,0 +1,51 @@
1
+ import { StyleSheet } from "react-native";
2
+ import type { useAppDesignTokens } from "@umituz/react-native-design-system";
3
+
4
+ export const getFeedbackFormStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
5
+ StyleSheet.create({
6
+ container: {
7
+ width: "100%",
8
+ },
9
+ typeContainer: {
10
+ marginBottom: 24,
11
+ },
12
+ typeScroll: {
13
+ gap: 8,
14
+ },
15
+ typeButton: {
16
+ flexDirection: "row",
17
+ alignItems: "center",
18
+ paddingHorizontal: 16,
19
+ paddingVertical: 8,
20
+ borderRadius: 20,
21
+ borderWidth: 1,
22
+ gap: 6,
23
+ },
24
+ ratingContainer: {
25
+ alignItems: "center",
26
+ marginBottom: 24,
27
+ },
28
+ stars: {
29
+ flexDirection: "row",
30
+ gap: 8,
31
+ },
32
+ starButton: {
33
+ padding: 4,
34
+ },
35
+ inputContainer: {
36
+ marginBottom: 24,
37
+ },
38
+ textArea: {
39
+ textAlignVertical: "top",
40
+ minHeight: 120,
41
+ borderWidth: 1,
42
+ borderRadius: 8,
43
+ padding: 12,
44
+ },
45
+ errorText: {
46
+ marginTop: 8,
47
+ },
48
+ submitButton: {
49
+ width: "100%",
50
+ },
51
+ });
@@ -8,6 +8,8 @@ import { View, StyleSheet, TouchableOpacity, ScrollView, TextInput } from "react
8
8
  import { useAppDesignTokens, AtomicText, AtomicButton, AtomicIcon } from "@umituz/react-native-design-system";
9
9
  import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
10
10
 
11
+ import { getFeedbackFormStyles as getStyles } from "./FeedbackForm.styles";
12
+
11
13
  export interface FeedbackFormProps {
12
14
  onSubmit: (data: { type: FeedbackType; rating: FeedbackRating; description: string; title: string }) => Promise<void>;
13
15
  texts: {
@@ -151,6 +153,7 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
151
153
  color: tokens.colors.textPrimary,
152
154
  backgroundColor: tokens.colors.surface,
153
155
  borderColor: error ? tokens.colors.error : tokens.colors.border,
156
+ fontSize: tokens.typography.bodyMedium.fontSize,
154
157
  }
155
158
  ]}
156
159
  />
@@ -176,52 +179,3 @@ export const FeedbackForm: React.FC<FeedbackFormProps> = ({
176
179
  );
177
180
  };
178
181
 
179
- const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
180
- StyleSheet.create({
181
- container: {
182
- width: "100%",
183
- },
184
- typeContainer: {
185
- marginBottom: 24,
186
- },
187
- typeScroll: {
188
- gap: 8,
189
- },
190
- typeButton: {
191
- flexDirection: "row",
192
- alignItems: "center",
193
- paddingHorizontal: 16,
194
- paddingVertical: 8,
195
- borderRadius: 20,
196
- borderWidth: 1,
197
- gap: 6,
198
- },
199
- ratingContainer: {
200
- alignItems: "center",
201
- marginBottom: 24,
202
- },
203
- stars: {
204
- flexDirection: "row",
205
- gap: 8,
206
- },
207
- starButton: {
208
- padding: 4,
209
- },
210
- inputContainer: {
211
- marginBottom: 24,
212
- },
213
- textArea: {
214
- textAlignVertical: "top",
215
- minHeight: 120,
216
- borderWidth: 1,
217
- borderRadius: 8,
218
- padding: 12,
219
- fontSize: tokens.typography.bodyMedium.fontSize,
220
- },
221
- errorText: {
222
- marginTop: 8,
223
- },
224
- submitButton: {
225
- width: "100%",
226
- },
227
- });
@@ -6,10 +6,9 @@
6
6
 
7
7
  import React from "react";
8
8
  import { View, ScrollView } from "react-native";
9
- import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
9
+ import { useAppDesignTokens, AtomicText, ScreenLayout, NavigationHeader, useAppNavigation } from "@umituz/react-native-design-system";
10
10
  import { LevelProgress } from "../LevelProgress";
11
11
  import { StreakDisplay } from "../StreakDisplay";
12
- import { Header } from "./Header";
13
12
  import { StatsGrid } from "./StatsGrid";
14
13
  import { AchievementsList } from "./AchievementsList";
15
14
  import { styles } from "./styles";
@@ -39,6 +38,7 @@ export const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
39
38
  subtextColor,
40
39
  headerComponent,
41
40
  }) => {
41
+ const navigation = useAppNavigation();
42
42
  const tokens = useAppDesignTokens();
43
43
 
44
44
  // Use tokens for fallbacks
@@ -48,75 +48,72 @@ export const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
48
48
  const finalTextColor = textColor || tokens.colors.textPrimary;
49
49
  const finalSubtextColor = subtextColor || tokens.colors.textSecondary;
50
50
 
51
+ const header = (
52
+ <NavigationHeader
53
+ title={title}
54
+ onBackPress={() => navigation.goBack()}
55
+ />
56
+ );
57
+
51
58
  return (
52
- <View style={[styles.container, { backgroundColor: finalBackgroundColor }, containerStyle]}>
59
+ <ScreenLayout
60
+ header={header}
61
+ contentContainerStyle={styles.scrollContent}
62
+ hideScrollIndicator={false}
63
+ >
53
64
  {headerComponent}
54
65
 
55
- <ScrollView
56
- style={styles.scrollView}
57
- contentContainerStyle={styles.scrollContent}
58
- showsVerticalScrollIndicator={false}
59
- >
60
- {/* Header */}
61
- <Header
62
- title={title}
63
- headerStyle={headerStyle}
64
- titleStyle={titleStyle}
66
+ {/* Level Progress */}
67
+ <View style={styles.section}>
68
+ <LevelProgress
69
+ {...levelProps}
70
+ primaryColor={finalAccentColor}
71
+ backgroundColor={finalCardBackgroundColor}
65
72
  textColor={finalTextColor}
73
+ subtextColor={finalSubtextColor}
66
74
  />
75
+ </View>
67
76
 
68
- {/* Level Progress */}
77
+ {/* Streak (if provided) */}
78
+ {streakProps && (
69
79
  <View style={styles.section}>
70
- <LevelProgress
71
- {...levelProps}
80
+ <AtomicText
81
+ style={[styles.sectionTitle, { color: finalTextColor }, sectionTitleStyle]}
82
+ >
83
+ {streakTitle}
84
+ </AtomicText>
85
+ <StreakDisplay
86
+ {...streakProps}
72
87
  primaryColor={finalAccentColor}
73
88
  backgroundColor={finalCardBackgroundColor}
74
89
  textColor={finalTextColor}
75
90
  subtextColor={finalSubtextColor}
76
91
  />
77
92
  </View>
93
+ )}
78
94
 
79
- {/* Streak (if provided) */}
80
- {streakProps && (
81
- <View style={styles.section}>
82
- <AtomicText
83
- style={[styles.sectionTitle, { color: finalTextColor }, sectionTitleStyle]}
84
- >
85
- {streakTitle}
86
- </AtomicText>
87
- <StreakDisplay
88
- {...streakProps}
89
- primaryColor={finalAccentColor}
90
- backgroundColor={finalCardBackgroundColor}
91
- textColor={finalTextColor}
92
- subtextColor={finalSubtextColor}
93
- />
94
- </View>
95
- )}
96
-
97
- {/* Stats Grid */}
98
- <StatsGrid
99
- statsTitle={statsTitle}
100
- stats={stats}
101
- accentColor={finalAccentColor}
102
- cardBackgroundColor={finalCardBackgroundColor}
103
- textColor={finalTextColor}
104
- subtextColor={finalSubtextColor}
105
- sectionTitleStyle={sectionTitleStyle}
106
- />
95
+ {/* Stats Grid */}
96
+ <StatsGrid
97
+ statsTitle={statsTitle}
98
+ stats={stats}
99
+ accentColor={finalAccentColor}
100
+ cardBackgroundColor={finalCardBackgroundColor}
101
+ textColor={finalTextColor}
102
+ subtextColor={finalSubtextColor}
103
+ sectionTitleStyle={sectionTitleStyle}
104
+ />
107
105
 
108
- {/* Achievements */}
109
- <AchievementsList
110
- achievementsTitle={achievementsTitle}
111
- achievements={achievements}
112
- emptyAchievementsText={emptyAchievementsText}
113
- accentColor={finalAccentColor}
114
- cardBackgroundColor={finalCardBackgroundColor}
115
- textColor={finalTextColor}
116
- subtextColor={finalSubtextColor}
117
- sectionTitleStyle={sectionTitleStyle}
118
- />
119
- </ScrollView>
120
- </View>
106
+ {/* Achievements */}
107
+ <AchievementsList
108
+ achievementsTitle={achievementsTitle}
109
+ achievements={achievements}
110
+ emptyAchievementsText={emptyAchievementsText}
111
+ accentColor={finalAccentColor}
112
+ cardBackgroundColor={finalCardBackgroundColor}
113
+ textColor={finalTextColor}
114
+ subtextColor={finalSubtextColor}
115
+ sectionTitleStyle={sectionTitleStyle}
116
+ />
117
+ </ScreenLayout>
121
118
  );
122
119
  };
@@ -9,6 +9,7 @@ import { AtomicText, AtomicButton } from "@umituz/react-native-design-system";
9
9
  import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
10
10
  import { ContentValidationService } from "../../domain/services/ContentValidationService";
11
11
  import { StyleCacheService } from "../../domain/services/StyleCacheService";
12
+ import { useAppNavigation, NavigationHeader } from "@umituz/react-native-design-system";
12
13
 
13
14
  export interface LegalContentScreenProps {
14
15
  content?: string;
@@ -34,6 +35,7 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
34
35
  createStyles,
35
36
  }) => {
36
37
  const tokens = useAppDesignTokens();
38
+ const navigation = useAppNavigation();
37
39
 
38
40
  const styles = React.useMemo(() => {
39
41
  const cacheKey = StyleCacheService.createTokenCacheKey(tokens);
@@ -101,17 +103,15 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
101
103
  testID={testID}
102
104
  scrollable={true}
103
105
  hideScrollIndicator={false}
106
+ header={
107
+ <NavigationHeader
108
+ title={title}
109
+ onBackPress={() => navigation.goBack()}
110
+ />
111
+ }
104
112
  contentContainerStyle={styles.scrollContent}
105
113
  >
106
114
  <View style={styles.content}>
107
- <AtomicText
108
- type="headlineLarge"
109
- color="primary"
110
- style={styles.title}
111
- >
112
- {title}
113
- </AtomicText>
114
-
115
115
  {contentSection}
116
116
  </View>
117
117
  </ScreenLayout>
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import React from "react";
9
- import { ScreenLayout } from "@umituz/react-native-design-system";
9
+ import { ScreenLayout, NavigationHeader, useAppNavigation } from "@umituz/react-native-design-system";
10
10
  import { LegalScreenHeader } from "../components/LegalScreenHeader";
11
11
  import { LegalDocumentsList } from "../components/LegalDocumentsList";
12
12
 
@@ -45,10 +45,21 @@ export const LegalScreen: React.FC<LegalScreenProps> = React.memo((props) => {
45
45
  testID = "legal-screen",
46
46
  } = props;
47
47
 
48
+ const navigation = useAppNavigation();
49
+
48
50
  return (
49
- <ScreenLayout testID={testID} hideScrollIndicator>
51
+ <ScreenLayout
52
+ testID={testID}
53
+ hideScrollIndicator
54
+ header={
55
+ <NavigationHeader
56
+ title={title || ""}
57
+ onBackPress={() => navigation.goBack()}
58
+ />
59
+ }
60
+ >
50
61
  <LegalScreenHeader title={title} description={description} />
51
-
62
+
52
63
  <LegalDocumentsList
53
64
  documentsHeader={documentsHeader}
54
65
  privacyTitle={privacyTitle}
@@ -110,7 +110,7 @@ export { ReminderItem } from './reminders/presentation/components/ReminderItem';
110
110
  export type { ReminderItemProps, ReminderItemTranslations } from './reminders/presentation/components/ReminderItem';
111
111
 
112
112
  export { ReminderForm } from './reminders/presentation/components/ReminderForm';
113
- export type { ReminderFormProps, ReminderFormTranslations } from './reminders/presentation/components/ReminderForm';
113
+ export type { ReminderFormProps, ReminderFormTranslations } from './reminders/presentation/components/ReminderForm.constants';
114
114
 
115
115
  export { FormButton } from './reminders/presentation/components/FormButton';
116
116
  export type { FormButtonProps } from './reminders/presentation/components/FormButton';
@@ -7,11 +7,21 @@
7
7
 
8
8
  import React, { useMemo } from 'react';
9
9
  import { View, StyleSheet } from 'react-native';
10
- import { AtomicIcon, AtomicCard, AtomicText, ScreenLayout, BASE_TOKENS, AtomicSpinner, type IconColor } from '@umituz/react-native-design-system';
10
+ import {
11
+ AtomicIcon,
12
+ AtomicCard,
13
+ AtomicText,
14
+ ScreenLayout,
15
+ BASE_TOKENS,
16
+ AtomicSpinner,
17
+ type IconColor,
18
+ NavigationHeader,
19
+ useAppNavigation,
20
+ useAppDesignTokens,
21
+ type DesignTokens
22
+ } from '@umituz/react-native-design-system';
11
23
  import { Switch } from 'react-native';
12
- import { useAppDesignTokens } from '@umituz/react-native-design-system';
13
24
  import { useNotificationSettings } from '../../infrastructure/hooks/useNotificationSettings';
14
- import type { DesignTokens } from '@umituz/react-native-design-system';
15
25
 
16
26
  export interface NotificationsScreenProps {
17
27
  translations: {
@@ -30,13 +40,21 @@ export const NotificationsScreen: React.FC<NotificationsScreenProps> = ({
30
40
  iconColor = 'primary',
31
41
  testID = 'notifications-screen',
32
42
  }) => {
43
+ const navigation = useAppNavigation();
33
44
  const tokens = useAppDesignTokens();
34
45
  const styles = useMemo(() => getStyles(tokens), [tokens]);
35
46
  const { notificationsEnabled, setNotificationsEnabled, isLoading } = useNotificationSettings();
36
47
 
48
+ const header = (
49
+ <NavigationHeader
50
+ title={translations.title}
51
+ onBackPress={() => navigation.goBack()}
52
+ />
53
+ );
54
+
37
55
  if (isLoading) {
38
56
  return (
39
- <ScreenLayout testID={testID}>
57
+ <ScreenLayout testID={testID} header={header}>
40
58
  <AtomicSpinner
41
59
  size="lg"
42
60
  color="primary"
@@ -48,34 +66,43 @@ export const NotificationsScreen: React.FC<NotificationsScreenProps> = ({
48
66
  }
49
67
 
50
68
  return (
51
- <ScreenLayout testID={testID} hideScrollIndicator>
52
- <AtomicCard style={styles.card}>
53
- <View style={styles.settingItem}>
54
- <View style={styles.iconContainer}>
55
- <AtomicIcon name={iconName} size="lg" color={iconColor as IconColor} />
69
+ <ScreenLayout
70
+ testID={testID}
71
+ hideScrollIndicator
72
+ header={header}
73
+ >
74
+ <View style={styles.content}>
75
+ <AtomicCard style={styles.card}>
76
+ <View style={styles.settingItem}>
77
+ <View style={styles.iconContainer}>
78
+ <AtomicIcon name={iconName} size="lg" color={iconColor as IconColor} />
79
+ </View>
80
+ <View style={styles.textContainer}>
81
+ <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
82
+ {translations.title}
83
+ </AtomicText>
84
+ <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: BASE_TOKENS.spacing.xs }}>
85
+ {translations.description}
86
+ </AtomicText>
87
+ </View>
88
+ <Switch
89
+ value={notificationsEnabled}
90
+ onValueChange={setNotificationsEnabled}
91
+ trackColor={{ false: tokens.colors.surfaceSecondary, true: tokens.colors.primary }}
92
+ thumbColor={tokens.colors.surface}
93
+ testID="notifications-toggle"
94
+ />
56
95
  </View>
57
- <View style={styles.textContainer}>
58
- <AtomicText type="bodyLarge" style={{ color: tokens.colors.textPrimary }}>
59
- {translations.title}
60
- </AtomicText>
61
- <AtomicText type="bodySmall" style={{ color: tokens.colors.textSecondary, marginTop: BASE_TOKENS.spacing.xs }}>
62
- {translations.description}
63
- </AtomicText>
64
- </View>
65
- <Switch
66
- value={notificationsEnabled}
67
- onValueChange={setNotificationsEnabled}
68
- trackColor={{ false: tokens.colors.surfaceSecondary, true: tokens.colors.primary }}
69
- thumbColor={tokens.colors.surface}
70
- testID="notifications-toggle"
71
- />
72
- </View>
73
- </AtomicCard>
96
+ </AtomicCard>
97
+ </View>
74
98
  </ScreenLayout>
75
99
  );
76
100
  };
77
101
 
78
102
  const getStyles = (tokens: DesignTokens) => StyleSheet.create({
103
+ content: {
104
+ padding: BASE_TOKENS.spacing.md,
105
+ },
79
106
  loadingContainer: {
80
107
  flex: 1,
81
108
  justifyContent: 'center',
@@ -0,0 +1,35 @@
1
+ import type { Reminder, ReminderFrequency, CreateReminderInput, TimePreset } from '../../../infrastructure/services/types';
2
+
3
+ export const DEFAULT_HOUR = 9;
4
+ export const DEFAULT_MINUTE = 0;
5
+ export const DEFAULT_WEEKDAY = 2; // Tuesday
6
+ export const MAX_TITLE_LENGTH = 100;
7
+ export const MAX_BODY_LENGTH = 500;
8
+
9
+ export const VALID_HOUR_RANGE = { min: 0, max: 23 } as const;
10
+ export const VALID_MINUTE_RANGE = { min: 0, max: 59 } as const;
11
+ export const VALID_WEEKDAY_RANGE = { min: 0, max: 6 } as const;
12
+
13
+ export interface ReminderFormTranslations {
14
+ titleLabel: string;
15
+ titlePlaceholder: string;
16
+ bodyLabel: string;
17
+ bodyPlaceholder: string;
18
+ timeLabel: string;
19
+ frequencyLabel: string;
20
+ weekdayLabel: string;
21
+ saveButton: string;
22
+ cancelButton: string;
23
+ customTimeLabel: string;
24
+ getPresetLabel: (key: string) => string;
25
+ getFrequencyLabel: (key: string) => string;
26
+ getWeekdayLabel: (key: string) => string;
27
+ }
28
+
29
+ export interface ReminderFormProps {
30
+ initialData?: Reminder;
31
+ translations: ReminderFormTranslations;
32
+ onSave: (data: CreateReminderInput) => void;
33
+ onCancel: () => void;
34
+ timePresets?: TimePreset[];
35
+ }
@@ -0,0 +1,22 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import type { useAppDesignTokens } from '@umituz/react-native-design-system';
3
+
4
+ export const createReminderFormStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
5
+ StyleSheet.create({
6
+ container: {
7
+ flex: 1,
8
+ padding: 16,
9
+ backgroundColor: tokens.colors.surface,
10
+ },
11
+ section: { marginBottom: 20 },
12
+ label: { color: tokens.colors.textPrimary, marginBottom: 8 },
13
+ input: {
14
+ backgroundColor: tokens.colors.surfaceSecondary,
15
+ borderRadius: 8,
16
+ padding: 12,
17
+ fontSize: 16,
18
+ color: tokens.colors.textPrimary,
19
+ },
20
+ multilineInput: { minHeight: 80, textAlignVertical: 'top' },
21
+ buttonRow: { flexDirection: 'row', gap: 12, marginTop: 24 },
22
+ });
@@ -11,44 +11,20 @@ import { TimePresetSelector } from './TimePresetSelector';
11
11
  import { FrequencySelector } from './FrequencySelector';
12
12
  import { WeekdaySelector } from './WeekdaySelector';
13
13
  import { FormButton } from './FormButton';
14
+ import {
15
+ DEFAULT_HOUR,
16
+ DEFAULT_MINUTE,
17
+ DEFAULT_WEEKDAY,
18
+ MAX_TITLE_LENGTH,
19
+ MAX_BODY_LENGTH,
20
+ VALID_HOUR_RANGE,
21
+ VALID_MINUTE_RANGE,
22
+ VALID_WEEKDAY_RANGE,
23
+ type ReminderFormProps,
24
+ } from './ReminderForm.constants';
25
+ import { createReminderFormStyles as createStyles } from './ReminderForm.styles';
14
26
  import { DEFAULT_TIME_PRESETS, FREQUENCY_OPTIONS } from '../../infrastructure/config/reminderPresets';
15
- import type { Reminder, ReminderFrequency, CreateReminderInput, TimePreset } from '../../../infrastructure/services/types';
16
-
17
- // Constants for magic numbers
18
- const DEFAULT_HOUR = 9;
19
- const DEFAULT_MINUTE = 0;
20
- const DEFAULT_WEEKDAY = 2; // Tuesday
21
- const MAX_TITLE_LENGTH = 100;
22
- const MAX_BODY_LENGTH = 500;
23
-
24
- // Validation constants
25
- const VALID_HOUR_RANGE = { min: 0, max: 23 } as const;
26
- const VALID_MINUTE_RANGE = { min: 0, max: 59 } as const;
27
- const VALID_WEEKDAY_RANGE = { min: 0, max: 6 } as const;
28
-
29
- export interface ReminderFormTranslations {
30
- titleLabel: string;
31
- titlePlaceholder: string;
32
- bodyLabel: string;
33
- bodyPlaceholder: string;
34
- timeLabel: string;
35
- frequencyLabel: string;
36
- weekdayLabel: string;
37
- saveButton: string;
38
- cancelButton: string;
39
- customTimeLabel: string;
40
- getPresetLabel: (key: string) => string;
41
- getFrequencyLabel: (key: string) => string;
42
- getWeekdayLabel: (key: string) => string;
43
- }
44
-
45
- export interface ReminderFormProps {
46
- initialData?: Reminder;
47
- translations: ReminderFormTranslations;
48
- onSave: (data: CreateReminderInput) => void;
49
- onCancel: () => void;
50
- timePresets?: TimePreset[];
51
- }
27
+ import type { ReminderFrequency, TimePreset } from '../../../infrastructure/services/types';
52
28
 
53
29
  export const ReminderForm: React.FC<ReminderFormProps> = ({
54
30
  initialData,
@@ -207,23 +183,3 @@ export const ReminderForm: React.FC<ReminderFormProps> = ({
207
183
  </ScrollView>
208
184
  );
209
185
  };
210
-
211
- const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
212
- StyleSheet.create({
213
- container: {
214
- flex: 1,
215
- padding: 16,
216
- backgroundColor: tokens.colors.surface,
217
- },
218
- section: { marginBottom: 20 },
219
- label: { color: tokens.colors.textPrimary, marginBottom: 8 },
220
- input: {
221
- backgroundColor: tokens.colors.surfaceSecondary,
222
- borderRadius: 8,
223
- padding: 12,
224
- fontSize: 16,
225
- color: tokens.colors.textPrimary,
226
- },
227
- multilineInput: { minHeight: 80, textAlignVertical: 'top' },
228
- buttonRow: { flexDirection: 'row', gap: 12, marginTop: 24 },
229
- });
@@ -5,8 +5,15 @@
5
5
 
6
6
  import React, { useMemo, useCallback } from 'react';
7
7
  import { View, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
8
- import { AtomicText, AtomicIcon, AtomicSpinner } from '@umituz/react-native-design-system';
9
- import { useAppDesignTokens } from '@umituz/react-native-design-system';
8
+ import {
9
+ AtomicText,
10
+ AtomicIcon,
11
+ AtomicSpinner,
12
+ ScreenLayout,
13
+ NavigationHeader,
14
+ useAppNavigation,
15
+ useAppDesignTokens
16
+ } from '@umituz/react-native-design-system';
10
17
  import { ReminderItem } from '../components/ReminderItem';
11
18
  import { useReminders, useRemindersLoading } from '../../infrastructure/storage/RemindersStore';
12
19
  import { useReminderActions } from '../../infrastructure/hooks/useReminderActions';
@@ -25,6 +32,7 @@ export const ReminderListScreen: React.FC<ReminderListScreenProps> = ({
25
32
  onEditPress,
26
33
  maxReminders = 20,
27
34
  }) => {
35
+ const navigation = useAppNavigation();
28
36
  const tokens = useAppDesignTokens();
29
37
  const styles = useMemo(() => createStyles(tokens), [tokens]);
30
38
 
@@ -69,32 +77,39 @@ export const ReminderListScreen: React.FC<ReminderListScreenProps> = ({
69
77
 
70
78
  const keyExtractor = useCallback((item: Reminder) => item.id, []);
71
79
 
80
+ const header = (
81
+ <NavigationHeader
82
+ title={translations.screenTitle}
83
+ onBackPress={() => navigation.goBack()}
84
+ />
85
+ );
86
+
72
87
  if (isLoading) {
73
88
  return (
74
- <View style={[styles.loadingContainer, { backgroundColor: tokens.colors.surface }]}>
89
+ <ScreenLayout header={header}>
75
90
  <AtomicSpinner size="lg" color="primary" fullContainer />
76
- </View>
91
+ </ScreenLayout>
77
92
  );
78
93
  }
79
94
 
80
95
  return (
81
- <View style={{ flex: 1 }}>
96
+ <ScreenLayout header={header}>
82
97
  <FlatList
83
98
  data={reminders}
84
99
  renderItem={renderItem}
85
100
  keyExtractor={keyExtractor}
86
101
  ListEmptyComponent={renderEmpty}
87
- contentContainerStyle={[styles.listContent, { backgroundColor: tokens.colors.surface }]}
102
+ contentContainerStyle={styles.listContent}
88
103
  showsVerticalScrollIndicator={false}
89
104
  />
90
105
 
91
106
  {canAddMore && (
92
107
  <TouchableOpacity style={styles.fab} onPress={onAddPress} activeOpacity={0.8}>
93
- <AtomicIcon name="add" size="md" color="onSurface" />
108
+ <AtomicIcon name="add" size="md" color="onPrimary" />
94
109
  <AtomicText type="bodyMedium" style={styles.fabText}>{translations.addButtonLabel}</AtomicText>
95
110
  </TouchableOpacity>
96
111
  )}
97
- </View>
112
+ </ScreenLayout>
98
113
  );
99
114
  };
100
115
 
@@ -102,7 +117,7 @@ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
102
117
  StyleSheet.create({
103
118
  loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' },
104
119
  listContent: { padding: 16, paddingBottom: 100, flexGrow: 1 },
105
- emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32 },
120
+ emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 32, paddingTop: 100 },
106
121
  emptyIconContainer: {
107
122
  width: 80,
108
123
  height: 80,
@@ -127,5 +142,5 @@ const createStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
127
142
  justifyContent: 'center',
128
143
  gap: 8,
129
144
  },
130
- fabText: { color: tokens.colors.surface, fontWeight: '600' },
145
+ fabText: { color: tokens.colors.onPrimary, fontWeight: '600' },
131
146
  });
@@ -18,6 +18,8 @@ import {
18
18
  ScreenLayout,
19
19
  AtomicSpinner,
20
20
  AtomicText,
21
+ useAppNavigation,
22
+ NavigationHeader,
21
23
  } from "@umituz/react-native-design-system";
22
24
  import type { DesignTokens } from "@umituz/react-native-design-system";
23
25
  import type { VideoTutorial } from "../../types";
@@ -67,23 +69,32 @@ export const VideoTutorialsScreen: React.FC<VideoTutorialsScreenProps> = React.m
67
69
  [handleTutorialPress]
68
70
  );
69
71
 
72
+ const navigation = useAppNavigation();
73
+
70
74
  if (isLoading) return <AtomicSpinner size="lg" fullContainer />;
71
75
 
72
76
  const hasFeatured = featuredTutorials && featuredTutorials.length > 0;
73
77
  const hasTutorials = tutorials && tutorials.length > 0;
74
78
 
79
+ const header = (
80
+ <NavigationHeader
81
+ title={title}
82
+ onBackPress={() => navigation.goBack()}
83
+ />
84
+ );
85
+
75
86
  if (!hasTutorials && !hasFeatured) {
76
87
  return (
77
- <View style={styles.emptyContainer}>
78
- <AtomicText color="secondary" type="bodyLarge">{emptyMessage}</AtomicText>
79
- </View>
88
+ <ScreenLayout header={header} edges={["bottom"]}>
89
+ <View style={styles.emptyContainer}>
90
+ <AtomicText color="secondary" type="bodyLarge">{emptyMessage}</AtomicText>
91
+ </View>
92
+ </ScreenLayout>
80
93
  );
81
94
  }
82
95
 
83
96
  return (
84
- <ScreenLayout scrollable={false} edges={["top", "bottom"]}>
85
- <AtomicText style={styles.title}>{title}</AtomicText>
86
-
97
+ <ScreenLayout header={header} scrollable={false} edges={["bottom"]}>
87
98
  {hasFeatured && (
88
99
  <View style={styles.section}>
89
100
  <AtomicText color="secondary" style={styles.sectionTitle}>{featuredTitle}</AtomicText>
@@ -13,9 +13,9 @@ export const SettingsFooter: React.FC<SettingsFooterProps> = ({
13
13
  appVersion,
14
14
  versionLabel,
15
15
  }) => {
16
- const displayText = versionText || (appVersion && versionLabel
17
- ? `${versionLabel} ${appVersion}`
18
- : appVersion);
16
+ const displayText = versionText || (appVersion
17
+ ? (versionLabel ? `${versionLabel} ${appVersion}` : `Version ${appVersion}`)
18
+ : undefined);
19
19
 
20
20
  if (!displayText) return null;
21
21
 
@@ -6,7 +6,12 @@
6
6
  */
7
7
 
8
8
  import React from "react";
9
- import { useAppDesignTokens, StackNavigator, type StackScreen, type StackNavigatorConfig } from "@umituz/react-native-design-system";
9
+ import {
10
+ useAppDesignTokens,
11
+ StackNavigator,
12
+ type StackScreen,
13
+ type StackNavigatorConfig,
14
+ } from "@umituz/react-native-design-system";
10
15
  import { useLocalization, LanguageSelectionScreen } from "../../domains/localization";
11
16
  import { NotificationSettingsScreen } from "../../domains/notifications";
12
17
  import { AccountScreen } from "@umituz/react-native-auth";
@@ -16,30 +21,31 @@ import { FAQScreen } from "../../domains/faqs";
16
21
  import { AboutScreen } from "../../domains/about";
17
22
  import { LegalScreen } from "../../domains/legal";
18
23
  import { GamificationScreen } from "../../domains/gamification";
19
- import { useNavigationHandlers } from "./hooks";
24
+ import { useNavigationHandlers, useSettingsScreens } from "./hooks";
20
25
  import {
21
26
  createNotificationTranslations,
22
27
  createQuietHoursTranslations,
23
28
  createLegalScreenProps,
24
29
  } from "./utils";
25
- import type { SettingsStackParamList, SettingsStackNavigatorProps, AdditionalScreen } from "./types";
30
+ import type { SettingsStackParamList, SettingsStackNavigatorProps } from "./types";
26
31
 
27
- export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
28
- appInfo,
29
- legalUrls,
30
- faqData,
31
- config = {},
32
- showUserProfile = false,
33
- userProfile,
34
- accountConfig,
35
- additionalScreens = [],
36
- devSettings,
37
- customSections = [],
38
- showHeader = true,
39
- showCloseButton = false,
40
- onClose,
41
- gamificationConfig,
42
- }) => {
32
+ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = (props) => {
33
+ const {
34
+ appInfo,
35
+ 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
+ } = props;
43
49
  const tokens = useAppDesignTokens();
44
50
  const { t } = useLocalization();
45
51
  const { handlePrivacyPress, handleTermsPress, handleEulaPress, aboutConfig } =
@@ -47,21 +53,9 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
47
53
 
48
54
  const screenOptions = React.useMemo(
49
55
  () => ({
50
- headerStyle: {
51
- backgroundColor: tokens.colors.surface,
52
- borderBottomColor: tokens.colors.borderLight,
53
- borderBottomWidth: 1,
54
- elevation: 0,
55
- shadowOpacity: 0,
56
- },
57
- headerTintColor: tokens.colors.textPrimary,
58
- headerTitleStyle: {
59
- color: tokens.colors.textPrimary,
60
- fontWeight: "600" as const,
61
- },
62
- headerBackTitle: t("settings.title"),
56
+ headerShown: false,
63
57
  }),
64
- [tokens, t]
58
+ []
65
59
  );
66
60
 
67
61
  const notificationTranslations = React.useMemo(() => createNotificationTranslations(t), [t]);
@@ -71,150 +65,14 @@ export const SettingsStackNavigator: React.FC<SettingsStackNavigatorProps> = ({
71
65
  [t, handlePrivacyPress, handleTermsPress, handleEulaPress]
72
66
  );
73
67
 
74
- const screens = React.useMemo(() => {
75
- const baseScreens: StackScreen[] = [
76
- {
77
- name: "SettingsMain",
78
- options: { headerShown: false },
79
- children: () => (
80
- <SettingsScreen
81
- config={config}
82
- appVersion={appInfo.version}
83
- showUserProfile={showUserProfile}
84
- userProfile={userProfile}
85
- devSettings={devSettings}
86
- customSections={customSections}
87
- showHeader={showHeader}
88
- showCloseButton={showCloseButton}
89
- onClose={onClose}
90
- />
91
- ),
92
- },
93
- {
94
- name: "Appearance",
95
- component: AppearanceScreen,
96
- options: { headerShown: false },
97
- },
98
- {
99
- name: "About",
100
- options: { headerTitle: t("settings.about.title") },
101
- children: () => <AboutScreen config={aboutConfig} />,
102
- },
103
- {
104
- name: "Legal",
105
- options: { headerTitle: t("settings.legal.title") },
106
- children: () => <LegalScreen {...legalScreenProps} />,
107
- },
108
- {
109
- name: "Notifications",
110
- options: { headerShown: false },
111
- children: () => (
112
- <NotificationSettingsScreen
113
- translations={notificationTranslations}
114
- quietHoursTranslations={quietHoursTranslations}
115
- />
116
- ),
117
- },
118
- ];
119
-
120
- // FAQ screen - conditionally add
121
- const faqScreen: StackScreen | null = (faqData && faqData.categories.length > 0)
122
- ? {
123
- name: "FAQ",
124
- options: { headerTitle: t("settings.faqs.title") },
125
- children: () => (
126
- <FAQScreen
127
- categories={faqData.categories}
128
- searchPlaceholder={t("settings.faqs.searchPlaceholder")}
129
- emptySearchTitle={t("settings.faqs.emptySearchTitle")}
130
- emptySearchMessage={t("settings.faqs.emptySearchMessage")}
131
- headerTitle={t("settings.faqs.headerTitle")}
132
- />
133
- ),
134
- }
135
- : null;
136
-
137
- // Additional screens - map to StackScreen format
138
- const additionalStackScreens: StackScreen[] = (additionalScreens as readonly AdditionalScreen[]).map((screen: AdditionalScreen): StackScreen => {
139
- // Create base screen object
140
- const stackScreen: Record<string, unknown> = {
141
- name: screen.name,
142
- };
143
-
144
- // Conditionally add properties
145
- if (screen.component) {
146
- stackScreen.component = screen.component;
147
- }
148
- if (screen.children) {
149
- stackScreen.children = screen.children;
150
- }
151
- if (screen.options) {
152
- stackScreen.options = screen.options;
153
- }
154
-
155
- // Type assertion to StackScreen
156
- return stackScreen as unknown as StackScreen;
157
- });
158
-
159
- // Gamification screen - conditionally add
160
- const gamificationScreen: StackScreen | null = gamificationConfig?.enabled
161
- ? {
162
- name: "Gamification",
163
- options: { headerTitle: t("settings.gamification.title") },
164
- children: () => <GamificationScreen config={gamificationConfig} />,
165
- }
166
- : null;
167
-
168
- // Language selection screen
169
- const languageScreen: StackScreen = {
170
- name: "LanguageSelection",
171
- options: { headerShown: false },
172
- children: () => (
173
- <LanguageSelectionScreen
174
- headerTitle={t("settings.language.title")}
175
- searchPlaceholder={t("settings.languageSelection.searchPlaceholder")}
176
- />
177
- ),
178
- };
179
-
180
- // Account screen - conditionally add
181
- const accountScreen: StackScreen | null = accountConfig
182
- ? {
183
- name: "Account",
184
- options: { headerTitle: t("settings.account.title") },
185
- children: () => <AccountScreen config={accountConfig} />,
186
- }
187
- : null;
188
-
189
- // Compose final list using spread operator (immutable)
190
- return [
191
- ...baseScreens,
192
- ...(faqScreen ? [faqScreen] : []),
193
- ...additionalStackScreens,
194
- ...(gamificationScreen ? [gamificationScreen] : []),
195
- languageScreen,
196
- ...(accountScreen ? [accountScreen] : []),
197
- ];
198
- }, [
199
- t,
200
- showHeader,
201
- showCloseButton,
202
- onClose,
203
- config,
204
- appInfo.version,
205
- showUserProfile,
206
- userProfile,
207
- devSettings,
208
- customSections,
68
+ const screens = useSettingsScreens({
69
+ ...props,
209
70
  aboutConfig,
210
- legalScreenProps,
71
+ legalProps: legalScreenProps,
211
72
  notificationTranslations,
212
73
  quietHoursTranslations,
213
- faqData,
214
- additionalScreens,
215
- gamificationConfig,
216
- accountConfig,
217
- ]);
74
+ t,
75
+ });
218
76
 
219
77
  const navigatorConfig: StackNavigatorConfig<SettingsStackParamList> = {
220
78
  initialRouteName: "SettingsMain",
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export { useNavigationHandlers } from "./useNavigationHandlers";
6
+ export { useSettingsScreens } from "./useSettingsScreens";
@@ -0,0 +1,163 @@
1
+ import React, { useMemo } from 'react';
2
+ import type { StackScreen } from "@umituz/react-native-design-system";
3
+ import { LanguageSelectionScreen } from "../../../domains/localization";
4
+ import { NotificationSettingsScreen } from "../../../domains/notifications";
5
+ import { AccountScreen } from "@umituz/react-native-auth";
6
+ import { SettingsScreen } from "../../screens/SettingsScreen";
7
+ import { AppearanceScreen } from "../../screens/AppearanceScreen";
8
+ import { FAQScreen } from "../../../domains/faqs";
9
+ import { AboutScreen } from "../../../domains/about";
10
+ import { LegalScreen } from "../../../domains/legal";
11
+ import { GamificationScreen } from "../../../domains/gamification";
12
+ import type { SettingsStackNavigatorProps, AdditionalScreen } from "../types";
13
+
14
+ export interface UseSettingsScreensProps extends SettingsStackNavigatorProps {
15
+ aboutConfig: any;
16
+ legalProps: any;
17
+ notificationTranslations: any;
18
+ quietHoursTranslations: any;
19
+ t: (key: string) => string;
20
+ }
21
+
22
+ export const useSettingsScreens = (props: UseSettingsScreensProps): StackScreen[] => {
23
+ const {
24
+ appInfo,
25
+ config,
26
+ showUserProfile,
27
+ userProfile,
28
+ devSettings,
29
+ customSections,
30
+ showHeader,
31
+ showCloseButton,
32
+ onClose,
33
+ aboutConfig,
34
+ legalProps,
35
+ notificationTranslations,
36
+ quietHoursTranslations,
37
+ faqData,
38
+ additionalScreens,
39
+ gamificationConfig,
40
+ accountConfig,
41
+ t,
42
+ } = props;
43
+
44
+ return useMemo(() => {
45
+ const baseScreens: StackScreen[] = [
46
+ {
47
+ name: "SettingsMain",
48
+ options: { headerShown: false },
49
+ children: () => React.createElement(SettingsScreen, {
50
+ config,
51
+ appVersion: appInfo.version,
52
+ showUserProfile,
53
+ userProfile,
54
+ devSettings,
55
+ customSections,
56
+ showHeader,
57
+ showCloseButton,
58
+ onClose,
59
+ }),
60
+ },
61
+ {
62
+ name: "Appearance",
63
+ component: AppearanceScreen as any,
64
+ options: { headerShown: false },
65
+ },
66
+ {
67
+ name: "About",
68
+ options: { headerShown: false },
69
+ children: () => React.createElement(AboutScreen, { config: aboutConfig }),
70
+ },
71
+ {
72
+ name: "Legal",
73
+ options: { headerShown: false },
74
+ children: () => React.createElement(LegalScreen, legalProps),
75
+ },
76
+ {
77
+ name: "Notifications",
78
+ options: { headerShown: false },
79
+ children: () => React.createElement(NotificationSettingsScreen, {
80
+ translations: notificationTranslations,
81
+ quietHoursTranslations,
82
+ }),
83
+ },
84
+ ];
85
+
86
+ const faqScreen: StackScreen | null = (faqData && faqData.categories?.length > 0)
87
+ ? {
88
+ name: "FAQ",
89
+ options: { headerShown: false },
90
+ children: () => React.createElement(FAQScreen, {
91
+ categories: faqData.categories,
92
+ searchPlaceholder: t("settings.faqs.searchPlaceholder"),
93
+ emptySearchTitle: t("settings.faqs.emptySearchTitle"),
94
+ emptySearchMessage: t("settings.faqs.emptySearchMessage"),
95
+ headerTitle: t("settings.faqs.headerTitle"),
96
+ }),
97
+ }
98
+ : null;
99
+
100
+ const additionalStackScreens: StackScreen[] = (additionalScreens || []).map((screen: AdditionalScreen): StackScreen => {
101
+ const stackScreen: any = { name: screen.name };
102
+ if (screen.component) stackScreen.component = screen.component;
103
+ if (screen.children) stackScreen.children = screen.children;
104
+ if (screen.options) stackScreen.options = screen.options;
105
+ return stackScreen as StackScreen;
106
+ });
107
+
108
+ const gamificationScreen: StackScreen | null = gamificationConfig?.enabled
109
+ ? {
110
+ name: "Gamification",
111
+ options: { headerShown: false },
112
+ children: () => React.createElement(GamificationScreen, { config: gamificationConfig }),
113
+ }
114
+ : null;
115
+
116
+ const languageScreen: StackScreen = {
117
+ name: "LanguageSelection",
118
+ options: { headerShown: false },
119
+ children: () => React.createElement(LanguageSelectionScreen, {
120
+ headerTitle: t("settings.language.title"),
121
+ searchPlaceholder: t("settings.languageSelection.searchPlaceholder"),
122
+ }),
123
+ };
124
+
125
+ const accountScreen: StackScreen | null = accountConfig
126
+ ? {
127
+ name: "Account",
128
+ options: { headerShown: false },
129
+ children: () => React.createElement(AccountScreen, { config: accountConfig }),
130
+ }
131
+ : null;
132
+
133
+ const allScreens: StackScreen[] = [
134
+ ...baseScreens,
135
+ ...(faqScreen ? [faqScreen] : []),
136
+ ...additionalStackScreens,
137
+ ...(gamificationScreen ? [gamificationScreen] : []),
138
+ languageScreen,
139
+ ...(accountScreen ? [accountScreen] : []),
140
+ ];
141
+
142
+ return allScreens;
143
+ }, [
144
+ t,
145
+ showHeader,
146
+ showCloseButton,
147
+ onClose,
148
+ config,
149
+ appInfo.version,
150
+ showUserProfile,
151
+ userProfile,
152
+ devSettings,
153
+ customSections,
154
+ aboutConfig,
155
+ legalProps,
156
+ notificationTranslations,
157
+ quietHoursTranslations,
158
+ faqData,
159
+ additionalScreens,
160
+ gamificationConfig,
161
+ accountConfig,
162
+ ]);
163
+ };
@@ -6,7 +6,6 @@
6
6
  import React from "react";
7
7
  import {
8
8
  ScreenLayout,
9
- useAppNavigation,
10
9
  } from "@umituz/react-native-design-system";
11
10
  import { useSettingsScreen } from "./hooks/useSettingsScreen";
12
11
  import { SettingsHeader } from "./components/SettingsHeader";
@@ -1,8 +1,5 @@
1
- /**
2
- * Settings Header Component
3
- * Handles close button functionality
4
- */
5
-
1
+ import React from "react";
2
+ import { Pressable } from "react-native";
6
3
  import { useAppDesignTokens, AtomicIcon, useAppNavigation, NavigationHeader } from "@umituz/react-native-design-system";
7
4
  import { useLocalization } from "../../../domains/localization";
8
5
 
@@ -16,6 +16,13 @@ export function getAppVersion(): string {
16
16
  return version;
17
17
  }
18
18
 
19
+ /**
20
+ * Gets the current build number from Expo constants
21
+ */
22
+ export function getBuildNumber(): string | undefined {
23
+ return Constants.expoConfig?.ios?.buildNumber ?? Constants.expoConfig?.android?.versionCode?.toString();
24
+ }
25
+
19
26
  /**
20
27
  * Validates if the current platform is supported
21
28
  */