@umituz/react-native-gamification 1.4.1 → 1.4.3
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 +2 -2
- package/src/store/gamificationStore.ts +127 -132
- package/LICENSE +0 -29
- package/README.md +0 -283
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-gamification",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|
|
@@ -51,4 +51,4 @@
|
|
|
51
51
|
"README.md",
|
|
52
52
|
"LICENSE"
|
|
53
53
|
]
|
|
54
|
-
}
|
|
54
|
+
}
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
* Zustand store with persist middleware
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { persist, createJSONStorage } from "zustand/middleware";
|
|
8
|
-
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
6
|
+
import { createStore } from "@umituz/react-native-storage";
|
|
9
7
|
import type {
|
|
10
8
|
GamificationState,
|
|
11
9
|
GamificationActions,
|
|
@@ -34,134 +32,131 @@ const DEFAULT_STATE: GamificationState = {
|
|
|
34
32
|
|
|
35
33
|
let currentConfig: GamificationConfig | null = null;
|
|
36
34
|
|
|
37
|
-
export const useGamificationStore =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
persist
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
addPoints: (amount: number) => {
|
|
68
|
-
set((state) => ({ points: state.points + amount }));
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
completeTask: () => {
|
|
72
|
-
const state = get();
|
|
73
|
-
const pointsToAdd = currentConfig?.pointsPerAction ?? 15;
|
|
74
|
-
|
|
75
|
-
set({
|
|
76
|
-
totalTasksCompleted: state.totalTasksCompleted + 1,
|
|
77
|
-
points: state.points + pointsToAdd,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Update streak
|
|
81
|
-
get().updateStreak();
|
|
82
|
-
|
|
83
|
-
// Check achievements
|
|
84
|
-
get().checkAchievements();
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
updateStreak: () => {
|
|
88
|
-
const state = get();
|
|
89
|
-
const now = new Date();
|
|
90
|
-
const lastDate = state.streak.lastActivityDate
|
|
91
|
-
? new Date(state.streak.lastActivityDate)
|
|
92
|
-
: null;
|
|
93
|
-
|
|
94
|
-
let newStreak = state.streak.current;
|
|
95
|
-
|
|
96
|
-
if (!lastDate || !isSameDay(lastDate, now)) {
|
|
97
|
-
if (isStreakActive(state.streak.lastActivityDate)) {
|
|
98
|
-
newStreak = state.streak.current + 1;
|
|
99
|
-
} else {
|
|
100
|
-
newStreak = 1;
|
|
101
|
-
}
|
|
35
|
+
export const useGamificationStore = createStore<GamificationState, GamificationActions>({
|
|
36
|
+
name: "gamification-storage",
|
|
37
|
+
initialState: DEFAULT_STATE,
|
|
38
|
+
persist: true,
|
|
39
|
+
version: 1,
|
|
40
|
+
partialize: (state) => ({
|
|
41
|
+
points: state.points,
|
|
42
|
+
totalTasksCompleted: state.totalTasksCompleted,
|
|
43
|
+
achievements: state.achievements,
|
|
44
|
+
streak: state.streak,
|
|
45
|
+
isLoading: false,
|
|
46
|
+
isInitialized: false,
|
|
47
|
+
}),
|
|
48
|
+
actions: (set, get) => ({
|
|
49
|
+
initialize: async (config: GamificationConfig) => {
|
|
50
|
+
currentConfig = config;
|
|
51
|
+
const state = get();
|
|
52
|
+
|
|
53
|
+
// Initialize achievements from config
|
|
54
|
+
const achievements: Achievement[] = config.achievements.map((def) => ({
|
|
55
|
+
...def,
|
|
56
|
+
isUnlocked: false,
|
|
57
|
+
progress: 0,
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Merge with existing unlocked achievements
|
|
61
|
+
const mergedAchievements = achievements.map((ach) => {
|
|
62
|
+
const existing = state.achievements.find((a) => a.id === ach.id);
|
|
63
|
+
if (existing) {
|
|
64
|
+
return { ...ach, isUnlocked: existing.isUnlocked, progress: existing.progress };
|
|
102
65
|
}
|
|
66
|
+
return ach;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
set({ achievements: mergedAchievements, isInitialized: true });
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
addPoints: (amount: number) => {
|
|
73
|
+
const state = get();
|
|
74
|
+
set({ points: state.points + amount });
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
completeTask: () => {
|
|
78
|
+
const state = get();
|
|
79
|
+
const pointsToAdd = currentConfig?.pointsPerAction ?? 15;
|
|
80
|
+
|
|
81
|
+
set({
|
|
82
|
+
totalTasksCompleted: state.totalTasksCompleted + 1,
|
|
83
|
+
points: state.points + pointsToAdd,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Update streak
|
|
87
|
+
get().updateStreak();
|
|
88
|
+
|
|
89
|
+
// Check achievements
|
|
90
|
+
get().checkAchievements();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
updateStreak: () => {
|
|
94
|
+
const state = get();
|
|
95
|
+
const now = new Date();
|
|
96
|
+
const lastDate = state.streak.lastActivityDate
|
|
97
|
+
? new Date(state.streak.lastActivityDate)
|
|
98
|
+
: null;
|
|
99
|
+
|
|
100
|
+
let newStreak = state.streak.current;
|
|
101
|
+
|
|
102
|
+
if (!lastDate || !isSameDay(lastDate, now)) {
|
|
103
|
+
if (isStreakActive(state.streak.lastActivityDate)) {
|
|
104
|
+
newStreak = state.streak.current + 1;
|
|
105
|
+
} else {
|
|
106
|
+
newStreak = 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
set({
|
|
111
|
+
streak: {
|
|
112
|
+
current: newStreak,
|
|
113
|
+
longest: Math.max(state.streak.longest, newStreak),
|
|
114
|
+
lastActivityDate: now.toISOString(),
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
checkAchievements: () => {
|
|
120
|
+
if (!currentConfig) return [];
|
|
121
|
+
|
|
122
|
+
const state = get();
|
|
123
|
+
const newlyUnlocked: Achievement[] = [];
|
|
124
|
+
|
|
125
|
+
const updatedAchievements = state.achievements.map((ach) => {
|
|
126
|
+
if (ach.isUnlocked) return ach;
|
|
127
|
+
|
|
128
|
+
const progress = updateAchievementProgress(
|
|
129
|
+
ach,
|
|
130
|
+
state.totalTasksCompleted,
|
|
131
|
+
state.streak.current
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const shouldUnlock = checkAchievementUnlock(
|
|
135
|
+
ach,
|
|
136
|
+
state.totalTasksCompleted,
|
|
137
|
+
state.streak.current
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (shouldUnlock && !ach.isUnlocked) {
|
|
141
|
+
const unlocked = {
|
|
142
|
+
...ach,
|
|
143
|
+
isUnlocked: true,
|
|
144
|
+
unlockedAt: new Date().toISOString(),
|
|
145
|
+
progress: 100,
|
|
146
|
+
};
|
|
147
|
+
newlyUnlocked.push(unlocked);
|
|
148
|
+
return unlocked;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { ...ach, progress };
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
set({ achievements: updatedAchievements });
|
|
155
|
+
return newlyUnlocked;
|
|
156
|
+
},
|
|
103
157
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
checkAchievements: () => {
|
|
114
|
-
if (!currentConfig) return [];
|
|
115
|
-
|
|
116
|
-
const state = get();
|
|
117
|
-
const newlyUnlocked: Achievement[] = [];
|
|
118
|
-
|
|
119
|
-
const updatedAchievements = state.achievements.map((ach) => {
|
|
120
|
-
if (ach.isUnlocked) return ach;
|
|
121
|
-
|
|
122
|
-
const progress = updateAchievementProgress(
|
|
123
|
-
ach,
|
|
124
|
-
state.totalTasksCompleted,
|
|
125
|
-
state.streak.current
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const shouldUnlock = checkAchievementUnlock(
|
|
129
|
-
ach,
|
|
130
|
-
state.totalTasksCompleted,
|
|
131
|
-
state.streak.current
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
if (shouldUnlock && !ach.isUnlocked) {
|
|
135
|
-
const unlocked = {
|
|
136
|
-
...ach,
|
|
137
|
-
isUnlocked: true,
|
|
138
|
-
unlockedAt: new Date().toISOString(),
|
|
139
|
-
progress: 100,
|
|
140
|
-
};
|
|
141
|
-
newlyUnlocked.push(unlocked);
|
|
142
|
-
return unlocked;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return { ...ach, progress };
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
set({ achievements: updatedAchievements });
|
|
149
|
-
return newlyUnlocked;
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
reset: async () => {
|
|
153
|
-
set(DEFAULT_STATE);
|
|
154
|
-
},
|
|
155
|
-
}),
|
|
156
|
-
{
|
|
157
|
-
name: "gamification-storage",
|
|
158
|
-
storage: createJSONStorage(() => AsyncStorage),
|
|
159
|
-
partialize: (state) => ({
|
|
160
|
-
points: state.points,
|
|
161
|
-
totalTasksCompleted: state.totalTasksCompleted,
|
|
162
|
-
achievements: state.achievements,
|
|
163
|
-
streak: state.streak,
|
|
164
|
-
}),
|
|
165
|
-
}
|
|
166
|
-
)
|
|
167
|
-
);
|
|
158
|
+
reset: async () => {
|
|
159
|
+
set(DEFAULT_STATE);
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
});
|
package/LICENSE
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 Ümit UZ
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
package/README.md
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
# @umituz/react-native-gamification
|
|
2
|
-
|
|
3
|
-
Comprehensive gamification system for React Native apps with achievements, points, levels, streaks, leaderboards, rewards, and progress tracking.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🏆 **Achievements** - Track and unlock user achievements
|
|
8
|
-
- 💰 **Points System** - Award and manage user points
|
|
9
|
-
- 📈 **Levels** - Experience-based leveling system
|
|
10
|
-
- 🔥 **Streaks** - Track consecutive activities
|
|
11
|
-
- 🏅 **Leaderboards** - Competitive rankings
|
|
12
|
-
- 🎁 **Rewards** - Unlockable rewards system
|
|
13
|
-
- 📊 **Progress Tracking** - Monitor user progress across metrics
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install @umituz/react-native-gamification
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Peer Dependencies
|
|
22
|
-
|
|
23
|
-
- `react` >= 18.2.0
|
|
24
|
-
- `react-native` >= 0.74.0
|
|
25
|
-
- `@umituz/react-native-storage` (latest)
|
|
26
|
-
- `zustand` >= 4.0.0
|
|
27
|
-
|
|
28
|
-
## Quick Start
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { useGamification, useGamificationInitializer } from '@umituz/react-native-gamification';
|
|
32
|
-
|
|
33
|
-
function App() {
|
|
34
|
-
const userId = 'user-123';
|
|
35
|
-
useGamificationInitializer(userId);
|
|
36
|
-
|
|
37
|
-
const {
|
|
38
|
-
achievements,
|
|
39
|
-
pointBalance,
|
|
40
|
-
level,
|
|
41
|
-
addPoints,
|
|
42
|
-
addExperience
|
|
43
|
-
} = useGamification();
|
|
44
|
-
|
|
45
|
-
// Add points when user completes an action
|
|
46
|
-
const handleActionComplete = async () => {
|
|
47
|
-
await addPoints(10, 'action_completed', 'action-id', 'actions', 'Completed action');
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
// Your app UI
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Usage
|
|
57
|
-
|
|
58
|
-
### Initialization
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { useGamification, useGamificationInitializer } from '@umituz/react-native-gamification';
|
|
62
|
-
|
|
63
|
-
function MyComponent() {
|
|
64
|
-
const userId = 'user-123';
|
|
65
|
-
|
|
66
|
-
// Initialize gamification system
|
|
67
|
-
useGamificationInitializer(userId);
|
|
68
|
-
|
|
69
|
-
const gamification = useGamification();
|
|
70
|
-
|
|
71
|
-
// Check if initialized
|
|
72
|
-
if (!gamification.isInitialized) {
|
|
73
|
-
return <Loading />;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return <YourContent />;
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Achievements
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { useAchievements } from '@umituz/react-native-gamification';
|
|
84
|
-
|
|
85
|
-
function AchievementsScreen() {
|
|
86
|
-
const {
|
|
87
|
-
achievements,
|
|
88
|
-
unlockAchievement,
|
|
89
|
-
updateAchievementProgress
|
|
90
|
-
} = useAchievements();
|
|
91
|
-
|
|
92
|
-
// Update progress
|
|
93
|
-
await updateAchievementProgress('achievement-id', 50);
|
|
94
|
-
|
|
95
|
-
// Unlock achievement
|
|
96
|
-
await unlockAchievement('achievement-id');
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<View>
|
|
100
|
-
{achievements.map(achievement => (
|
|
101
|
-
<AchievementCard key={achievement.id} achievement={achievement} />
|
|
102
|
-
))}
|
|
103
|
-
</View>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Points
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
import { usePoints } from '@umituz/react-native-gamification';
|
|
112
|
-
|
|
113
|
-
function PointsDisplay() {
|
|
114
|
-
const {
|
|
115
|
-
pointBalance,
|
|
116
|
-
addPoints,
|
|
117
|
-
deductPoints
|
|
118
|
-
} = usePoints();
|
|
119
|
-
|
|
120
|
-
// Add points
|
|
121
|
-
await addPoints(100, 'purchase', 'purchase-id', 'purchases', 'Made a purchase');
|
|
122
|
-
|
|
123
|
-
// Deduct points
|
|
124
|
-
await deductPoints(50, 'reward_claim', 'reward-id', 'Claimed reward');
|
|
125
|
-
|
|
126
|
-
return (
|
|
127
|
-
<Text>Total Points: {pointBalance?.total || 0}</Text>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Levels
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { useLevel } from '@umituz/react-native-gamification';
|
|
136
|
-
|
|
137
|
-
function LevelDisplay() {
|
|
138
|
-
const {
|
|
139
|
-
level,
|
|
140
|
-
addExperience
|
|
141
|
-
} = useLevel();
|
|
142
|
-
|
|
143
|
-
// Add experience
|
|
144
|
-
await addExperience(50, 'action_completed');
|
|
145
|
-
|
|
146
|
-
return (
|
|
147
|
-
<View>
|
|
148
|
-
<Text>Level: {level?.currentLevel || 1}</Text>
|
|
149
|
-
<Text>XP: {level?.currentExperience || 0} / {level?.experienceToNextLevel || 100}</Text>
|
|
150
|
-
<ProgressBar progress={level?.levelProgress || 0} />
|
|
151
|
-
</View>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Streaks
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
import { useStreaks } from '@umituz/react-native-gamification';
|
|
160
|
-
|
|
161
|
-
function StreakDisplay() {
|
|
162
|
-
const {
|
|
163
|
-
streaks,
|
|
164
|
-
updateStreakActivity
|
|
165
|
-
} = useStreaks();
|
|
166
|
-
|
|
167
|
-
// Update streak
|
|
168
|
-
await updateStreakActivity('daily_login', new Date().toISOString());
|
|
169
|
-
|
|
170
|
-
const loginStreak = streaks.find(s => s.type === 'daily_login');
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
<Text>Login Streak: {loginStreak?.currentStreak || 0} days</Text>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Rewards
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
import { useRewards } from '@umituz/react-native-gamification';
|
|
182
|
-
|
|
183
|
-
function RewardsScreen() {
|
|
184
|
-
const {
|
|
185
|
-
rewards,
|
|
186
|
-
claimReward,
|
|
187
|
-
getAvailableRewards
|
|
188
|
-
} = useRewards();
|
|
189
|
-
|
|
190
|
-
const availableRewards = getAvailableRewards();
|
|
191
|
-
|
|
192
|
-
const handleClaim = async (rewardId: string) => {
|
|
193
|
-
const claim = await claimReward(rewardId);
|
|
194
|
-
if (claim) {
|
|
195
|
-
console.log('Reward claimed!');
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<View>
|
|
201
|
-
{availableRewards.map(reward => (
|
|
202
|
-
<RewardCard
|
|
203
|
-
key={reward.id}
|
|
204
|
-
reward={reward}
|
|
205
|
-
onClaim={() => handleClaim(reward.id)}
|
|
206
|
-
/>
|
|
207
|
-
))}
|
|
208
|
-
</View>
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Progress Tracking
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
import { useProgress } from '@umituz/react-native-gamification';
|
|
217
|
-
|
|
218
|
-
function ProgressTracker() {
|
|
219
|
-
const {
|
|
220
|
-
progress,
|
|
221
|
-
updateProgress
|
|
222
|
-
} = useProgress();
|
|
223
|
-
|
|
224
|
-
// Update progress
|
|
225
|
-
await updateProgress({
|
|
226
|
-
userId: 'user-123',
|
|
227
|
-
metric: 'goals_completed',
|
|
228
|
-
increment: 1,
|
|
229
|
-
category: 'goals',
|
|
230
|
-
period: 'all-time'
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const goalsProgress = progress.find(p => p.metric === 'goals_completed');
|
|
234
|
-
|
|
235
|
-
return (
|
|
236
|
-
<View>
|
|
237
|
-
<Text>Goals Completed: {goalsProgress?.currentValue || 0}</Text>
|
|
238
|
-
<ProgressBar progress={goalsProgress?.progress || 0} />
|
|
239
|
-
</View>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Custom Repository
|
|
245
|
-
|
|
246
|
-
You can provide a custom repository implementation for app-specific storage:
|
|
247
|
-
|
|
248
|
-
```typescript
|
|
249
|
-
import { useGamificationRepository } from '@umituz/react-native-gamification';
|
|
250
|
-
import { MyCustomRepository } from './MyCustomRepository';
|
|
251
|
-
|
|
252
|
-
function App() {
|
|
253
|
-
const customRepository = new MyCustomRepository();
|
|
254
|
-
|
|
255
|
-
useGamificationRepository(customRepository);
|
|
256
|
-
|
|
257
|
-
// Rest of your app
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
## Architecture
|
|
262
|
-
|
|
263
|
-
This package follows Domain-Driven Design (DDD) principles:
|
|
264
|
-
|
|
265
|
-
- **Domain Layer** - Entities and repository interfaces
|
|
266
|
-
- **Infrastructure Layer** - Repository implementations and stores
|
|
267
|
-
- **Presentation Layer** - React hooks for easy integration
|
|
268
|
-
|
|
269
|
-
## License
|
|
270
|
-
|
|
271
|
-
MIT
|
|
272
|
-
|
|
273
|
-
## Author
|
|
274
|
-
|
|
275
|
-
Ümit UZ <umit@umituz.com>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|