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
- achievedAchievements: string[];
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 [achievedAchievements, setAchievedAchievements] = React.useState(() => {
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) && !achievedAchievements.includes(condition.data.id)) {
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 updatedAchievements = [...achievedAchievements, ...newAchievements.map(a => a.id)];
226
- setAchievedAchievements(updatedAchievements);
227
- localStorage.setItem(`${storageKey}-achievements`, JSON.stringify(updatedAchievements));
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, achievedAchievements, storageKey]);
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
- setCurrentAchievement(achievementQueue[0]);
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
- achievedAchievements,
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(achievedAchievements), onClose: () => setShowBadges(false) }),
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 [achievedAchievements, setAchievedAchievements] = useState(() => {
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) && !achievedAchievements.includes(condition.data.id)) {
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 updatedAchievements = [...achievedAchievements, ...newAchievements.map(a => a.id)];
224
- setAchievedAchievements(updatedAchievements);
225
- localStorage.setItem(`${storageKey}-achievements`, JSON.stringify(updatedAchievements));
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, achievedAchievements, storageKey]);
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
- setCurrentAchievement(achievementQueue[0]);
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
- achievedAchievements,
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(achievedAchievements), onClose: () => setShowBadges(false) }),
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
  };
@@ -0,0 +1,6 @@
1
+ import EventEmitter from 'eventemitter3';
2
+ interface EventTypes {
3
+ checkAchievements: (payload: Record<string, any>) => void;
4
+ }
5
+ declare const reactAchievementsEventEmitter: EventEmitter<EventTypes, any>;
6
+ export { reactAchievementsEventEmitter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-achievements",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "This package allows users to transpose a React achievements engine over their React apps",
5
5
  "keywords": [
6
6
  "react",
@@ -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
- achievedAchievements: string[];
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 [achievedAchievements, setAchievedAchievements] = useState<string[]>(() => {
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) && !achievedAchievements.includes(condition.data.id)) {
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 updatedAchievements = [...achievedAchievements, ...newAchievements.map(a => a.id)];
76
- setAchievedAchievements(updatedAchievements);
77
- localStorage.setItem(`${storageKey}-achievements`, JSON.stringify(updatedAchievements));
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, achievedAchievements, storageKey]);
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
- setCurrentAchievement(achievementQueue[0]);
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
- achievedAchievements,
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(achievedAchievements)}
157
+ achievements={getAchievements(unlockedAchievements)}
134
158
  onClose={() => setShowBadges(false)}
135
159
  />
136
160
  <BadgesButton onClick={showBadgesModal} position={badgesButtonPosition} />