@umituz/react-native-settings 4.20.62 → 4.21.2

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 (70) hide show
  1. package/package.json +6 -61
  2. package/src/domains/feedback/domain/entities/FeedbackEntity.ts +8 -8
  3. package/src/domains/gamification/components/AchievementCard.tsx +142 -0
  4. package/src/domains/gamification/components/AchievementItem.tsx +182 -0
  5. package/src/domains/gamification/components/AchievementToast.tsx +122 -0
  6. package/src/domains/gamification/components/GamificationScreen/AchievementsList.tsx +84 -0
  7. package/src/domains/gamification/components/GamificationScreen/Header.tsx +29 -0
  8. package/src/domains/gamification/components/GamificationScreen/StatsGrid.tsx +51 -0
  9. package/src/domains/gamification/components/GamificationScreen/index.tsx +111 -0
  10. package/src/domains/gamification/components/GamificationScreen/styles.ts +43 -0
  11. package/src/domains/gamification/components/GamificationScreen/types.ts +77 -0
  12. package/src/domains/gamification/components/GamificationScreenWrapper.tsx +4 -4
  13. package/src/domains/gamification/components/GamificationSettingsItem.tsx +1 -1
  14. package/src/domains/gamification/components/LevelProgress.tsx +129 -0
  15. package/src/domains/gamification/components/PointsBadge.tsx +60 -0
  16. package/src/domains/gamification/components/StatsCard.tsx +89 -0
  17. package/src/domains/gamification/components/StreakDisplay.tsx +119 -0
  18. package/src/domains/gamification/components/index.ts +13 -0
  19. package/src/domains/gamification/examples/gamification.config.example.ts +1 -1
  20. package/src/domains/gamification/hooks/useGamification.ts +91 -0
  21. package/src/domains/gamification/index.ts +46 -19
  22. package/src/domains/gamification/store/gamificationStore.ts +162 -0
  23. package/src/domains/gamification/types/index.ts +95 -23
  24. package/src/domains/gamification/types/settings.ts +28 -0
  25. package/src/domains/gamification/utils/calculations.ts +85 -0
  26. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -51
  27. package/.github/ISSUE_TEMPLATE/documentation.md +0 -52
  28. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -63
  29. package/.github/PULL_REQUEST_TEMPLATE.md +0 -84
  30. package/AI_AGENT_GUIDELINES.md +0 -367
  31. package/ARCHITECTURE.md +0 -246
  32. package/CHANGELOG.md +0 -67
  33. package/CODE_OF_CONDUCT.md +0 -75
  34. package/CONTRIBUTING.md +0 -107
  35. package/DOCUMENTATION_MIGRATION.md +0 -319
  36. package/DOCUMENTATION_TEMPLATE.md +0 -155
  37. package/SECURITY.md +0 -98
  38. package/SETTINGS_SCREEN_GUIDE.md +0 -185
  39. package/TESTING.md +0 -358
  40. package/src/__tests__/integration.test.tsx +0 -371
  41. package/src/__tests__/performance.test.tsx +0 -369
  42. package/src/__tests__/setup.test.tsx +0 -20
  43. package/src/__tests__/setup.ts +0 -154
  44. package/src/domains/about/__tests__/integration.test.tsx +0 -328
  45. package/src/domains/about/__tests__/types.d.ts +0 -5
  46. package/src/domains/about/domain/entities/__tests__/AppInfo.test.ts +0 -93
  47. package/src/domains/about/infrastructure/repositories/__tests__/AboutRepository.test.ts +0 -153
  48. package/src/domains/about/presentation/components/__tests__/AboutContent.simple.test.tsx +0 -178
  49. package/src/domains/about/presentation/components/__tests__/AboutContent.test.tsx +0 -293
  50. package/src/domains/about/presentation/components/__tests__/AboutHeader.test.tsx +0 -201
  51. package/src/domains/about/presentation/components/__tests__/AboutSettingItem.test.tsx +0 -71
  52. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.simple.test.tsx +0 -229
  53. package/src/domains/about/presentation/hooks/__tests__/useAboutInfo.test.tsx +0 -240
  54. package/src/domains/about/presentation/screens/__tests__/AboutScreen.simple.test.tsx +0 -199
  55. package/src/domains/about/presentation/screens/__tests__/AboutScreen.test.tsx +0 -366
  56. package/src/domains/about/utils/__tests__/index.test.ts +0 -408
  57. package/src/domains/appearance/__tests__/components/AppearanceScreen.test.tsx +0 -195
  58. package/src/domains/appearance/__tests__/hooks/index.test.tsx +0 -232
  59. package/src/domains/appearance/__tests__/integration/index.test.tsx +0 -207
  60. package/src/domains/appearance/__tests__/services/appearanceService.test.ts +0 -299
  61. package/src/domains/appearance/__tests__/setup.ts +0 -88
  62. package/src/domains/appearance/__tests__/stores/appearanceStore.test.tsx +0 -175
  63. package/src/domains/cloud-sync/presentation/components/__tests__/CloudSyncSetting.test.tsx +0 -78
  64. package/src/domains/legal/__tests__/ContentValidationService.test.ts +0 -195
  65. package/src/domains/legal/__tests__/StyleCacheService.test.ts +0 -110
  66. package/src/domains/legal/__tests__/UrlHandlerService.test.ts +0 -71
  67. package/src/domains/legal/__tests__/setup.ts +0 -82
  68. package/src/presentation/components/__tests__/SettingsErrorBoundary.test.tsx +0 -186
  69. package/src/presentation/screens/__tests__/SettingsScreen.test.tsx +0 -322
  70. package/src/presentation/screens/hooks/__tests__/useFeatureDetection.test.tsx +0 -261
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "4.20.62",
4
- "description": "Complete settings hub for React Native apps - consolidated package with settings, about, legal, appearance, feedback, FAQs, and rating",
3
+ "version": "4.21.2",
4
+ "description": "Complete settings hub for React Native apps - consolidated package with settings, about, legal, appearance, feedback, FAQs, rating, and gamification",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "scripts": {
8
8
  "typecheck": "tsc --noEmit",
9
9
  "lint": "eslint src",
10
10
  "lint:fix": "eslint src --fix",
11
- "version:patch": "npm version patch -m 'chore: release v%s'",
12
- "version:minor": "npm version minor -m 'chore: release v%s'",
13
- "version:major": "npm version major -m 'chore: release v%s'"
11
+ "vp": "npm version patch && npm publish && git push"
14
12
  },
