@umituz/react-native-gamification 1.2.1 → 1.3.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/LICENSE CHANGED
@@ -22,3 +22,8 @@ SOFTWARE.
22
22
 
23
23
 
24
24
 
25
+
26
+
27
+
28
+
29
+
package/README.md CHANGED
@@ -276,3 +276,8 @@ MIT
276
276
 
277
277
 
278
278
 
279
+
280
+
281
+
282
+
283
+
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "@umituz/react-native-gamification",
3
- "version": "1.2.1",
4
- "description": "Comprehensive gamification system for React Native apps with achievements, points, levels, streaks, leaderboards, rewards, and progress tracking",
3
+ "version": "1.3.0",
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",
7
7
  "scripts": {
8
8
  "typecheck": "tsc --noEmit",
9
- "lint": "tsc --noEmit",
10
- "version:minor": "npm version minor -m 'chore: release v%s'",
11
- "version:major": "npm version major -m 'chore: release v%s'"
9
+ "lint": "tsc --noEmit"
12
10
  },
13
11
  "keywords": [
14
12
  "react-native",
@@ -17,10 +15,8 @@
17
15
  "points",
18
16
  "levels",
19
17
  "streaks",
20
- "leaderboards",
21
- "rewards",
22
18
  "progress",
23
- "gameification"
19
+ "rewards"
24
20
  ],
25
21
  "author": "Ümit UZ <umit@umituz.com>",
26
22
  "license": "MIT",
@@ -29,19 +25,19 @@
29
25
  "url": "https://github.com/umituz/react-native-gamification"
30
26
  },
31
27
  "peerDependencies": {
28
+ "@react-native-async-storage/async-storage": ">=1.21.0",
32
29
  "react": ">=18.2.0",
33
30
  "react-native": ">=0.74.0",
34
- "@umituz/react-native-storage": "latest",
35
31
  "zustand": ">=4.0.0"
36
32
  },
37
33
  "devDependencies": {
34
+ "@react-native-async-storage/async-storage": "^1.23.1",
35
+ "@types/node": "^25.0.2",
38
36
  "@types/react": "^18.2.45",
39
37
  "@types/react-native": "^0.73.0",
40
38
  "react": "^18.2.0",
41
39
  "react-native": "^0.74.0",
42
- "typescript": "^5.3.3"
43
- },
44
- "dependencies": {
40
+ "typescript": "^5.3.3",
45
41
  "zustand": "^4.5.0"
46
42
  },
47
43
  "publishConfig": {
@@ -53,5 +49,3 @@
53
49
  "LICENSE"
54
50
  ]
55
51
  }
