@umituz/react-native-settings 4.23.71 → 4.23.73

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.
@@ -4,9 +4,8 @@
4
4
  */
5
5
 
6
6
  import React from "react";
7
- import { View, StyleSheet, TouchableOpacity, ScrollView, KeyboardAvoidingView, Platform } from "react-native";
8
- import { SafeAreaView } from "react-native-safe-area-context";
9
- import { useAppDesignTokens, AtomicText, AtomicIcon, BaseModal } from "@umituz/react-native-design-system";
7
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
8
+ import { useAppDesignTokens, AtomicText, AtomicIcon, BaseModal, ScreenLayout } from "@umituz/react-native-design-system";
10
9
  import { FeedbackForm } from "./FeedbackForm";
11
10
  import type { FeedbackType, FeedbackRating } from "../../domain/entities/FeedbackEntity";
12
11
  import type { FeedbackFormProps } from "./FeedbackForm";
@@ -37,43 +36,39 @@ export const FeedbackModal: React.FC<FeedbackModalProps> = ({
37
36
 
38
37
  return (
39
38
  <BaseModal visible={visible} onClose={onClose}>
40
- <SafeAreaView style={styles.safeArea}>
41
- <KeyboardAvoidingView
42
- behavior={Platform.OS === "ios" ? "padding" : "height"}
43
- style={styles.keyboardView}
44
- >
45
- <View style={[styles.header, { borderBottomColor: tokens.colors.border }]}>
46
- <View style={styles.headerText}>
47
- <AtomicText type="headlineSmall" color="textPrimary">
48
- {title}
39
+ <ScreenLayout
40
+ scrollable={true}
41
+ edges={[]}
42
+ keyboardAvoiding={true}
43
+ contentContainerStyle={styles.content}
44
+ hideScrollIndicator={false}
45
+ >
46
+ <View style={[styles.header, { borderBottomColor: tokens.colors.border }]}>
47
+ <View style={styles.headerText}>
48
+ <AtomicText type="headlineSmall" color="textPrimary">
49
+ {title}
50
+ </AtomicText>
51
+ {subtitle && (
52
+ <AtomicText type="bodySmall" color="textSecondary" style={{ marginTop: 4 }}>
53
+ {subtitle}
49
54
  </AtomicText>
50
- {subtitle && (
51
- <AtomicText type="bodySmall" color="textSecondary" style={{ marginTop: 4 }}>
52
- {subtitle}
53
- </AtomicText>
54
- )}
55
- </View>
56
- <TouchableOpacity
57
- onPress={onClose}
58
- style={[styles.closeButton, { backgroundColor: tokens.colors.surfaceVariant }]}
59
- >
60
- <AtomicIcon name="close" size="sm" color="onSurface" />
61
- </TouchableOpacity>
55
+ )}
62
56
  </View>
63
-
64
- <ScrollView
65
- contentContainerStyle={styles.content}
66
- keyboardShouldPersistTaps="handled"
57
+ <TouchableOpacity
58
+ onPress={onClose}
59
+ style={[styles.closeButton, { backgroundColor: tokens.colors.surfaceVariant }]}
67
60
  >
68
- <FeedbackForm
69
- onSubmit={onSubmit}
70
- initialType={initialType}
71
- isSubmitting={isSubmitting}
72
- texts={texts}
73
- />
74
- </ScrollView>
75
- </KeyboardAvoidingView>
76
- </SafeAreaView>
61
+ <AtomicIcon name="close" size="sm" color="onSurface" />
62
+ </TouchableOpacity>
63
+ </View>
64
+
65
+ <FeedbackForm
66
+ onSubmit={onSubmit}
67
+ initialType={initialType}
68
+ isSubmitting={isSubmitting}
69
+ texts={texts}
70
+ />
71
+ </ScreenLayout>
77
72
  </BaseModal>
78
73
  );
79
74
  };
@@ -81,12 +76,6 @@ export const FeedbackModal: React.FC<FeedbackModalProps> = ({
81
76
 
82
77
  const getStyles = (_tokens: ReturnType<typeof useAppDesignTokens>) =>
83
78
  StyleSheet.create({
84
- safeArea: {
85
- flex: 1,
86
- },
87
- keyboardView: {
88
- flex: 1,
89
- },
90
79
  header: {
91
80
  flexDirection: "row",
92
81
  justifyContent: "space-between",
@@ -0,0 +1,122 @@
1
+ /**
2
+ * GamificationScreen - Pure Presentational Component
3
+ * Renders the gamification dashboard with all data provided as props
4
+ * Under 200 lines - only presentation logic
5
+ */
6
+
7
+ import React from "react";
8
+ import { View, ScrollView } from "react-native";
9
+ import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
10
+ import { LevelProgress } from "../LevelProgress";
11
+ import { StreakDisplay } from "../StreakDisplay";
12
+ import { Header } from "./Header";
13
+ import { StatsGrid } from "./StatsGrid";
14
+ import { AchievementsList } from "./AchievementsList";
15
+ import { styles } from "./styles";
16
+ import type { GamificationScreenProps } from "./types";
17
+
18
+ /**
19
+ * Internal component that renders the screen with all props
20
+ */
21
+ export const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
22
+ title,
23
+ statsTitle,
24
+ achievementsTitle,
25
+ streakTitle,
26
+ levelProps,
27
+ stats,
28
+ achievements,
29
+ streakProps,
30
+ emptyAchievementsText,
31
+ containerStyle,
32
+ headerStyle,
33
+ titleStyle,
34
+ sectionTitleStyle,
35
+ accentColor,
36
+ backgroundColor,
37
+ cardBackgroundColor,
38
+ textColor,
39
+ subtextColor,
40
+ headerComponent,
41
+ }) => {
42
+ const tokens = useAppDesignTokens();
43
+
44
+ // Use tokens for fallbacks
45
+ const finalAccentColor = accentColor || tokens.colors.primary;
46
+ const finalBackgroundColor = backgroundColor || tokens.colors.backgroundPrimary;
47
+ const finalCardBackgroundColor = cardBackgroundColor || tokens.colors.surface;
48
+ const finalTextColor = textColor || tokens.colors.textPrimary;
49
+ const finalSubtextColor = subtextColor || tokens.colors.textSecondary;
50
+
51
+ return (
52
+ <View style={[styles.container, { backgroundColor: finalBackgroundColor }, containerStyle]}>
53
+ {headerComponent}
54
+
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}
65
+ textColor={finalTextColor}
66
+ />
67
+
68
+ {/* Level Progress */}
69
+ <View style={styles.section}>
70
+ <LevelProgress
71
+ {...levelProps}
72
+ primaryColor={finalAccentColor}
73
+ backgroundColor={finalCardBackgroundColor}
74
+ textColor={finalTextColor}
75
+ subtextColor={finalSubtextColor}
76
+ />
77
+ </View>
78
+
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
+ />
107
+
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>
121
+ );
122
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * GamificationScreen With Config
3
+ * Component that uses the hook to fetch data and render the screen
4
+ * Separated to satisfy React Hooks rules
5
+ */
6
+
7
+ import React from "react";
8
+ import { GamificationScreenInner } from "./GamificationScreen";
9
+ import type { GamificationConfigProps } from "./types";
10
+ import type { GamificationScreenProps } from "./types";
11
+ import type { Achievement } from "../../types";
12
+
13
+ /**
14
+ * Component that uses the hook - separated to satisfy React Hooks rules
15
+ */
16
+ export const GamificationScreenWithConfig: React.FC<GamificationConfigProps> = ({ config }) => {
17
+ // Import hook here to avoid conditional hook usage
18
+ const { useGamification } = require("../../hooks/useGamification");
19
+
20
+ const {
21
+ points,
22
+ level,
23
+ streak,
24
+ achievements,
25
+ } = useGamification(config);
26
+
27
+ // Transform store achievements to UI props
28
+ const achievementItems = achievements.map((a: Achievement) => ({
29
+ ...a,
30
+ title: a.title,
31
+ description: a.description,
32
+ icon: a.icon,
33
+ isUnlocked: a.isUnlocked,
34
+ progress: a.progress,
35
+ threshold: a.threshold,
36
+ id: a.id,
37
+ type: a.type
38
+ }));
39
+
40
+ const screenProps: GamificationScreenProps = {
41
+ title: config.translations.title,
42
+ statsTitle: config.translations.statsTitle,
43
+ achievementsTitle: config.translations.achievementsTitle,
44
+ streakTitle: config.translations.streakTitle,
45
+ levelProps: {
46
+ level: level.currentLevel,
47
+ points: level.currentPoints,
48
+ pointsToNext: level.pointsToNext,
49
+ progress: level.progress,
50
+ levelTitle: config.translations.levelTitle,
51
+ showPoints: true,
52
+ },
53
+ streakProps: {
54
+ current: streak.current,
55
+ longest: streak.longest,
56
+ currentLabel: config.translations.currentStreak,
57
+ bestLabel: config.translations.bestStreak,
58
+ daysLabel: config.translations.days,
59
+ },
60
+ stats: [
61
+ {
62
+ label: config.translations.statsTitle,
63
+ value: points,
64
+ icon: "star",
65
+ },
66
+ ],
67
+ achievements: achievementItems,
68
+ emptyAchievementsText: config.translations.emptyAchievements,
69
+ };
70
+
71
+ return <GamificationScreenInner {...screenProps} />;
72
+ };
@@ -1,22 +1,18 @@
1
- import React from "react";
2
1
  /**
3
2
  * GamificationScreen Component
4
3
  * Full gamification screen with integrated hook
5
4
  * Generic for 100+ apps - NO hardcoded strings
5
+ *
6
+ * Split into multiple files to keep each under 200 lines:
7
+ * - GamificationScreen.tsx: Pure presentational component
8
+ * - GamificationScreenWithConfig.tsx: Config-based version with hook
9
+ * - index.tsx: Main entry point that routes to appropriate component
6
10
  */
7
11
 
8
- import { View, ScrollView } from "react-native";
9
- import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
10
- import { LevelProgress } from "../LevelProgress";
11
- import { StreakDisplay } from "../StreakDisplay";
12
- import { Header } from "./Header";
13
- import { StatsGrid } from "./StatsGrid";
14
- import { AchievementsList } from "./AchievementsList";
15
- import { styles } from "./styles";
12
+ import React from "react";
13
+ import { GamificationScreenInner } from "./GamificationScreen";
14
+ import { GamificationScreenWithConfig } from "./GamificationScreenWithConfig";
16
15
  import type { GamificationScreenProps, GamificationConfigProps } from "./types";
17
- import type { Achievement } from "../../types";
18
-
19
- export type { GamificationScreenProps };
20
16
 
21
17
  /**
22
18
  * GamificationScreen that accepts either detailed props OR a config object
@@ -32,169 +28,3 @@ export const GamificationScreen: React.FC<GamificationScreenProps | Gamification
32
28
  return <GamificationScreenInner {...(props as GamificationScreenProps)} />;
33
29
  };
34
30
 
35
- /**
36
- * Component that uses the hook - separated to satisfy React Hooks rules
37
- */
38
- const GamificationScreenWithConfig: React.FC<GamificationConfigProps> = ({ config }) => {
39
- // Import hook here to avoid conditional hook usage
40
- const { useGamification } = require("../../hooks/useGamification");
41
-
42
- const {
43
- points,
44
- level,
45
- streak,
46
- achievements,
47
- } = useGamification(config);
48
-
49
- // Transform store achievements to UI props
50
- const achievementItems = achievements.map((a: Achievement) => ({
51
- ...a,
52
- title: a.title,
53
- description: a.description,
54
- icon: a.icon,
55
- isUnlocked: a.isUnlocked,
56
- progress: a.progress,
57
- threshold: a.threshold,
58
- id: a.id,
59
- type: a.type
60
- }));
61
-
62
- const screenProps: GamificationScreenProps = {
63
- title: config.translations.title,
64
- statsTitle: config.translations.statsTitle,
65
- achievementsTitle: config.translations.achievementsTitle,
66
- streakTitle: config.translations.streakTitle,
67
- levelProps: {
68
- level: level.currentLevel,
69
- points: level.currentPoints,
70
- pointsToNext: level.pointsToNext,
71
- progress: level.progress,
72
- levelTitle: config.translations.levelTitle,
73
- showPoints: true,
74
- },
75
- streakProps: {
76
- current: streak.current,
77
- longest: streak.longest,
78
- currentLabel: config.translations.currentStreak,
79
- bestLabel: config.translations.bestStreak,
80
- daysLabel: config.translations.days,
81
- },
82
- stats: [
83
- {
84
- label: config.translations.statsTitle,
85
- value: points,
86
- icon: "star",
87
- },
88
- ],
89
- achievements: achievementItems,
90
- emptyAchievementsText: config.translations.emptyAchievements,
91
- };
92
-
93
- return <GamificationScreenInner {...screenProps} />;
94
- };
95
-
96
- /**
97
- * Internal component that renders the screen with all props
98
- */
99
- const GamificationScreenInner: React.FC<GamificationScreenProps> = ({
100
- title,
101
- statsTitle,
102
- achievementsTitle,
103
- streakTitle,
104
- levelProps,
105
- stats,
106
- achievements,
107
- streakProps,
108
- emptyAchievementsText,
109
- containerStyle,
110
- headerStyle,
111
- titleStyle,
112
- sectionTitleStyle,
113
- accentColor,
114
- backgroundColor,
115
- cardBackgroundColor,
116
- textColor,
117
- subtextColor,
118
- headerComponent,
119
- }) => {
120
- const tokens = useAppDesignTokens();
121
-
122
- // Use tokens for fallbacks
123
- const finalAccentColor = accentColor || tokens.colors.primary;
124
- const finalBackgroundColor = backgroundColor || tokens.colors.backgroundPrimary;
125
- const finalCardBackgroundColor = cardBackgroundColor || tokens.colors.surface;
126
- const finalTextColor = textColor || tokens.colors.textPrimary;
127
- const finalSubtextColor = subtextColor || tokens.colors.textSecondary;
128
-
129
- return (
130
- <View style={[styles.container, { backgroundColor: finalBackgroundColor }, containerStyle]}>
131
- {headerComponent}
132
-
133
- <ScrollView
134
- style={styles.scrollView}
135
- contentContainerStyle={styles.scrollContent}
136
- showsVerticalScrollIndicator={false}
137
- >
138
- {/* Header */}
139
- <Header
140
- title={title}
141
- headerStyle={headerStyle}
142
- titleStyle={titleStyle}
143
- textColor={finalTextColor}
144
- />
145
-
146
- {/* Level Progress */}
147
- <View style={styles.section}>
148
- <LevelProgress
149
- {...levelProps}
150
- primaryColor={finalAccentColor}
151
- backgroundColor={finalCardBackgroundColor}
152
- textColor={finalTextColor}
153
- subtextColor={finalSubtextColor}
154
- />
155
- </View>
156
-
157
- {/* Streak (if provided) */}
158
- {streakProps && (
159
- <View style={styles.section}>
160
- <AtomicText
161
- style={[styles.sectionTitle, { color: finalTextColor }, sectionTitleStyle]}
162
- >
163
- {streakTitle}
164
- </AtomicText>
165
- <StreakDisplay
166
- {...streakProps}
167
- primaryColor={finalAccentColor}
168
- backgroundColor={finalCardBackgroundColor}
169
- textColor={finalTextColor}
170
- subtextColor={finalSubtextColor}
171
- />
172
- </View>
173
- )}
174
-
175
- {/* Stats Grid */}
176
- <StatsGrid
177
- statsTitle={statsTitle}
178
- stats={stats}
179
- accentColor={finalAccentColor}
180
- cardBackgroundColor={finalCardBackgroundColor}
181
- textColor={finalTextColor}
182
- subtextColor={finalSubtextColor}
183
- sectionTitleStyle={sectionTitleStyle}
184
- />
185
-
186
- {/* Achievements */}
187
- <AchievementsList
188
- achievementsTitle={achievementsTitle}
189
- achievements={achievements}
190
- emptyAchievementsText={emptyAchievementsText}
191
- accentColor={finalAccentColor}
192
- cardBackgroundColor={finalCardBackgroundColor}
193
- textColor={finalTextColor}
194
- subtextColor={finalSubtextColor}
195
- sectionTitleStyle={sectionTitleStyle}
196
- />
197
- </ScrollView>
198
- </View>
199
- );
200
- };
@@ -10,4 +10,5 @@ export { AchievementToast, type AchievementToastProps } from "./AchievementToast
10
10
  export { StreakDisplay, type StreakDisplayProps } from "./StreakDisplay";
11
11
  export { StatsCard, type StatsCardProps } from "./StatsCard";
12
12
  export { AchievementItem, type AchievementItemProps } from "./AchievementItem";
13
- export { GamificationScreen, type GamificationScreenProps } from "./GamificationScreen/index";
13
+ export { GamificationScreen } from "./GamificationScreen/index";
14
+ export type { GamificationScreenProps, GamificationConfigProps } from "./GamificationScreen/types";
@@ -3,9 +3,8 @@
3
3
  * Shared logic for legal document screens
4
4
  */
5
5
  import React from "react";
6
- import { View, ScrollView, StyleSheet } from "react-native";
7
- import { useSafeAreaInsets } from "react-native-safe-area-context";
8
- import { useAppDesignTokens, type DesignTokens } from "@umituz/react-native-design-system";
6
+ import { View, StyleSheet } from "react-native";
7
+ import { useAppDesignTokens, type DesignTokens, ScreenLayout } from "@umituz/react-native-design-system";
9
8
  import { AtomicText, AtomicButton } from "@umituz/react-native-design-system";
10
9
  import { UrlHandlerService } from "../../domain/services/UrlHandlerService";
11
10
  import { ContentValidationService } from "../../domain/services/ContentValidationService";
@@ -35,7 +34,6 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
35
34
  createStyles,
36
35
  }) => {
37
36
  const tokens = useAppDesignTokens();
38
- const insets = useSafeAreaInsets();
39
37
 
40
38
  const styles = React.useMemo(() => {
41
39
  const cacheKey = StyleCacheService.createTokenCacheKey(tokens);
@@ -58,14 +56,6 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
58
56
  }
59
57
  }, [onUrlPress, url]);
60
58
 
61
- const containerStyle = React.useMemo(() => [
62
- styles.container,
63
- {
64
- backgroundColor: tokens.colors.backgroundPrimary,
65
- paddingTop: insets.top,
66
- },
67
- ], [styles.container, tokens.colors.backgroundPrimary, insets.top]);
68
-
69
59
  const showContent = React.useMemo(() => !!(content), [content]);
70
60
  const showUrlSection = React.useMemo(() =>
71
61
  ContentValidationService.shouldShowUrlSection(url, onUrlPress),
@@ -107,23 +97,23 @@ export const LegalContentScreen: React.FC<LegalContentScreenProps> = React.memo(
107
97
  }, [showContent, showUrlSection, styles, content, viewOnlineText, openText, handleUrlPress]);
108
98
 
109
99
  return (
110
- <View style={containerStyle} testID={testID}>
111
- <ScrollView
112
- contentContainerStyle={styles.scrollContent}
113
- showsVerticalScrollIndicator={false}
114
- >
115
- <View style={styles.content}>
116
- <AtomicText
117
- type="headlineLarge"
118
- color="primary"
119
- style={styles.title}
120
- >
121
- {title}
122
- </AtomicText>
100
+ <ScreenLayout
101
+ testID={testID}
102
+ scrollable={true}
103
+ hideScrollIndicator={false}
104
+ contentContainerStyle={styles.scrollContent}
105
+ >
106
+ <View style={styles.content}>
107
+ <AtomicText
108
+ type="headlineLarge"
109
+ color="primary"
110
+ style={styles.title}
111
+ >
112
+ {title}
113
+ </AtomicText>
123
114
 
124
- {contentSection}
125
- </View>
126
- </ScrollView>
127
- </View>
115
+ {contentSection}
116
+ </View>
117
+ </ScreenLayout>
128
118
  );
129
119
  });