15
13
  "keywords": [
16
14
  "react-native",
@@ -37,13 +35,10 @@
37
35
  "url": "https://github.com/umituz/react-native-settings"
38
36
  },
39
37
  "dependencies": {
40
- "@react-native-async-storage/async-storage": "^2.2.0",
41
38
  "@umituz/react-native-auth": "latest",
42
39
  "@umituz/react-native-design-system": "latest",
43
- "@umituz/react-native-gamification": "latest",
44
40
  "@umituz/react-native-localization": "latest",
45
41
  "@umituz/react-native-notifications": "latest",
46
- "@umituz/react-native-onboarding": "latest",
47
42
  "@umituz/react-native-storage": "latest",
48
43
  "@umituz/react-native-tanstack": "latest",
49
44
  "firebase": "^12.7.0"
@@ -55,79 +50,29 @@
55
50
  "@tanstack/react-query": ">=5.0.0",
56
51
  "react": ">=19.0.0",
57
52
  "react-native": ">=0.81.0",
58
- "react-native-safe-area-context": ">=4.0.0",
59
- "zustand": ">=5.0.0"
53
+ "react-native-safe-area-context": ">=4.0.0"
60
54
  },
61
55
  "devDependencies": {
62
- "@babel/plugin-transform-runtime": "^7.28.5",
63
56
  "@expo/vector-icons": "^15.0.0",
64
- "@gorhom/bottom-sheet": "^5.2.8",
65
- "@react-native-community/datetimepicker": "^8.5.1",
66
- "@react-navigation/bottom-tabs": "^7.9.0",
67
57
  "@react-navigation/native": "^7.1.26",
68
58
  "@react-navigation/stack": "^7.6.13",
69
- "@sentry/react-native": "^7.8.0",
70
- "@sentry/types": "^10.32.1",
71
59
  "@tanstack/react-query": "^5.0.0",
72
- "@types/jest": "^29.5.14",
73
60
  "@types/react": "~19.1.10",
74
61
  "@typescript-eslint/eslint-plugin": "^7.18.0",
75
62
  "@typescript-eslint/parser": "^7.18.0",
76
- "@umituz/react-native-auth": "latest",
77
- "@umituz/react-native-design-system": "latest",
78
- "@umituz/react-native-filesystem": "latest",
79
- "@umituz/react-native-firebase": "latest",
80
- "@umituz/react-native-haptics": "latest",
81
- "@umituz/react-native-localization": "latest",
82
- "@umituz/react-native-notifications": "latest",
83
- "@umituz/react-native-onboarding": "latest",
84
- "@umituz/react-native-sentry": "^1.4.3",
85
- "@umituz/react-native-storage": "latest",
86
- "@umituz/react-native-tanstack": "latest",
87
- "@umituz/react-native-uuid": "latest",
88
63
  "eslint": "^8.57.0",
89
64
  "eslint-plugin-react": "^7.37.5",
90
65
  "eslint-plugin-react-native": "^5.0.0",
91
- "expo-apple-authentication": "^8.0.8",
92
- "expo-application": "^7.0.8",
93
- "expo-clipboard": "^8.0.8",
94
- "expo-crypto": "^15.0.8",
95
- "expo-device": "^8.0.10",
96
- "expo-file-system": "^19.0.21",
97
- "expo-haptics": "^15.0.8",
98
- "expo-image": "^3.0.11",
99
- "expo-localization": "^17.0.8",
100
- "expo-notifications": "^0.32.15",
101
- "expo-sharing": "^14.0.8",
102
- "expo-video": "^3.0.15",
103
- "i18next": "^25.7.3",
104
66
  "react": "19.1.0",
105
- "react-i18next": "^16.5.0",
106
67
  "react-native": "0.81.5",
107
- "react-native-gesture-handler": "^2.30.0",
108
- "react-native-reanimated": "^4.2.1",
109
68
  "react-native-safe-area-context": "^5.6.2",
110
- "rn-emoji-keyboard": "^1.7.0",
111
- "typescript": "^5.3.0",
112
- "zustand": "^5.0.9"
69
+ "typescript": "^5.3.0"
113
70
  },