56
-
57
-
@@ -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,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,129 @@
1
+ /**
2
+ * LevelProgress Component
3
+ * Displays level and progress - 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 LevelProgressProps {
10
+ level: number;
11
+ points: number;
12
+ levelTitle: string;
13
+ pointsToNext: number;
14
+ progress: number;
15
+ showPoints?: boolean;
16
+ // Customization
17
+ containerStyle?: ViewStyle;
18
+ titleStyle?: TextStyle;
19
+ subtitleStyle?: TextStyle;
20
+ progressBarStyle?: ViewStyle;
21
+ progressFillStyle?: ViewStyle;
22
+ badgeStyle?: ViewStyle;
23
+ badgeTextStyle?: TextStyle;
24
+ // Colors (design system integration)
25
+ primaryColor?: string;
26
+ secondaryColor?: string;
27
+ backgroundColor?: string;
28
+ textColor?: string;
29
+ subtextColor?: string;
30
+ }
31
+
32
+ export const LevelProgress: React.FC<LevelProgressProps> = ({
33
+ level,
34
+ points,
35
+ levelTitle,
36
+ pointsToNext,
37
+ progress,
38
+ showPoints = true,
39
+ containerStyle,
40
+ titleStyle,
41
+ subtitleStyle,
42
+ progressBarStyle,
43
+ progressFillStyle,
44
+ badgeStyle,
45
+ badgeTextStyle,
46
+ primaryColor = "#FFD700",
47
+ secondaryColor = "#2A2A2A",
48
+ backgroundColor = "#1A1A1A",
49
+ textColor = "#FFFFFF",
50
+ subtextColor = "#888888",
51
+ }) => {
52
+ return (
53
+ <View style={[styles.container, { backgroundColor }, containerStyle]}>
54
+ <View style={styles.header}>
55
+ <View style={styles.titleSection}>
56
+ <Text style={[styles.levelTitle, { color: textColor }, titleStyle]}>
57
+ {levelTitle}
58
+ </Text>
59
+ {showPoints && (
60
+ <Text style={[styles.subtitle, { color: subtextColor }, subtitleStyle]}>
61
+ {points} / {points + pointsToNext}
62
+ </Text>
63
+ )}
64
+ </View>
65
+
66
+ <View style={[styles.badge, { backgroundColor: `${primaryColor}20`, borderColor: `${primaryColor}40` }, badgeStyle]}>
67
+ <Text style={[styles.badgeText, { color: primaryColor }, badgeTextStyle]}>
68
+ {level}
69
+ </Text>
70
+ </View>
71
+ </View>
72
+
73
+ <View style={[styles.progressBar, { backgroundColor: secondaryColor }, progressBarStyle]}>
74
+ <View
75
+ style={[
76
+ styles.progressFill,
77
+ { width: `${Math.min(100, progress)}%`, backgroundColor: primaryColor },
78
+ progressFillStyle,
79
+ ]}
80
+ />
81
+ </View>
82
+ </View>
83
+ );
84
+ };
85
+
86
+ const styles = StyleSheet.create({
87
+ container: {
88
+ borderRadius: 16,
89
+ padding: 16,
90
+ },
91
+ header: {
92
+ flexDirection: "row",
93
+ justifyContent: "space-between",
94
+ alignItems: "center",
95
+ marginBottom: 12,
96
+ },
97
+ titleSection: {
98
+ flex: 1,
99
+ },
100
+ levelTitle: {
101
+ fontSize: 18,
102
+ fontWeight: "700",
103
+ },
104
+ subtitle: {
105
+ fontSize: 14,
106
+ marginTop: 2,
107
+ },
108
+ badge: {
109
+ width: 48,
110
+ height: 48,
111
+ borderRadius: 24,
112
+ justifyContent: "center",
113
+ alignItems: "center",
114
+ borderWidth: 2,
115
+ },
116
+ badgeText: {
117
+ fontSize: 18,
118
+ fontWeight: "bold",
119
+ },
120
+ progressBar: {
121
+ height: 8,
122
+ borderRadius: 4,
123
+ overflow: "hidden",
124
+ },
125
+ progressFill: {
126
+ height: "100%",
127
+ borderRadius: 4,
128
+ },
129
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * PointsBadge Component
3
+ * Displays points with optional 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 PointsBadgeProps {
10
+ points: number;
11
+ icon?: React.ReactNode;
12
+ // Customization
13
+ containerStyle?: ViewStyle;
14
+ textStyle?: TextStyle;
15
+ // Colors
16
+ backgroundColor?: string;
17
+ textColor?: string;
18
+ borderColor?: string;
19
+ }
20
+
21
+ export const PointsBadge: React.FC<PointsBadgeProps> = ({
22
+ points,
23
+ icon,
24
+ containerStyle,
25
+ textStyle,
26
+ backgroundColor = "#FFD70020",
27
+ textColor = "#FFD700",
28
+ borderColor = "#FFD70040",
29
+ }) => {
30
+ return (
31
+ <View
32
+ style={[
33
+ styles.container,
34
+ { backgroundColor, borderColor },
35
+ containerStyle,
36
+ ]}
37
+ >
38
+ {icon}
39
+ <Text style={[styles.text, { color: textColor }, textStyle]}>
40
+ {points}
41
+ </Text>
42
+ </View>
43
+ );
44
+ };
45
+
46
+ const styles = StyleSheet.create({
47
+ container: {
48
+ flexDirection: "row",
49
+ alignItems: "center",
50
+ gap: 6,
51
+ paddingHorizontal: 12,
52
+ paddingVertical: 6,
53
+ borderRadius: 20,
54
+ borderWidth: 1,
55
+ },
56
+ text: {
57
+ fontSize: 16,
58
+ fontWeight: "bold",
59
+ },
60
+ });
@@ -0,0 +1,119 @@
1
+ /**
2
+ * StreakDisplay Component
3
+ * Displays streak information - 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 StreakDisplayProps {
10
+ current: number;
11
+ longest: number;
12
+ currentLabel: string; // e.g., "Current Streak"
13
+ bestLabel: string; // e.g., "Best"
14
+ daysLabel: string; // e.g., "days"
15
+ icon?: React.ReactNode;
16
+ // Customization
17
+ containerStyle?: ViewStyle;
18
+ numberStyle?: TextStyle;
19
+ labelStyle?: TextStyle;
20
+ // Colors
21
+ primaryColor?: string;
22
+ backgroundColor?: string;
23
+ textColor?: string;
24
+ subtextColor?: string;
25
+ }
26
+
27
+ export const StreakDisplay: React.FC<StreakDisplayProps> = ({
28
+ current,
29
+ longest,
30
+ currentLabel,
31
+ bestLabel,
32
+ daysLabel,
33
+ icon,
34
+ containerStyle,
35
+ numberStyle,
36
+ labelStyle,
37
+ primaryColor = "#FF6B35",
38
+ backgroundColor = "#1A1A1A",
39
+ textColor = "#FFFFFF",
40
+ subtextColor = "#888888",
41
+ }) => {
42
+ return (
43
+ <View style={[styles.container, { backgroundColor }, containerStyle]}>
44
+ <View style={styles.mainStreak}>
45
+ {icon && <View style={styles.iconContainer}>{icon}</View>}
46
+ <View style={styles.streakInfo}>
47
+ <Text style={[styles.number, { color: primaryColor }, numberStyle]}>
48
+ {current}
49
+ </Text>
50
+ <Text style={[styles.label, { color: subtextColor }, labelStyle]}>
51
+ {daysLabel}
52
+ </Text>
53
+ </View>
54
+ <Text style={[styles.currentLabel, { color: textColor }]}>
55
+ {currentLabel}
56
+ </Text>
57
+ </View>
58
+
59
+ <View style={[styles.bestStreak, { backgroundColor: `${primaryColor}20` }]}>
60
+ <Text style={[styles.bestLabel, { color: subtextColor }]}>
61
+ {bestLabel}
62
+ </Text>
63
+ <Text style={[styles.bestNumber, { color: primaryColor }]}>
64
+ {longest}
65
+ </Text>
66
+ </View>
67
+ </View>
68
+ );
69
+ };
70
+
71
+ const styles = StyleSheet.create({
72
+ container: {
73
+ borderRadius: 16,
74
+ padding: 16,
75
+ flexDirection: "row",
76
+ alignItems: "center",
77
+ justifyContent: "space-between",
78
+ },
79
+ mainStreak: {
80
+ flexDirection: "row",
81
+ alignItems: "center",
82
+ gap: 12,
83
+ },
84
+ iconContainer: {
85
+ width: 40,
86
+ height: 40,
87
+ justifyContent: "center",
88
+ alignItems: "center",
89
+ },
90
+ streakInfo: {
91
+ alignItems: "center",
92
+ },
93
+ number: {
94
+ fontSize: 32,
95
+ fontWeight: "bold",
96
+ },
97
+ label: {
98
+ fontSize: 12,
99
+ textTransform: "uppercase",
100
+ },
101
+ currentLabel: {
102
+ fontSize: 14,
103
+ fontWeight: "500",
104
+ },
105
+ bestStreak: {
106
+ paddingHorizontal: 12,
107
+ paddingVertical: 8,
108
+ borderRadius: 12,
109
+ alignItems: "center",
110
+ },
111
+ bestLabel: {
112
+ fontSize: 11,
113
+ textTransform: "uppercase",
114
+ },
115
+ bestNumber: {
116
+ fontSize: 18,
117
+ fontWeight: "bold",
118
+ },
119
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Gamification Components
3
+ * All text via props - NO hardcoded strings
4
+ */
5
+
6
+ export { LevelProgress, type LevelProgressProps } from "./LevelProgress";
7
+ export { PointsBadge, type PointsBadgeProps } from "./PointsBadge";
8
+ export { AchievementCard, type AchievementCardProps } from "./AchievementCard";
9
+ export { AchievementToast, type AchievementToastProps } from "./AchievementToast";
10
+ export { StreakDisplay, type StreakDisplayProps } from "./StreakDisplay";