@umituz/react-native-gamification 1.2.0 → 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 +5 -0
- package/README.md +5 -0
- package/package.json +8 -14
- package/src/components/AchievementCard.tsx +142 -0
- package/src/components/AchievementToast.tsx +122 -0
- package/src/components/LevelProgress.tsx +129 -0
- package/src/components/PointsBadge.tsx +60 -0
- package/src/components/StreakDisplay.tsx +119 -0
- package/src/components/index.ts +10 -0
- package/src/hooks/useGamification.ts +91 -0
- package/src/index.ts +35 -111
- package/src/store/gamificationStore.ts +167 -0
- package/src/types/index.ts +103 -0
- package/src/utils/calculations.ts +85 -0
- package/src/domain/entities/Achievement.ts +0 -98
- package/src/domain/entities/Leaderboard.ts +0 -101
- package/src/domain/entities/Level.ts +0 -40
- package/src/domain/entities/Point.ts +0 -40
- package/src/domain/entities/Progress.ts +0 -43
- package/src/domain/entities/Reward.ts +0 -131
- package/src/domain/entities/Streak.ts +0 -154
- package/src/domain/repositories/IGamificationRepository.ts +0 -235
- package/src/infrastructure/repositories/StorageGamificationRepository.ts +0 -734
- package/src/infrastructure/storage/GamificationStore.ts +0 -350
- package/src/presentation/hooks/useAchievements.ts +0 -37
- package/src/presentation/hooks/useGamification.ts +0 -93
- package/src/presentation/hooks/useLevel.ts +0 -39
- package/src/presentation/hooks/usePoints.ts +0 -36
- package/src/presentation/hooks/useProgress.ts +0 -33
- package/src/presentation/hooks/useRewards.ts +0 -43
- package/src/presentation/hooks/useStreaks.ts +0 -36
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gamification Store - Unified State Management
|
|
3
|
-
*
|
|
4
|
-
* Uses Zustand for state management with repository pattern
|
|
5
|
-
* App-specific implementations can provide custom repository
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { create } from "zustand";
|
|
9
|
-
import type { IGamificationRepository } from "../../domain/repositories/IGamificationRepository";
|
|
10
|
-
import { storageGamificationRepository } from "../repositories/StorageGamificationRepository";
|
|
11
|
-
import type { Achievement } from "../../domain/entities/Achievement";
|
|
12
|
-
import type { PointBalance, PointTransaction } from "../../domain/entities/Point";
|
|
13
|
-
import type { Level, LevelProgress } from "../../domain/entities/Level";
|
|
14
|
-
import type { Streak } from "../../domain/entities/Streak";
|
|
15
|
-
import type { Leaderboard, LeaderboardEntry, LeaderboardRanking } from "../../domain/entities/Leaderboard";
|
|
16
|
-
import type { Reward, RewardClaim } from "../../domain/entities/Reward";
|
|
17
|
-
import type { Progress, ProgressUpdate } from "../../domain/entities/Progress";
|
|
18
|
-
|
|
19
|
-
interface GamificationStore {
|
|
20
|
-
// Repository
|
|
21
|
-
repository: IGamificationRepository;
|
|
22
|
-
|
|
23
|
-
// State
|
|
24
|
-
userId: string | null;
|
|
25
|
-
achievements: Achievement[];
|
|
26
|
-
pointBalance: PointBalance | null;
|
|
27
|
-
pointTransactions: PointTransaction[];
|
|
28
|
-
level: Level | null;
|
|
29
|
-
streaks: Streak[];
|
|
30
|
-
rewards: Reward[];
|
|
31
|
-
progress: Progress[];
|
|
32
|
-
|
|
33
|
-
// Loading states
|
|
34
|
-
isLoading: boolean;
|
|
35
|
-
isInitialized: boolean;
|
|
36
|
-
|
|
37
|
-
// Actions - Initialization
|
|
38
|
-
setUserId: (userId: string) => void;
|
|
39
|
-
setRepository: (repository: IGamificationRepository) => void;
|
|
40
|
-
initialize: (userId: string) => Promise<void>;
|
|
41
|
-
|
|
42
|
-
// Actions - Achievements
|
|
43
|
-
loadAchievements: () => Promise<void>;
|
|
44
|
-
updateAchievementProgress: (achievementId: string, progress: number) => Promise<void>;
|
|
45
|
-
unlockAchievement: (achievementId: string) => Promise<void>;
|
|
46
|
-
|
|
47
|
-
// Actions - Points
|
|
48
|
-
loadPointBalance: () => Promise<void>;
|
|
49
|
-
loadPointTransactions: (limit?: number) => Promise<void>;
|
|
50
|
-
addPoints: (
|
|
51
|
-
amount: number,
|
|
52
|
-
source: string,
|
|
53
|
-
sourceId?: string,
|
|
54
|
-
category?: string,
|
|
55
|
-
description?: string,
|
|
56
|
-
) => Promise<void>;
|
|
57
|
-
deductPoints: (
|
|
58
|
-
amount: number,
|
|
59
|
-
source: string,
|
|
60
|
-
sourceId?: string,
|
|
61
|
-
description?: string,
|
|
62
|
-
) => Promise<void>;
|
|
63
|
-
|
|
64
|
-
// Actions - Levels
|
|
65
|
-
loadLevel: () => Promise<void>;
|
|
66
|
-
addExperience: (amount: number, source?: string) => Promise<void>;
|
|
67
|
-
|
|
68
|
-
// Actions - Streaks
|
|
69
|
-
loadStreaks: () => Promise<void>;
|
|
70
|
-
updateStreakActivity: (type: string, activityDate: string) => Promise<void>;
|
|
71
|
-
|
|
72
|
-
// Actions - Rewards
|
|
73
|
-
loadRewards: () => Promise<void>;
|
|
74
|
-
claimReward: (rewardId: string) => Promise<RewardClaim | null>;
|
|
75
|
-
|
|
76
|
-
// Actions - Progress
|
|
77
|
-
loadProgress: (metric?: string) => Promise<void>;
|
|
78
|
-
updateProgress: (update: ProgressUpdate) => Promise<void>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const useGamificationStore = create<GamificationStore>((set, get) => ({
|
|
82
|
-
// Repository
|
|
83
|
-
repository: storageGamificationRepository,
|
|
84
|
-
|
|
85
|
-
// State
|
|
86
|
-
userId: null,
|
|
87
|
-
achievements: [],
|
|
88
|
-
pointBalance: null,
|
|
89
|
-
pointTransactions: [],
|
|
90
|
-
level: null,
|
|
91
|
-
streaks: [],
|
|
92
|
-
rewards: [],
|
|
93
|
-
progress: [],
|
|
94
|
-
|
|
95
|
-
// Loading states
|
|
96
|
-
isLoading: false,
|
|
97
|
-
isInitialized: false,
|
|
98
|
-
|
|
99
|
-
// Actions - Initialization
|
|
100
|
-
setUserId: (userId: string) => {
|
|
101
|
-
set({ userId });
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
setRepository: (repository: IGamificationRepository) => {
|
|
105
|
-
set({ repository });
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
initialize: async (userId: string) => {
|
|
109
|
-
set({ isLoading: true, userId });
|
|
110
|
-
const store = get();
|
|
111
|
-
try {
|
|
112
|
-
await Promise.all([
|
|
113
|
-
store.loadAchievements(),
|
|
114
|
-
store.loadPointBalance(),
|
|
115
|
-
store.loadLevel(),
|
|
116
|
-
store.loadStreaks(),
|
|
117
|
-
store.loadRewards(),
|
|
118
|
-
store.loadProgress(),
|
|
119
|
-
]);
|
|
120
|
-
set({ isLoading: false, isInitialized: true });
|
|
121
|
-
} catch (error) {
|
|
122
|
-
/* eslint-disable-next-line no-console */
|
|
123
|
-
if (__DEV__) console.error("Failed to initialize gamification:", error);
|
|
124
|
-
set({ isLoading: false, isInitialized: true });
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
// Actions - Achievements
|
|
129
|
-
loadAchievements: async () => {
|
|
130
|
-
const { userId, repository } = get();
|
|
131
|
-
if (!userId) return;
|
|
132
|
-
set({ isLoading: true });
|
|
133
|
-
const result = await repository.loadAchievements(userId);
|
|
134
|
-
if (result.success) {
|
|
135
|
-
set({ achievements: result.data, isLoading: false });
|
|
136
|
-
} else {
|
|
137
|
-
/* eslint-disable-next-line no-console */
|
|
138
|
-
if (__DEV__) console.error("Failed to load achievements:", result.error);
|
|
139
|
-
set({ isLoading: false });
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
updateAchievementProgress: async (achievementId: string, progress: number) => {
|
|
144
|
-
const { userId, repository } = get();
|
|
145
|
-
if (!userId) return;
|
|
146
|
-
const result = await repository.updateAchievementProgress(userId, achievementId, progress);
|
|
147
|
-
if (result.success) {
|
|
148
|
-
const achievements = get().achievements.map((a) =>
|
|
149
|
-
a.id === achievementId ? result.data : a,
|
|
150
|
-
);
|
|
151
|
-
set({ achievements });
|
|
152
|
-
// Check if achievement should be unlocked
|
|
153
|
-
if (progress >= result.data.requirement && !result.data.unlocked) {
|
|
154
|
-
await get().unlockAchievement(achievementId);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
unlockAchievement: async (achievementId: string) => {
|
|
160
|
-
const { userId, repository } = get();
|
|
161
|
-
if (!userId) return;
|
|
162
|
-
const result = await repository.unlockAchievement(userId, achievementId);
|
|
163
|
-
if (result.success) {
|
|
164
|
-
const achievements = get().achievements.map((a) =>
|
|
165
|
-
a.id === achievementId ? result.data : a,
|
|
166
|
-
);
|
|
167
|
-
set({ achievements });
|
|
168
|
-
// Award points if achievement has points
|
|
169
|
-
if (result.data.points) {
|
|
170
|
-
await get().addPoints(
|
|
171
|
-
result.data.points,
|
|
172
|
-
"achievement",
|
|
173
|
-
achievementId,
|
|
174
|
-
"achievement",
|
|
175
|
-
`Unlocked achievement: ${result.data.title}`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
// Actions - Points
|
|
182
|
-
loadPointBalance: async () => {
|
|
183
|
-
const { userId, repository } = get();
|
|
184
|
-
if (!userId) return;
|
|
185
|
-
const result = await repository.loadPointBalance(userId);
|
|
186
|
-
if (result.success) {
|
|
187
|
-
set({ pointBalance: result.data });
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
loadPointTransactions: async (limit?: number) => {
|
|
192
|
-
const { userId, repository } = get();
|
|
193
|
-
if (!userId) return;
|
|
194
|
-
const result = await repository.loadPointTransactions(userId, limit);
|
|
195
|
-
if (result.success) {
|
|
196
|
-
set({ pointTransactions: result.data });
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
addPoints: async (
|
|
201
|
-
amount: number,
|
|
202
|
-
source: string,
|
|
203
|
-
sourceId?: string,
|
|
204
|
-
category?: string,
|
|
205
|
-
description?: string,
|
|
206
|
-
) => {
|
|
207
|
-
const { userId, repository } = get();
|
|
208
|
-
if (!userId) return;
|
|
209
|
-
const result = await repository.addPoints(userId, amount, source, sourceId, category, description);
|
|
210
|
-
if (result.success) {
|
|
211
|
-
await get().loadPointBalance();
|
|
212
|
-
await get().loadPointTransactions(10);
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
deductPoints: async (
|
|
217
|
-
amount: number,
|
|
218
|
-
source: string,
|
|
219
|
-
sourceId?: string,
|
|
220
|
-
description?: string,
|
|
221
|
-
) => {
|
|
222
|
-
const { userId, repository } = get();
|
|
223
|
-
if (!userId) return;
|
|
224
|
-
const result = await repository.deductPoints(userId, amount, source, sourceId, description);
|
|
225
|
-
if (result.success) {
|
|
226
|
-
await get().loadPointBalance();
|
|
227
|
-
await get().loadPointTransactions(10);
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
// Actions - Levels
|
|
232
|
-
loadLevel: async () => {
|
|
233
|
-
const { userId, repository } = get();
|
|
234
|
-
if (!userId) return;
|
|
235
|
-
const result = await repository.loadLevel(userId);
|
|
236
|
-
if (result.success) {
|
|
237
|
-
set({ level: result.data });
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
addExperience: async (amount: number, source?: string) => {
|
|
242
|
-
const { userId, repository } = get();
|
|
243
|
-
if (!userId) return;
|
|
244
|
-
const result = await repository.addExperience(userId, amount, source);
|
|
245
|
-
if (result.success) {
|
|
246
|
-
await get().loadLevel();
|
|
247
|
-
// Check for level up
|
|
248
|
-
if (result.data.canLevelUp) {
|
|
249
|
-
// Award points for level up (optional)
|
|
250
|
-
await get().addPoints(
|
|
251
|
-
result.data.currentLevel * 10,
|
|
252
|
-
"level_up",
|
|
253
|
-
undefined,
|
|
254
|
-
"level",
|
|
255
|
-
`Leveled up to level ${result.data.currentLevel}`,
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
|
|
261
|
-
// Actions - Streaks
|
|
262
|
-
loadStreaks: async () => {
|
|
263
|
-
const { userId, repository } = get();
|
|
264
|
-
if (!userId) return;
|
|
265
|
-
const result = await repository.loadStreaks(userId);
|
|
266
|
-
if (result.success) {
|
|
267
|
-
set({ streaks: result.data });
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
|
|
271
|
-
updateStreakActivity: async (type: string, activityDate: string) => {
|
|
272
|
-
const { userId, repository } = get();
|
|
273
|
-
if (!userId) return;
|
|
274
|
-
const result = await repository.updateStreakActivity(userId, type, activityDate);
|
|
275
|
-
if (result.success) {
|
|
276
|
-
const streaks = get().streaks.map((s) => (s.id === result.data.id ? result.data : s));
|
|
277
|
-
const existingIndex = streaks.findIndex((s) => s.id === result.data.id);
|
|
278
|
-
if (existingIndex === -1) {
|
|
279
|
-
streaks.push(result.data);
|
|
280
|
-
}
|
|
281
|
-
set({ streaks });
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
|
|
285
|
-
// Actions - Rewards
|
|
286
|
-
loadRewards: async () => {
|
|
287
|
-
const { userId, repository } = get();
|
|
288
|
-
if (!userId) return;
|
|
289
|
-
const result = await repository.loadRewards(userId);
|
|
290
|
-
if (result.success) {
|
|
291
|
-
set({ rewards: result.data });
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
claimReward: async (rewardId: string): Promise<RewardClaim | null> => {
|
|
296
|
-
const { userId, repository } = get();
|
|
297
|
-
if (!userId) return null;
|
|
298
|
-
const reward = get().rewards.find((r) => r.id === rewardId);
|
|
299
|
-
if (reward?.pointsCost) {
|
|
300
|
-
const balance = get().pointBalance;
|
|
301
|
-
if (!balance || balance.total < reward.pointsCost) {
|
|
302
|
-
return null;
|
|
303
|
-
}
|
|
304
|
-
await get().deductPoints(
|
|
305
|
-
reward.pointsCost,
|
|
306
|
-
"reward_claim",
|
|
307
|
-
rewardId,
|
|
308
|
-
`Claimed reward: ${reward.title}`,
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
const result = await repository.claimReward(userId, rewardId);
|
|
312
|
-
if (result.success) {
|
|
313
|
-
await get().loadRewards();
|
|
314
|
-
return result.data;
|
|
315
|
-
}
|
|
316
|
-
return null;
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
// Actions - Progress
|
|
320
|
-
loadProgress: async (metric?: string) => {
|
|
321
|
-
const { userId, repository } = get();
|
|
322
|
-
if (!userId) return;
|
|
323
|
-
const result = await repository.loadProgress(userId, metric);
|
|
324
|
-
if (result.success) {
|
|
325
|
-
if (metric) {
|
|
326
|
-
const existing = get().progress.filter((p) => p.metric !== metric);
|
|
327
|
-
set({ progress: [...existing, ...result.data] });
|
|
328
|
-
} else {
|
|
329
|
-
set({ progress: result.data });
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
|
|
334
|
-
updateProgress: async (update: ProgressUpdate) => {
|
|
335
|
-
const { userId, repository } = get();
|
|
336
|
-
if (!userId) return;
|
|
337
|
-
const result = await repository.updateProgress(update);
|
|
338
|
-
if (result.success) {
|
|
339
|
-
const progress = get().progress.map((p) => (p.id === result.data.id ? result.data : p));
|
|
340
|
-
const existingIndex = progress.findIndex((p) => p.id === result.data.id);
|
|
341
|
-
if (existingIndex === -1) {
|
|
342
|
-
progress.push(result.data);
|
|
343
|
-
}
|
|
344
|
-
set({ progress });
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
}));
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useAchievements Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for achievement operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { Achievement } from "../../domain/entities/Achievement";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for achievement operations
|
|
12
|
-
*/
|
|
13
|
-
export const useAchievements = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
achievements: store.achievements,
|
|
18
|
-
loadAchievements: store.loadAchievements,
|
|
19
|
-
updateAchievementProgress: store.updateAchievementProgress,
|
|
20
|
-
unlockAchievement: store.unlockAchievement,
|
|
21
|
-
getAchievementById: (id: string): Achievement | undefined => {
|
|
22
|
-
return store.achievements.find((a) => a.id === id);
|
|
23
|
-
},
|
|
24
|
-
getAchievementsByCategory: (category: string): Achievement[] => {
|
|
25
|
-
return store.achievements.filter((a) => a.category === category);
|
|
26
|
-
},
|
|
27
|
-
getUnlockedAchievements: (): Achievement[] => {
|
|
28
|
-
return store.achievements.filter((a) => a.unlocked);
|
|
29
|
-
},
|
|
30
|
-
getLockedAchievements: (): Achievement[] => {
|
|
31
|
-
return store.achievements.filter((a) => !a.unlocked);
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useGamification Hook
|
|
3
|
-
*
|
|
4
|
-
* Main hook for accessing gamification store
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useEffect } from "react";
|
|
8
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
9
|
-
import type { IGamificationRepository } from "../../domain/repositories/IGamificationRepository";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Main hook for gamification operations
|
|
13
|
-
*/
|
|
14
|
-
export const useGamification = () => {
|
|
15
|
-
const store = useGamificationStore();
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
// State
|
|
19
|
-
userId: store.userId,
|
|
20
|
-
achievements: store.achievements,
|
|
21
|
-
pointBalance: store.pointBalance,
|
|
22
|
-
pointTransactions: store.pointTransactions,
|
|
23
|
-
level: store.level,
|
|
24
|
-
streaks: store.streaks,
|
|
25
|
-
rewards: store.rewards,
|
|
26
|
-
progress: store.progress,
|
|
27
|
-
isLoading: store.isLoading,
|
|
28
|
-
isInitialized: store.isInitialized,
|
|
29
|
-
|
|
30
|
-
// Actions - Initialization
|
|
31
|
-
setUserId: store.setUserId,
|
|
32
|
-
setRepository: store.setRepository,
|
|
33
|
-
initialize: store.initialize,
|
|
34
|
-
|
|
35
|
-
// Actions - Achievements
|
|
36
|
-
loadAchievements: store.loadAchievements,
|
|
37
|
-
updateAchievementProgress: store.updateAchievementProgress,
|
|
38
|
-
unlockAchievement: store.unlockAchievement,
|
|
39
|
-
|
|
40
|
-
// Actions - Points
|
|
41
|
-
loadPointBalance: store.loadPointBalance,
|
|
42
|
-
loadPointTransactions: store.loadPointTransactions,
|
|
43
|
-
addPoints: store.addPoints,
|
|
44
|
-
deductPoints: store.deductPoints,
|
|
45
|
-
|
|
46
|
-
// Actions - Levels
|
|
47
|
-
loadLevel: store.loadLevel,
|
|
48
|
-
addExperience: store.addExperience,
|
|
49
|
-
|
|
50
|
-
// Actions - Streaks
|
|
51
|
-
loadStreaks: store.loadStreaks,
|
|
52
|
-
updateStreakActivity: store.updateStreakActivity,
|
|
53
|
-
|
|
54
|
-
// Actions - Rewards
|
|
55
|
-
loadRewards: store.loadRewards,
|
|
56
|
-
claimReward: store.claimReward,
|
|
57
|
-
|
|
58
|
-
// Actions - Progress
|
|
59
|
-
loadProgress: store.loadProgress,
|
|
60
|
-
updateProgress: store.updateProgress,
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Hook to initialize gamification on mount
|
|
66
|
-
*/
|
|
67
|
-
export const useGamificationInitializer = (userId: string | null) => {
|
|
68
|
-
const { initialize, isInitialized } = useGamificationStore();
|
|
69
|
-
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (userId && !isInitialized) {
|
|
72
|
-
initialize(userId);
|
|
73
|
-
}
|
|
74
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
-
}, [userId, isInitialized]);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Hook to set custom repository
|
|
80
|
-
*/
|
|
81
|
-
export const useGamificationRepository = (repository: IGamificationRepository | null) => {
|
|
82
|
-
const { setRepository } = useGamificationStore();
|
|
83
|
-
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
if (repository) {
|
|
86
|
-
setRepository(repository);
|
|
87
|
-
}
|
|
88
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
89
|
-
}, [repository]);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useLevel Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for level operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { Level, LevelProgress } from "../../domain/entities/Level";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for level operations
|
|
12
|
-
*/
|
|
13
|
-
export const useLevel = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
level: store.level,
|
|
18
|
-
loadLevel: store.loadLevel,
|
|
19
|
-
addExperience: store.addExperience,
|
|
20
|
-
getCurrentLevel: (): number => {
|
|
21
|
-
return store.level?.currentLevel || 1;
|
|
22
|
-
},
|
|
23
|
-
getCurrentExperience: (): number => {
|
|
24
|
-
return store.level?.currentExperience || 0;
|
|
25
|
-
},
|
|
26
|
-
getTotalExperience: (): number => {
|
|
27
|
-
return store.level?.totalExperience || 0;
|
|
28
|
-
},
|
|
29
|
-
getExperienceToNextLevel: (): number => {
|
|
30
|
-
return store.level?.experienceToNextLevel || 100;
|
|
31
|
-
},
|
|
32
|
-
getLevelProgress: (): number => {
|
|
33
|
-
return store.level?.levelProgress || 0;
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePoints Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for points operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { PointBalance, PointTransaction } from "../../domain/entities/Point";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for points operations
|
|
12
|
-
*/
|
|
13
|
-
export const usePoints = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
pointBalance: store.pointBalance,
|
|
18
|
-
pointTransactions: store.pointTransactions,
|
|
19
|
-
loadPointBalance: store.loadPointBalance,
|
|
20
|
-
loadPointTransactions: store.loadPointTransactions,
|
|
21
|
-
addPoints: store.addPoints,
|
|
22
|
-
deductPoints: store.deductPoints,
|
|
23
|
-
getTotalPoints: (): number => {
|
|
24
|
-
return store.pointBalance?.total || 0;
|
|
25
|
-
},
|
|
26
|
-
getPointsByCategory: (category: string): number => {
|
|
27
|
-
return store.pointBalance?.byCategory[category] || 0;
|
|
28
|
-
},
|
|
29
|
-
getRecentTransactions: (limit: number = 10): PointTransaction[] => {
|
|
30
|
-
return store.pointTransactions.slice(0, limit);
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useProgress Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for progress tracking operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { Progress } from "../../domain/entities/Progress";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for progress tracking operations
|
|
12
|
-
*/
|
|
13
|
-
export const useProgress = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
progress: store.progress,
|
|
18
|
-
loadProgress: store.loadProgress,
|
|
19
|
-
updateProgress: store.updateProgress,
|
|
20
|
-
getProgressByMetric: (metric: string): Progress | undefined => {
|
|
21
|
-
return store.progress.find((p) => p.metric === metric);
|
|
22
|
-
},
|
|
23
|
-
getProgressByCategory: (category: string): Progress[] => {
|
|
24
|
-
return store.progress.filter((p) => p.category === category);
|
|
25
|
-
},
|
|
26
|
-
getProgressByPeriod: (period: "daily" | "weekly" | "monthly" | "all-time"): Progress[] => {
|
|
27
|
-
return store.progress.filter((p) => p.period === period);
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useRewards Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for reward operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { Reward } from "../../domain/entities/Reward";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for reward operations
|
|
12
|
-
*/
|
|
13
|
-
export const useRewards = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
rewards: store.rewards,
|
|
18
|
-
loadRewards: store.loadRewards,
|
|
19
|
-
claimReward: store.claimReward,
|
|
20
|
-
getRewardById: (id: string): Reward | undefined => {
|
|
21
|
-
return store.rewards.find((r) => r.id === id);
|
|
22
|
-
},
|
|
23
|
-
getUnlockedRewards: (): Reward[] => {
|
|
24
|
-
return store.rewards.filter((r) => r.unlocked && !r.claimed);
|
|
25
|
-
},
|
|
26
|
-
getClaimedRewards: (): Reward[] => {
|
|
27
|
-
return store.rewards.filter((r) => r.claimed);
|
|
28
|
-
},
|
|
29
|
-
getAvailableRewards: (): Reward[] => {
|
|
30
|
-
const balance = store.pointBalance?.total || 0;
|
|
31
|
-
const level = store.level?.currentLevel || 1;
|
|
32
|
-
return store.rewards.filter(
|
|
33
|
-
(r) =>
|
|
34
|
-
!r.claimed &&
|
|
35
|
-
(!r.pointsCost || r.pointsCost <= balance) &&
|
|
36
|
-
(!r.levelRequired || r.levelRequired <= level),
|
|
37
|
-
);
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useStreaks Hook
|
|
3
|
-
*
|
|
4
|
-
* Hook for streak operations
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { useGamificationStore } from "../../infrastructure/storage/GamificationStore";
|
|
8
|
-
import type { Streak } from "../../domain/entities/Streak";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Hook for streak operations
|
|
12
|
-
*/
|
|
13
|
-
export const useStreaks = () => {
|
|
14
|
-
const store = useGamificationStore();
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
streaks: store.streaks,
|
|
18
|
-
loadStreaks: store.loadStreaks,
|
|
19
|
-
updateStreakActivity: store.updateStreakActivity,
|
|
20
|
-
getStreakByType: (type: string): Streak | undefined => {
|
|
21
|
-
return store.streaks.find((s) => s.type === type);
|
|
22
|
-
},
|
|
23
|
-
getActiveStreaks: (): Streak[] => {
|
|
24
|
-
return store.streaks.filter((s) => s.isActive);
|
|
25
|
-
},
|
|
26
|
-
getLongestStreak: (type?: string): number => {
|
|
27
|
-
const streaks = type
|
|
28
|
-
? store.streaks.filter((s) => s.type === type)
|
|
29
|
-
: store.streaks;
|
|
30
|
-
return Math.max(...streaks.map((s) => s.longestStreak), 0);
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|