114
71
  "publishConfig": {
115
72
  "access": "public"
116
73
  },
117
74
  "files": [
118
75
  "src",
119
- "README.md",
120
- "LICENSE",
121
- "CONTRIBUTING.md",
122
- "CHANGELOG.md",
123
- "CODE_OF_CONDUCT.md",
124
- "SECURITY.md",
125
- "ARCHITECTURE.md",
126
- "TESTING.md",
127
- "AI_AGENT_GUIDELINES.md",
128
- "SETTINGS_SCREEN_GUIDE.md",
129
- "DOCUMENTATION_TEMPLATE.md",
130
- "DOCUMENTATION_MIGRATION.md",
131
- ".github"
76
+ "README.md"
132
77
  ]
133
78
  }
@@ -58,15 +58,15 @@ export function createFeedback(
58
58
  };
59
59
  }
60
60
 
61
+ /**
62
+ * Get feedback type label
63
+ * @deprecated Use translation keys instead: `feedback.type.${type}`
64
+ * This function is kept for backward compatibility but should not be used.
65
+ * Use t(`feedback.type.${type}`) from useLocalization() instead.
66
+ */
61
67
  export function getFeedbackTypeLabel(type: FeedbackType): string {
62
- const labels: Record<FeedbackType, string> = {
63
- general: 'General Feedback',
64
- bug_report: 'Bug Report',
65
- feature_request: 'Feature Request',
66
- improvement: 'Improvement',
67
- other: 'Other',
68
- };
69
- return labels[type] || 'Unknown';
68
+ // Return type key for translation
69
+ return type;
70
70
  }
71
71
 
