react-achievements 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.
|
@@ -3,7 +3,7 @@ import { Metrics, AchievementConfig, MetricValue } from '../types';
|
|
|
3
3
|
interface AchievementContextProps {
|
|
4
4
|
metrics: Metrics;
|
|
5
5
|
setMetrics: (metrics: Metrics | ((prevMetrics: Metrics) => Metrics)) => void;
|
|
6
|
-
|
|
6
|
+
unlockedAchievements: string[];
|
|
7
7
|
checkAchievements: () => void;
|
|
8
8
|
showBadgesModal: () => void;
|
|
9
9
|
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -203,8 +203,12 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
203
203
|
}
|
|
204
204
|
return extractMetrics(initialState);
|
|
205
205
|
});
|
|
206
|
-
const [
|
|
207
|
-
const saved = localStorage.getItem(`${storageKey}-achievements`);
|
|
206
|
+
const [unlockedAchievements, setUnlockedAchievements] = React.useState(() => {
|
|
207
|
+
const saved = localStorage.getItem(`${storageKey}-unlocked-achievements`);
|
|
208
|
+
return saved ? JSON.parse(saved) : [];
|
|
209
|
+
});
|
|
210
|
+
const [newlyUnlockedAchievements, setNewlyUnlockedAchievements] = React.useState(() => {
|
|
211
|
+
const saved = localStorage.getItem(`${storageKey}-newly-unlocked-achievements`);
|
|
208
212
|
return saved ? JSON.parse(saved) : [];
|
|
209
213
|
});
|
|
210
214
|
const [achievementQueue, setAchievementQueue] = React.useState([]);
|
|
@@ -216,28 +220,36 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
216
220
|
Object.entries(config).forEach(([metricKey, conditions]) => {
|
|
217
221
|
const metricValue = metrics[metricKey];
|
|
218
222
|
conditions.forEach(condition => {
|
|
219
|
-
if (condition.check(metricValue) && !
|
|
223
|
+
if (condition.check(metricValue) && !unlockedAchievements.includes(condition.data.id)) {
|
|
220
224
|
newAchievements.push(condition.data);
|
|
221
225
|
}
|
|
222
226
|
});
|
|
223
227
|
});
|
|
224
228
|
if (newAchievements.length > 0) {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
const newlyUnlockedIds = newAchievements.map(a => a.id);
|
|
230
|
+
setUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
231
|
+
setNewlyUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
232
|
+
localStorage.setItem(`${storageKey}-unlocked-achievements`, JSON.stringify([...unlockedAchievements, ...newlyUnlockedIds]));
|
|
233
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify([...newlyUnlockedAchievements, ...newlyUnlockedIds]));
|
|
228
234
|
setAchievementQueue(prevQueue => [...prevQueue, ...newAchievements]);
|
|
229
235
|
setShowConfetti(true);
|
|
230
236
|
}
|
|
231
|
-
}, [config, metrics,
|
|
237
|
+
}, [config, metrics, unlockedAchievements, newlyUnlockedAchievements, storageKey]);
|
|
232
238
|
React.useEffect(() => {
|
|
233
239
|
checkAchievements();
|
|
234
240
|
}, [metrics, checkAchievements]);
|
|
235
241
|
React.useEffect(() => {
|
|
236
242
|
if (achievementQueue.length > 0 && !currentAchievement) {
|
|
237
|
-
|
|
243
|
+
const nextAchievement = achievementQueue[0];
|
|
244
|
+
setCurrentAchievement(nextAchievement);
|
|
238
245
|
setAchievementQueue(prevQueue => prevQueue.slice(1));
|
|
246
|
+
setNewlyUnlockedAchievements(prev => {
|
|
247
|
+
const updated = prev.filter(id => id !== nextAchievement.id);
|
|
248
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify(updated));
|
|
249
|
+
return updated;
|
|
250
|
+
});
|
|
239
251
|
}
|
|
240
|
-
}, [achievementQueue, currentAchievement]);
|
|
252
|
+
}, [achievementQueue, currentAchievement, storageKey]);
|
|
241
253
|
const showBadgesModal = () => {
|
|
242
254
|
setShowBadges(true);
|
|
243
255
|
};
|
|
@@ -253,19 +265,26 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
253
265
|
return updatedMetrics;
|
|
254
266
|
});
|
|
255
267
|
},
|
|
256
|
-
|
|
268
|
+
unlockedAchievements,
|
|
257
269
|
checkAchievements,
|
|
258
270
|
showBadgesModal
|
|
259
271
|
};
|
|
272
|
+
React.useEffect(() => {
|
|
273
|
+
if (newlyUnlockedAchievements.length > 0) {
|
|
274
|
+
const achievementsToShow = getAchievements(newlyUnlockedAchievements);
|
|
275
|
+
setAchievementQueue(achievementsToShow);
|
|
276
|
+
setShowConfetti(true);
|
|
277
|
+
}
|
|
278
|
+
}, []); // Run this effect only on component mount
|
|
260
279
|
return (React.createElement(AchievementContext.Provider, { value: contextValue },
|
|
261
280
|
children,
|
|
262
281
|
React.createElement(AchievementModal$1, { show: !!currentAchievement, achievement: currentAchievement, onClose: () => {
|
|
263
282
|
setCurrentAchievement(null);
|
|
264
|
-
if (achievementQueue.length === 0) {
|
|
283
|
+
if (achievementQueue.length === 0 && newlyUnlockedAchievements.length === 0) {
|
|
265
284
|
setShowConfetti(false);
|
|
266
285
|
}
|
|
267
286
|
} }),
|
|
268
|
-
React.createElement(BadgesModal$1, { show: showBadges, achievements: getAchievements(
|
|
287
|
+
React.createElement(BadgesModal$1, { show: showBadges, achievements: getAchievements(unlockedAchievements), onClose: () => setShowBadges(false) }),
|
|
269
288
|
React.createElement(BadgesButton$1, { onClick: showBadgesModal, position: badgesButtonPosition }),
|
|
270
289
|
React.createElement(ConfettiWrapper, { show: showConfetti || achievementQueue.length > 0 })));
|
|
271
290
|
};
|
package/dist/index.esm.js
CHANGED
|
@@ -201,8 +201,12 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
201
201
|
}
|
|
202
202
|
return extractMetrics(initialState);
|
|
203
203
|
});
|
|
204
|
-
const [
|
|
205
|
-
const saved = localStorage.getItem(`${storageKey}-achievements`);
|
|
204
|
+
const [unlockedAchievements, setUnlockedAchievements] = useState(() => {
|
|
205
|
+
const saved = localStorage.getItem(`${storageKey}-unlocked-achievements`);
|
|
206
|
+
return saved ? JSON.parse(saved) : [];
|
|
207
|
+
});
|
|
208
|
+
const [newlyUnlockedAchievements, setNewlyUnlockedAchievements] = useState(() => {
|
|
209
|
+
const saved = localStorage.getItem(`${storageKey}-newly-unlocked-achievements`);
|
|
206
210
|
return saved ? JSON.parse(saved) : [];
|
|
207
211
|
});
|
|
208
212
|
const [achievementQueue, setAchievementQueue] = useState([]);
|
|
@@ -214,28 +218,36 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
214
218
|
Object.entries(config).forEach(([metricKey, conditions]) => {
|
|
215
219
|
const metricValue = metrics[metricKey];
|
|
216
220
|
conditions.forEach(condition => {
|
|
217
|
-
if (condition.check(metricValue) && !
|
|
221
|
+
if (condition.check(metricValue) && !unlockedAchievements.includes(condition.data.id)) {
|
|
218
222
|
newAchievements.push(condition.data);
|
|
219
223
|
}
|
|
220
224
|
});
|
|
221
225
|
});
|
|
222
226
|
if (newAchievements.length > 0) {
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
const newlyUnlockedIds = newAchievements.map(a => a.id);
|
|
228
|
+
setUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
229
|
+
setNewlyUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
230
|
+
localStorage.setItem(`${storageKey}-unlocked-achievements`, JSON.stringify([...unlockedAchievements, ...newlyUnlockedIds]));
|
|
231
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify([...newlyUnlockedAchievements, ...newlyUnlockedIds]));
|
|
226
232
|
setAchievementQueue(prevQueue => [...prevQueue, ...newAchievements]);
|
|
227
233
|
setShowConfetti(true);
|
|
228
234
|
}
|
|
229
|
-
}, [config, metrics,
|
|
235
|
+
}, [config, metrics, unlockedAchievements, newlyUnlockedAchievements, storageKey]);
|
|
230
236
|
useEffect(() => {
|
|
231
237
|
checkAchievements();
|
|
232
238
|
}, [metrics, checkAchievements]);
|
|
233
239
|
useEffect(() => {
|
|
234
240
|
if (achievementQueue.length > 0 && !currentAchievement) {
|
|
235
|
-
|
|
241
|
+
const nextAchievement = achievementQueue[0];
|
|
242
|
+
setCurrentAchievement(nextAchievement);
|
|
236
243
|
setAchievementQueue(prevQueue => prevQueue.slice(1));
|
|
244
|
+
setNewlyUnlockedAchievements(prev => {
|
|
245
|
+
const updated = prev.filter(id => id !== nextAchievement.id);
|
|
246
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify(updated));
|
|
247
|
+
return updated;
|
|
248
|
+
});
|
|
237
249
|
}
|
|
238
|
-
}, [achievementQueue, currentAchievement]);
|
|
250
|
+
}, [achievementQueue, currentAchievement, storageKey]);
|
|
239
251
|
const showBadgesModal = () => {
|
|
240
252
|
setShowBadges(true);
|
|
241
253
|
};
|
|
@@ -251,19 +263,26 @@ const AchievementProvider = ({ children, config, initialState = {}, storageKey =
|
|
|
251
263
|
return updatedMetrics;
|
|
252
264
|
});
|
|
253
265
|
},
|
|
254
|
-
|
|
266
|
+
unlockedAchievements,
|
|
255
267
|
checkAchievements,
|
|
256
268
|
showBadgesModal
|
|
257
269
|
};
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (newlyUnlockedAchievements.length > 0) {
|
|
272
|
+
const achievementsToShow = getAchievements(newlyUnlockedAchievements);
|
|
273
|
+
setAchievementQueue(achievementsToShow);
|
|
274
|
+
setShowConfetti(true);
|
|
275
|
+
}
|
|
276
|
+
}, []); // Run this effect only on component mount
|
|
258
277
|
return (React.createElement(AchievementContext.Provider, { value: contextValue },
|
|
259
278
|
children,
|
|
260
279
|
React.createElement(AchievementModal$1, { show: !!currentAchievement, achievement: currentAchievement, onClose: () => {
|
|
261
280
|
setCurrentAchievement(null);
|
|
262
|
-
if (achievementQueue.length === 0) {
|
|
281
|
+
if (achievementQueue.length === 0 && newlyUnlockedAchievements.length === 0) {
|
|
263
282
|
setShowConfetti(false);
|
|
264
283
|
}
|
|
265
284
|
} }),
|
|
266
|
-
React.createElement(BadgesModal$1, { show: showBadges, achievements: getAchievements(
|
|
285
|
+
React.createElement(BadgesModal$1, { show: showBadges, achievements: getAchievements(unlockedAchievements), onClose: () => setShowBadges(false) }),
|
|
267
286
|
React.createElement(BadgesButton$1, { onClick: showBadgesModal, position: badgesButtonPosition }),
|
|
268
287
|
React.createElement(ConfettiWrapper, { show: showConfetti || achievementQueue.length > 0 })));
|
|
269
288
|
};
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@ import ConfettiWrapper from '../components/ConfettiWrapper';
|
|
|
8
8
|
interface AchievementContextProps {
|
|
9
9
|
metrics: Metrics;
|
|
10
10
|
setMetrics: (metrics: Metrics | ((prevMetrics: Metrics) => Metrics)) => void;
|
|
11
|
-
|
|
11
|
+
unlockedAchievements: string[];
|
|
12
12
|
checkAchievements: () => void;
|
|
13
13
|
showBadgesModal: () => void;
|
|
14
14
|
}
|
|
@@ -49,8 +49,13 @@ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
|
|
|
49
49
|
return extractMetrics(initialState);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
const [
|
|
53
|
-
const saved = localStorage.getItem(`${storageKey}-achievements`);
|
|
52
|
+
const [unlockedAchievements, setUnlockedAchievements] = useState<string[]>(() => {
|
|
53
|
+
const saved = localStorage.getItem(`${storageKey}-unlocked-achievements`);
|
|
54
|
+
return saved ? JSON.parse(saved) : [];
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const [newlyUnlockedAchievements, setNewlyUnlockedAchievements] = useState<string[]>(() => {
|
|
58
|
+
const saved = localStorage.getItem(`${storageKey}-newly-unlocked-achievements`);
|
|
54
59
|
return saved ? JSON.parse(saved) : [];
|
|
55
60
|
});
|
|
56
61
|
|
|
@@ -65,20 +70,24 @@ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
|
|
|
65
70
|
Object.entries(config).forEach(([metricKey, conditions]) => {
|
|
66
71
|
const metricValue = metrics[metricKey];
|
|
67
72
|
conditions.forEach(condition => {
|
|
68
|
-
if (condition.check(metricValue) && !
|
|
73
|
+
if (condition.check(metricValue) && !unlockedAchievements.includes(condition.data.id)) {
|
|
69
74
|
newAchievements.push(condition.data);
|
|
70
75
|
}
|
|
71
76
|
});
|
|
72
77
|
});
|
|
73
78
|
|
|
74
79
|
if (newAchievements.length > 0) {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
const newlyUnlockedIds = newAchievements.map(a => a.id);
|
|
81
|
+
setUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
82
|
+
setNewlyUnlockedAchievements(prev => [...prev, ...newlyUnlockedIds]);
|
|
83
|
+
|
|
84
|
+
localStorage.setItem(`${storageKey}-unlocked-achievements`, JSON.stringify([...unlockedAchievements, ...newlyUnlockedIds]));
|
|
85
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify([...newlyUnlockedAchievements, ...newlyUnlockedIds]));
|
|
86
|
+
|
|
78
87
|
setAchievementQueue(prevQueue => [...prevQueue, ...newAchievements]);
|
|
79
88
|
setShowConfetti(true);
|
|
80
89
|
}
|
|
81
|
-
}, [config, metrics,
|
|
90
|
+
}, [config, metrics, unlockedAchievements, newlyUnlockedAchievements, storageKey]);
|
|
82
91
|
|
|
83
92
|
useEffect(() => {
|
|
84
93
|
checkAchievements();
|
|
@@ -86,10 +95,17 @@ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
|
|
|
86
95
|
|
|
87
96
|
useEffect(() => {
|
|
88
97
|
if (achievementQueue.length > 0 && !currentAchievement) {
|
|
89
|
-
|
|
98
|
+
const nextAchievement = achievementQueue[0];
|
|
99
|
+
setCurrentAchievement(nextAchievement);
|
|
90
100
|
setAchievementQueue(prevQueue => prevQueue.slice(1));
|
|
101
|
+
|
|
102
|
+
setNewlyUnlockedAchievements(prev => {
|
|
103
|
+
const updated = prev.filter(id => id !== nextAchievement.id);
|
|
104
|
+
localStorage.setItem(`${storageKey}-newly-unlocked-achievements`, JSON.stringify(updated));
|
|
105
|
+
return updated;
|
|
106
|
+
});
|
|
91
107
|
}
|
|
92
|
-
}, [achievementQueue, currentAchievement]);
|
|
108
|
+
}, [achievementQueue, currentAchievement, storageKey]);
|
|
93
109
|
|
|
94
110
|
const showBadgesModal = () => {
|
|
95
111
|
setShowBadges(true);
|
|
@@ -110,11 +126,19 @@ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
|
|
|
110
126
|
return updatedMetrics;
|
|
111
127
|
});
|
|
112
128
|
},
|
|
113
|
-
|
|
129
|
+
unlockedAchievements,
|
|
114
130
|
checkAchievements,
|
|
115
131
|
showBadgesModal
|
|
116
132
|
};
|
|
117
133
|
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (newlyUnlockedAchievements.length > 0) {
|
|
136
|
+
const achievementsToShow = getAchievements(newlyUnlockedAchievements);
|
|
137
|
+
setAchievementQueue(achievementsToShow);
|
|
138
|
+
setShowConfetti(true);
|
|
139
|
+
}
|
|
140
|
+
}, []); // Run this effect only on component mount
|
|
141
|
+
|
|
118
142
|
return (
|
|
119
143
|
<AchievementContext.Provider value={contextValue}>
|
|
120
144
|
{children}
|
|
@@ -123,14 +147,14 @@ export const AchievementProvider: React.FC<AchievementProviderProps> = ({
|
|
|
123
147
|
achievement={currentAchievement}
|
|
124
148
|
onClose={() => {
|
|
125
149
|
setCurrentAchievement(null);
|
|
126
|
-
if (achievementQueue.length === 0) {
|
|
150
|
+
if (achievementQueue.length === 0 && newlyUnlockedAchievements.length === 0) {
|
|
127
151
|
setShowConfetti(false);
|
|
128
152
|
}
|
|
129
153
|
}}
|
|
130
154
|
/>
|
|
131
155
|
<BadgesModal
|
|
132
156
|
show={showBadges}
|
|
133
|
-
achievements={getAchievements(
|
|
157
|
+
achievements={getAchievements(unlockedAchievements)}
|
|
134
158
|
onClose={() => setShowBadges(false)}
|
|
135
159
|
/>
|
|
136
160
|
<BadgesButton onClick={showBadgesModal} position={badgesButtonPosition} />
|