@umituz/react-native-gamification 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-gamification",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Generic gamification system for React Native apps - achievements, points, levels, streaks with customizable UI components",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -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,236 @@
1
+ /**
2
+ * GamificationScreen Component
3
+ * Full gamification screen - all text via props
4
+ * Generic for 100+ apps - NO hardcoded strings
5
+ */
6
+
7
+ import React from "react";
8
+ import {
9
+ View,
10
+ Text,
11
+ ScrollView,
12
+ StyleSheet,
13
+ type ViewStyle,
14
+ type TextStyle,
15
+ } from "react-native";
16
+ import { LevelProgress, type LevelProgressProps } from "./LevelProgress";
17
+ import { StatsCard, type StatsCardProps } from "./StatsCard";
18
+ import { AchievementItem, type AchievementItemProps } from "./AchievementItem";
19
+ import { StreakDisplay, type StreakDisplayProps } from "./StreakDisplay";
20
+
21
+ export interface GamificationScreenProps {
22
+ // Section titles (all via props)
23
+ title: string;
24
+ statsTitle: string;
25
+ achievementsTitle: string;
26
+ streakTitle: string;
27
+
28
+ // Level data
29
+ levelProps: Omit<LevelProgressProps, "primaryColor" | "backgroundColor" | "textColor" | "subtextColor">;
30
+
31
+ // Stats data
32
+ stats: Array<Omit<StatsCardProps, "accentColor" | "backgroundColor" | "textColor" | "subtextColor">>;
33
+
34
+ // Achievements data
35
+ achievements: Array<
36
+ Omit<AchievementItemProps, "accentColor" | "backgroundColor" | "textColor" | "subtextColor" | "lockedOpacity">
37
+ >;
38
+
39
+ // Streak data (optional)
40
+ streakProps?: Omit<StreakDisplayProps, "primaryColor" | "backgroundColor" | "textColor" | "subtextColor">;
41
+
42
+ // Empty state
43
+ emptyAchievementsText?: string;
44
+
45
+ // Customization
46
+ containerStyle?: ViewStyle;
47
+ headerStyle?: ViewStyle;
48
+ titleStyle?: TextStyle;
49
+ sectionTitleStyle?: TextStyle;
50
+
51
+ // Colors (applied to all child components)
52
+ accentColor?: string;
53
+ backgroundColor?: string;
54
+ cardBackgroundColor?: string;
55
+ textColor?: string;
56
+ subtextColor?: string;
57
+
58
+ // Header component (optional - for back button etc)
59
+ headerComponent?: React.ReactNode;
60
+ }
61
+
62
+ export const GamificationScreen: React.FC<GamificationScreenProps> = ({
63
+ title,
64
+ statsTitle,
65
+ achievementsTitle,
66
+ streakTitle,
67
+ levelProps,
68
+ stats,
69
+ achievements,
70
+ streakProps,
71
+ emptyAchievementsText,
72
+ containerStyle,
73
+ headerStyle,
74
+ titleStyle,
75
+ sectionTitleStyle,
76
+ accentColor = "#FFD700",
77
+ backgroundColor = "#0A0A0A",
78
+ cardBackgroundColor = "#1A1A1A",
79
+ textColor = "#FFFFFF",
80
+ subtextColor = "#888888",
81
+ headerComponent,
82
+ }) => {
83
+ const unlockedAchievements = achievements.filter((a) => a.isUnlocked);
84
+ const lockedAchievements = achievements.filter((a) => !a.isUnlocked);
85
+
86
+ return (
87
+ <View style={[styles.container, { backgroundColor }, containerStyle]}>
88
+ {headerComponent}
89
+
90
+ <ScrollView
91
+ style={styles.scrollView}
92
+ contentContainerStyle={styles.scrollContent}
93
+ showsVerticalScrollIndicator={false}
94
+ >
95
+ {/* Header */}
96
+ <View style={[styles.header, headerStyle]}>
97
+ <Text style={[styles.title, { color: textColor }, titleStyle]}>
98
+ {title}
99
+ </Text>
100
+ </View>
101
+
102
+ {/* Level Progress */}
103
+ <View style={styles.section}>
104
+ <LevelProgress
105
+ {...levelProps}
106
+ primaryColor={accentColor}
107
+ backgroundColor={cardBackgroundColor}
108
+ textColor={textColor}
109
+ subtextColor={subtextColor}
110
+ />
111
+ </View>
112
+
113
+ {/* Streak (if provided) */}
114
+ {streakProps && (
115
+ <View style={styles.section}>
116
+ <Text
117
+ style={[styles.sectionTitle, { color: textColor }, sectionTitleStyle]}
118
+ >
119
+ {streakTitle}
120
+ </Text>
121
+ <StreakDisplay
122
+ {...streakProps}
123
+ primaryColor={accentColor}
124
+ backgroundColor={cardBackgroundColor}
125
+ textColor={textColor}
126
+ subtextColor={subtextColor}
127
+ />
128
+ </View>
129
+ )}
130
+
131
+ {/* Stats Grid */}
132
+ {stats.length > 0 && (
133
+ <View style={styles.section}>
134
+ <Text
135
+ style={[styles.sectionTitle, { color: textColor }, sectionTitleStyle]}
136
+ >
137
+ {statsTitle}
138
+ </Text>
139
+ <View style={styles.statsGrid}>
140
+ {stats.map((stat, index) => (
141
+ <StatsCard
142
+ key={index}
143
+ {...stat}
144
+ accentColor={accentColor}
145
+ backgroundColor={cardBackgroundColor}
146
+ textColor={textColor}
147
+ subtextColor={subtextColor}
148
+ />
149
+ ))}
150
+ </View>
151
+ </View>
152
+ )}
153
+
154
+ {/* Achievements */}
155
+ <View style={styles.section}>
156
+ <Text
157
+ style={[styles.sectionTitle, { color: textColor }, sectionTitleStyle]}
158
+ >
159
+ {achievementsTitle}
160
+ </Text>
161
+
162
+ {achievements.length === 0 && emptyAchievementsText ? (
163
+ <Text style={[styles.emptyText, { color: subtextColor }]}>
164
+ {emptyAchievementsText}
165
+ </Text>
166
+ ) : (
167
+ <>
168
+ {/* Unlocked achievements first */}
169
+ {unlockedAchievements.map((achievement, index) => (
170
+ <AchievementItem
171
+ key={`unlocked-${index}`}
172
+ {...achievement}
173
+ accentColor={accentColor}
174
+ backgroundColor={cardBackgroundColor}
175
+ textColor={textColor}
176
+ subtextColor={subtextColor}
177
+ />
178
+ ))}
179
+
180
+ {/* Locked achievements */}
181
+ {lockedAchievements.map((achievement, index) => (
182
+ <AchievementItem
183
+ key={`locked-${index}`}
184
+ {...achievement}
185
+ accentColor={accentColor}
186
+ backgroundColor={cardBackgroundColor}
187
+ textColor={textColor}
188
+ subtextColor={subtextColor}
189
+ lockedOpacity={0.6}
190
+ />
191
+ ))}
192
+ </>
193
+ )}
194
+ </View>
195
+ </ScrollView>
196
+ </View>
197
+ );
198
+ };
199
+
200
+ const styles = StyleSheet.create({
201
+ container: {
202
+ flex: 1,
203
+ },
204
+ scrollView: {
205
+ flex: 1,
206
+ },
207
+ scrollContent: {
208
+ padding: 16,
209
+ paddingBottom: 32,
210
+ },
211
+ header: {
212
+ marginBottom: 20,
213
+ },
214
+ title: {
215
+ fontSize: 28,
216
+ fontWeight: "bold",
217
+ },
218
+ section: {
219
+ marginBottom: 24,
220
+ },
221
+ sectionTitle: {
222
+ fontSize: 18,
223
+ fontWeight: "600",
224
+ marginBottom: 12,
225
+ },
226
+ statsGrid: {
227
+ flexDirection: "row",
228
+ flexWrap: "wrap",
229
+ gap: 12,
230
+ },
231
+ emptyText: {
232
+ fontSize: 14,
233
+ textAlign: "center",
234
+ paddingVertical: 20,
235
+ },
236
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * StatsCard Component
3
+ * Displays a stat with icon - 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 StatsCardProps {
10
+ value: number;
11
+ label: string;
12
+ icon: React.ReactNode;
13
+ suffix?: string;
14
+ // Customization
15
+ containerStyle?: ViewStyle;
16
+ valueStyle?: TextStyle;
17
+ labelStyle?: TextStyle;
18
+ // Colors
19
+ accentColor?: string;
20
+ backgroundColor?: string;
21
+ textColor?: string;
22
+ subtextColor?: string;
23
+ }
24
+
25
+ export const StatsCard: React.FC<StatsCardProps> = ({
26
+ value,
27
+ label,
28
+ icon,
29
+ suffix,
30
+ containerStyle,
31
+ valueStyle,
32
+ labelStyle,
33
+ accentColor = "#FFD700",
34
+ backgroundColor = "#1A1A1A",
35
+ textColor = "#FFFFFF",
36
+ subtextColor = "#888888",
37
+ }) => {
38
+ return (
39
+ <View style={[styles.container, { backgroundColor }, containerStyle]}>
40
+ <View style={[styles.iconContainer, { backgroundColor: `${accentColor}15` }]}>
41
+ {icon}
42
+ </View>
43
+ <View style={styles.valueRow}>
44
+ <Text style={[styles.value, { color: textColor }, valueStyle]}>
45
+ {value}
46
+ </Text>
47
+ {suffix && (
48
+ <Text style={[styles.suffix, { color: subtextColor }]}>{suffix}</Text>
49
+ )}
50
+ </View>
51
+ <Text style={[styles.label, { color: subtextColor }, labelStyle]}>
52
+ {label}
53
+ </Text>
54
+ </View>
55
+ );
56
+ };
57
+
58
+ const styles = StyleSheet.create({
59
+ container: {
60
+ flex: 1,
61
+ minWidth: "45%",
62
+ borderRadius: 12,
63
+ padding: 12,
64
+ },
65
+ iconContainer: {
66
+ width: 36,
67
+ height: 36,
68
+ borderRadius: 18,
69
+ alignItems: "center",
70
+ justifyContent: "center",
71
+ marginBottom: 8,
72
+ },
73
+ valueRow: {
74
+ flexDirection: "row",
75
+ alignItems: "baseline",
76
+ gap: 4,
77
+ },
78
+ value: {
79
+ fontSize: 24,
80
+ fontWeight: "bold",
81
+ },
82
+ suffix: {
83
+ fontSize: 12,
84
+ },
85
+ label: {
86
+ fontSize: 12,
87
+ marginTop: 2,
88
+ },
89
+ });
@@ -8,3 +8,6 @@ export { PointsBadge, type PointsBadgeProps } from "./PointsBadge";
8
8
  export { AchievementCard, type AchievementCardProps } from "./AchievementCard";
9
9
  export { AchievementToast, type AchievementToastProps } from "./AchievementToast";
10
10
  export { StreakDisplay, type StreakDisplayProps } from "./StreakDisplay";
11
+ export { StatsCard, type StatsCardProps } from "./StatsCard";
12
+ export { AchievementItem, type AchievementItemProps } from "./AchievementItem";
13
+ export { GamificationScreen, type GamificationScreenProps } from "./GamificationScreen";
package/src/index.ts CHANGED
@@ -46,4 +46,10 @@ export {
46
46
  type AchievementToastProps,
47
47
  StreakDisplay,
48
48
  type StreakDisplayProps,
49
+ StatsCard,
50
+ type StatsCardProps,
51
+ AchievementItem,
52
+ type AchievementItemProps,
53
+ GamificationScreen,
54
+ type GamificationScreenProps,
49
55
  } from "./components";