72
72
  export function getFeedbackTypeEmoji(type: FeedbackType): string {
@@ -0,0 +1,142 @@
1
+ /**
2
+ * AchievementCard Component
3
+ * Displays achievement - all text via props
4
+ */
5
+
6
+ import React from "react";
7
+ import { View, Text, StyleSheet, type ViewStyle, type TextStyle } from "react-native";
8
+
9
+ export interface AchievementCardProps {
10
+ title: string;
11
+ description: string;
12
+ icon: React.ReactNode;
13
+ isUnlocked: boolean;
14
+ progress: number;
15
+ // Customization
16
+ containerStyle?: ViewStyle;
17
+ titleStyle?: TextStyle;
18
+ descriptionStyle?: TextStyle;
19
+ progressBarStyle?: ViewStyle;
20
+ // Colors
21
+ unlockedColor?: string;
22
+ lockedColor?: string;
23
+ backgroundColor?: string;
24
+ textColor?: string;
25
+ subtextColor?: string;
26
+ }
27
+
28
+ export const AchievementCard: React.FC<AchievementCardProps> = ({
29
+ title,
30
+ description,
31
+ icon,
32
+ isUnlocked,
33
+ progress,
34
+ containerStyle,
35
+ titleStyle,
36
+ descriptionStyle,
37
+ progressBarStyle,
38
+ unlockedColor = "#4CAF50",
39
+ lockedColor = "#666666",
40
+ backgroundColor = "#1A1A1A",
41
+ textColor = "#FFFFFF",
42
+ subtextColor = "#888888",
43
+ }) => {
44
+ const accentColor = isUnlocked ? unlockedColor : lockedColor;
45
+
46
+ return (
47
+ <View
48
+ style={[
49
+ styles.container,
50
+ { backgroundColor, borderColor: `${accentColor}40` },
51
+ containerStyle,
52
+ ]}
53
+ >
54
+ <View style={[styles.iconContainer, { backgroundColor: `${accentColor}20` }]}>
55
+ {icon}
56
+ </View>
57
+
58
+ <View style={styles.content}>
59
+ <Text
60
+ style={[
61
+ styles.title,
62
+ { color: isUnlocked ? textColor : subtextColor },
63
+ titleStyle,
64
+ ]}
65
+ >
66
+ {title}
67
+ </Text>
68
+ <Text style={[styles.description, { color: subtextColor }, descriptionStyle]}>
69
+ {description}
70
+ </Text>
71
+
72
+ {!isUnlocked && (
73
+ <View style={[styles.progressBar, { backgroundColor: `${lockedColor}40` }, progressBarStyle]}>
74
+ <View
75
+ style={[
76
+ styles.progressFill,
77
+ { width: `${progress}%`, backgroundColor: accentColor },
78
+ ]}
79
+ />
80
+ </View>
81
+ )}
82
+ </View>
83
+
84
+ {isUnlocked && (
85
+ <View style={[styles.checkmark, { backgroundColor: unlockedColor }]}>
86
+ <Text style={styles.checkmarkText}>✓</Text>
87
+ </View>
88
+ )}
89
+ </View>
90
+ );
91
+ };
92
+
93
+ const styles = StyleSheet.create({
94
+ container: {
95
+ flexDirection: "row",
96
+ alignItems: "center",
97
+ padding: 12,
98
+ borderRadius: 12,
99
+ borderWidth: 1,
100
+ gap: 12,
101
+ },
102
+ iconContainer: {
103
+ width: 48,
104
+ height: 48,
105
+ borderRadius: 24,
106
+ justifyContent: "center",
107
+ alignItems: "center",
108
+ },
109
+ content: {
110
+ flex: 1,
111
+ },
112
+ title: {
113
+ fontSize: 16,
114
+ fontWeight: "600",
115
+ },
116
+ description: {
117
+ fontSize: 13,
118
+ marginTop: 2,
119
+ },
120
+ progressBar: {
121
+ height: 4,
122
+ borderRadius: 2,
123
+ marginTop: 8,
124
+ overflow: "hidden",
125
+ },
126
+ progressFill: {
127
+ height: "100%",
128
+ borderRadius: 2,
129
+ },
130
+ checkmark: {
131
+ width: 24,
132
+ height: 24,
133
+ borderRadius: 12,
134
+ justifyContent: "center",
135
+ alignItems: "center",
136
+ },
137
+ checkmarkText: {
138
+ color: "#FFFFFF",
139
+ fontSize: 14,
140
+ fontWeight: "bold",
141
+ },
142
+ });
@@ -0,0 +1,182 @@
1
+ /**
2
+ * AchievementItem Component
3
+ * List item for achievements - all text via props
4
+ */
5
+
6
+ import React from "react";
7
+ import {
8
+ View,
9
+ Text,
10
+ StyleSheet,
11
+ type ViewStyle,
12
+ type TextStyle,
13
+ } from "react-native";
14
+
15
+ export interface AchievementItemProps {
16
+ title: string;
17
+ description: string;
18
+ icon: React.ReactNode;
19
+ isUnlocked: boolean;
20
+ progress: number;
21
+ threshold: number;
22
+ progressLabel?: string;
23
+ // Customization
24
+ containerStyle?: ViewStyle;
25
+ titleStyle?: TextStyle;
26
+ descriptionStyle?: TextStyle;
27
+ // Colors
28
+ accentColor?: string;
29
+ backgroundColor?: string;
30
+ textColor?: string;
31
+ subtextColor?: string;
32
+ lockedOpacity?: number;
33
+ }
34
+
35
+ export const AchievementItem: React.FC<AchievementItemProps> = ({
36
+ title,
37
+ description,
38
+ icon,
39
+ isUnlocked,
40
+ progress,
41
+ threshold,
42
+ progressLabel,
43
+ containerStyle,
44
+ titleStyle,
45
+ descriptionStyle,
46
+ accentColor = "#FFD700",
47
+ backgroundColor = "#1A1A1A",
48
+ textColor = "#FFFFFF",
49
+ subtextColor = "#888888",
50
+ lockedOpacity = 0.5,
51
+ }) => {
52
+ const progressPercent = Math.min((progress / threshold) * 100, 100);
53
+
54
+ return (
55
+ <View
56
+ style={[
57
+ styles.container,
58
+ { backgroundColor, opacity: isUnlocked ? 1 : lockedOpacity },
59
+ containerStyle,
60
+ ]}
61
+ >
62
+ <View
63
+ style={[styles.iconContainer, { backgroundColor: `${accentColor}20` }]}
64
+ >
65
+ {icon}
66
+ </View>
67
+
68
+ <View style={styles.content}>
69
+ <View style={styles.header}>
70
+ <Text style={[styles.title, { color: textColor }, titleStyle]}>
71
+ {title}
72
+ </Text>
73
+ {isUnlocked && (
74
+ <View style={[styles.checkmark, { backgroundColor: accentColor }]}>
75
+ <Text style={styles.checkmarkText}>✓</Text>
76
+ </View>
77
+ )}
78
+ </View>
79
+
80
+ <Text
81
+ style={[styles.description, { color: subtextColor }, descriptionStyle]}
82
+ numberOfLines={2}
83
+ >
84
+ {description}
85
+ </Text>
86
+
87
+ {!isUnlocked && (
88
+ <View style={styles.progressContainer}>
89
+ <View
90
+ style={[styles.progressBar, { backgroundColor: `${accentColor}30` }]}
91
+ >
92
+ <View
93
+ style={[
94
+ styles.progressFill,
95
+ {
96
+ backgroundColor: accentColor,
97
+ width: `${progressPercent}%`,
98
+ },
99
+ ]}
100
+ />
101
+ </View>
102
+ {progressLabel && (
103
+ <Text style={[styles.progressText, { color: subtextColor }]}>
104
+ {progressLabel}
105
+ </Text>
106
+ )}
107
+ </View>
108
+ )}
109
+ </View>
110
+ </View>
111
+ );
112
+ };
113
+
114
+ const styles = StyleSheet.create({
115
+ container: {
116
+ flexDirection: "row",
117
+ alignItems: "center",
118
+ padding: 12,
119
+ borderRadius: 12,
120
+ marginBottom: 8,
121
+ },
122
+ iconContainer: {
123
+ width: 48,
124
+ height: 48,
125
+ borderRadius: 24,
126
+ alignItems: "center",
127
+ justifyContent: "center",
128
+ marginRight: 12,
129
+ },
130
+ content: {
131
+ flex: 1,
132
+ },
133
+ header: {
134
+ flexDirection: "row",
135
+ alignItems: "center",
136
+ justifyContent: "space-between",
137
+ marginBottom: 4,
138
+ },
139
+ title: {
140
+ fontSize: 16,
141
+ fontWeight: "600",
142
+ flex: 1,
143
+ },
144
+ checkmark: {
145
+ width: 20,
146
+ height: 20,
147
+ borderRadius: 10,
148
+ alignItems: "center",
149
+ justifyContent: "center",
150
+ marginLeft: 8,
151
+ },
152
+ checkmarkText: {
153
+ color: "#000",
154
+ fontSize: 12,
155
+ fontWeight: "bold",
156
+ },
157
+ description: {
158
+ fontSize: 13,
159
+ lineHeight: 18,
160
+ },
161
+ progressContainer: {
162
+ marginTop: 8,
163
+ flexDirection: "row",
164
+ alignItems: "center",
165
+ gap: 8,
166
+ },
167
+ progressBar: {
168
+ flex: 1,
169
+ height: 4,
170
+ borderRadius: 2,
171
+ overflow: "hidden",
172
+ },
173
+ progressFill: {
174
+ height: "100%",
175
+ borderRadius: 2,
176
+ },
177
+ progressText: {
178
+ fontSize: 11,
179
+ minWidth: 40,
180
+ textAlign: "right",
181
+ },
182
+ });
@@ -0,0 +1,122 @@
1
+ /**
2
+ * AchievementToast Component
3
+ * Shows achievement unlock notification - all text via props
4
+ */
5
+
6
+ import React, { useEffect, useRef } from "react";
7
+ import { View, Text, StyleSheet, Animated, type ViewStyle, type TextStyle } from "react-native";
8
+
9
+ export interface AchievementToastProps {
10
+ visible: boolean;
11
+ title: string;
12
+ label: string; // e.g., "Achievement Unlocked!" - from app translations
13
+ icon: React.ReactNode;
14
+ onDismiss: () => void;
15
+ duration?: number;
16
+ // Customization
17
+ containerStyle?: ViewStyle;
18
+ labelStyle?: TextStyle;
19
+ titleStyle?: TextStyle;
20
+ // Colors
21
+ backgroundColor?: string;
22
+ textColor?: string;
23
+ labelColor?: string;
24
+ }
25
+
26
+ export const AchievementToast: React.FC<AchievementToastProps> = ({
27
+ visible,
28
+ title,
29
+ label,
30
+ icon,
31
+ onDismiss,
32
+ duration = 3000,
33
+ containerStyle,
34
+ labelStyle,
35
+ titleStyle,
36
+ backgroundColor = "#FFD700",
37
+ textColor = "#000000",
38
+ labelColor = "#00000080",
39
+ }) => {
40
+ const translateY = useRef(new Animated.Value(-100)).current;
41
+
42
+ useEffect(() => {
43
+ if (visible) {
44
+ Animated.sequence([
45
+ Animated.timing(translateY, {
46
+ toValue: 0,
47
+ duration: 400,
48
+ useNativeDriver: true,
49
+ }),
50
+ Animated.delay(duration),
51
+ Animated.timing(translateY, {
52
+ toValue: -100,
53
+ duration: 300,
54
+ useNativeDriver: true,
55
+ }),
56
+ ]).start(() => {
57
+ onDismiss();
58
+ });
59
+ }
60
+ }, [visible, duration, onDismiss, translateY]);
61
+
62
+ if (!visible) return null;
63
+
64
+ return (
65
+ <Animated.View
66
+ style={[
67
+ styles.container,
68
+ { transform: [{ translateY }] },
69
+ containerStyle,
70
+ ]}
71
+ >
72
+ <View style={[styles.toast, { backgroundColor }]}>
73
+ <View style={styles.iconContainer}>{icon}</View>
74
+ <View style={styles.content}>
75
+ <Text style={[styles.label, { color: labelColor }, labelStyle]}>
76
+ {label}
77
+ </Text>
78
+ <Text style={[styles.title, { color: textColor }, titleStyle]}>
79
+ {title}
80
+ </Text>
81
+ </View>
82
+ </View>
83
+ </Animated.View>
84
+ );
85
+ };
86
+
87
+ const styles = StyleSheet.create({
88
+ container: {
89
+ position: "absolute",
90
+ top: 60,
91
+ left: 16,
92
+ right: 16,
93
+ zIndex: 1000,
94
+ },
95
+ toast: {
96
+ flexDirection: "row",
97
+ alignItems: "center",
98
+ padding: 16,
99
+ borderRadius: 16,
100
+ gap: 12,
101
+ },
102
+ iconContainer: {
103
+ width: 40,
104
+ height: 40,
105
+ justifyContent: "center",
106
+ alignItems: "center",
107
+ },
108
+ content: {
109
+ flex: 1,
110
+ },
111
+ label: {
112
+ fontSize: 12,
113
+ fontWeight: "bold",
114
+ textTransform: "uppercase",
115
+ letterSpacing: 1,
116
+ },
117
+ title: {
118
+ fontSize: 16,
119
+ fontWeight: "600",
120
+ marginTop: 2,
121
+ },
122
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * GamificationScreen AchievementsList Component
3
+ */
4
+
5
+ import React from "react";
6
+ import { View, Text, type TextStyle } from "react-native";
7
+ import { AchievementItem } from "../AchievementItem";
8
+ import { styles } from "./styles";
9
+ import type { AchievementItemProps } from "../AchievementItem";
10
+
11
+ export interface AchievementsListProps {
12
+ achievementsTitle: string;
13
+ achievements: Array<
14
+ Omit<
15
+ AchievementItemProps,
16
+ | "accentColor"
17
+ | "backgroundColor"
18
+ | "textColor"
19
+ | "subtextColor"
20
+ | "lockedOpacity"
21
+ >
22
+ >;
23
+ emptyAchievementsText?: string;
24
+ accentColor: string;
25
+ cardBackgroundColor: string;
26
+ textColor: string;
27
+ subtextColor: string;
28
+ sectionTitleStyle?: TextStyle;
29
+ }
30
+
31
+ export const AchievementsList: React.FC<AchievementsListProps> = ({
32
+ achievementsTitle,
33
+ achievements,
34
+ emptyAchievementsText,
35
+ accentColor,
36
+ cardBackgroundColor,
37
+ textColor,
38
+ subtextColor,
39
+ sectionTitleStyle,
40
+ }) => {
41
+ const unlockedAchievements = achievements.filter((a) => a.isUnlocked);
42
+ const lockedAchievements = achievements.filter((a) => !a.isUnlocked);
43
+
44
+ return (
45
+ <View style={styles.section}>
46
+ <Text style={[styles.sectionTitle, { color: textColor }, sectionTitleStyle]}>
47
+ {achievementsTitle}
48
+ </Text>
49
+
50
+ {achievements.length === 0 && emptyAchievementsText ? (
51
+ <Text style={[styles.emptyText, { color: subtextColor }]}>
52
+ {emptyAchievementsText}
53
+ </Text>
54
+ ) : (
55
+ <>
56
+ {/* Unlocked achievements first */}
57
+ {unlockedAchievements.map((achievement, index) => (
58
+ <AchievementItem
59
+ key={`unlocked-${index}`}
60
+ {...achievement}
61
+ accentColor={accentColor}
62
+ backgroundColor={cardBackgroundColor}
63
+ textColor={textColor}
64
+ subtextColor={subtextColor}
65
+ />
66
+ ))}
67
+
68
+ {/* Locked achievements */}
69
+ {lockedAchievements.map((achievement, index) => (
70
+ <AchievementItem
71
+ key={`locked-${index}`}
72
+ {...achievement}
73
+ accentColor={accentColor}
74
+ backgroundColor={cardBackgroundColor}
75
+ textColor={textColor}
76
+ subtextColor={subtextColor}
77
+ lockedOpacity={0.6}
78
+ />
79
+ ))}
80
+ </>
81
+ )}
82
+ </View>
83
+ );
84
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * GamificationScreen Header Component
3
+ */
4
+
5
+ import React from "react";
6
+ import { View, Text, type ViewStyle, type TextStyle } from "react-native";
7
+ import { styles } from "./styles";
8
+
9
+ export interface HeaderProps {
10
+ title: string;
11
+ headerStyle?: ViewStyle;
12
+ titleStyle?: TextStyle;
13
+ textColor: string;
14
+ }
15
+
16
+ export const Header: React.FC<HeaderProps> = ({
17
+ title,
18
+ headerStyle,
19
+ titleStyle,
20
+ textColor,
21
+ }) => {
22
+ return (
23
+ <View style={[styles.header, headerStyle]}>
24
+ <Text style={[styles.title, { color: textColor }, titleStyle]}>
25
+ {title}
26
+ </Text>
27
+ </View>
28
+ );
29
+ };