@@ -29,8 +29,6 @@ export class NotificationManager {
29
29
  shouldShowAlert: true,
30
30
  shouldPlaySound: true,
31
31
  shouldSetBadge: true,
32
- shouldShowBanner: true,
33
- shouldShowList: true,
34
32
  }),
35
33
  });
36
34
 
@@ -22,7 +22,11 @@ export class NotificationService {
22
22
 
23
23
  private constructor() {
24
24
  // Configure notification handler on initialization
25
- NotificationManager.configure();
25
+ try {
26
+ NotificationManager.configure();
27
+ } catch (error) {
28
+ console.error('[NotificationService] Failed to configure notification handler:', error);
29
+ }
26
30
  }
27
31
 
28
32
  static getInstance(): NotificationService {
package/src/index.ts CHANGED
@@ -91,7 +91,6 @@ export type { SettingsSectionProps } from './presentation/components/SettingsSec
91
91
  export { SettingsFooter } from './presentation/components/SettingsFooter';
92
92
  export type { SettingsFooterProps } from './presentation/components/SettingsFooter';
93
93
 
94
- export { SettingsErrorBoundary } from './presentation/components/SettingsErrorBoundary';
95
94
 
96
95
 
97
96
  // =============================================================================
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Infrastructure Utilities
3
+ * Centralized utility functions for common operations
4
+ */
5
+
6
+ export * from './styleUtils';
7
+ export * from './memoUtils';