react-achievements 3.9.1 → 4.0.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/README.md +148 -101
- package/dist/headless.cjs +317 -0
- package/dist/headless.cjs.map +1 -0
- package/dist/headless.d.ts +176 -0
- package/dist/headless.esm.js +222 -0
- package/dist/headless.esm.js.map +1 -0
- package/dist/index.cjs +839 -881
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +163 -153
- package/dist/index.esm.js +835 -883
- package/dist/index.esm.js.map +1 -1
- package/dist/web.cjs +1416 -0
- package/dist/web.cjs.map +1 -0
- package/dist/web.d.ts +534 -0
- package/dist/web.esm.js +1306 -0
- package/dist/web.esm.js.map +1 -0
- package/package.json +13 -28
- package/dist/types/__mocks__/confetti-wrapper.d.ts +0 -5
- package/dist/types/__mocks__/react-confetti.d.ts +0 -3
- package/dist/types/__mocks__/react-toastify.d.ts +0 -13
- package/dist/types/core/components/BadgesButton.d.ts +0 -25
- package/dist/types/core/components/BadgesButtonWithModal.d.ts +0 -53
- package/dist/types/core/components/BadgesModal.d.ts +0 -14
- package/dist/types/core/components/ConfettiWrapper.d.ts +0 -6
- package/dist/types/core/errors/AchievementErrors.d.ts +0 -55
- package/dist/types/core/hooks/useWindowSize.d.ts +0 -16
- package/dist/types/core/icons/defaultIcons.d.ts +0 -8
- package/dist/types/core/storage/AsyncStorageAdapter.d.ts +0 -48
- package/dist/types/core/storage/IndexedDBStorage.d.ts +0 -29
- package/dist/types/core/storage/LocalStorage.d.ts +0 -16
- package/dist/types/core/storage/MemoryStorage.d.ts +0 -11
- package/dist/types/core/storage/OfflineQueueStorage.d.ts +0 -42
- package/dist/types/core/storage/RestApiStorage.d.ts +0 -20
- package/dist/types/core/styles/defaultStyles.d.ts +0 -2
- package/dist/types/core/types.d.ts +0 -115
- package/dist/types/core/ui/BuiltInConfetti.d.ts +0 -7
- package/dist/types/core/ui/BuiltInModal.d.ts +0 -7
- package/dist/types/core/ui/BuiltInNotification.d.ts +0 -7
- package/dist/types/core/ui/LegacyWrappers.d.ts +0 -21
- package/dist/types/core/ui/interfaces.d.ts +0 -127
- package/dist/types/core/ui/legacyDetector.d.ts +0 -40
- package/dist/types/core/ui/themes.d.ts +0 -14
- package/dist/types/core/utils/configNormalizer.d.ts +0 -3
- package/dist/types/core/utils/dataExport.d.ts +0 -34
- package/dist/types/core/utils/dataImport.d.ts +0 -50
- package/dist/types/hooks/useAchievementEngine.d.ts +0 -36
- package/dist/types/hooks/useAchievements.d.ts +0 -1
- package/dist/types/hooks/useSimpleAchievements.d.ts +0 -63
- package/dist/types/index.d.ts +0 -36
- package/dist/types/providers/AchievementProvider.d.ts +0 -47
- package/dist/types/setupTests.d.ts +0 -1
- package/dist/types/utils/achievementHelpers.d.ts +0 -135
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# React Achievements
|
|
2
2
|
|
|
3
|
-
**Add gamification to your React app in
|
|
3
|
+
**Add gamification to your React app in minutes** - Track progress, unlock achievements, show badges, and celebrate milestones.
|
|
4
4
|
|
|
5
|
-
[
|
|
6
|
-
|
|
7
|
-
[📚 Documentation](https://dave-b-b.github.io/react-achievements/) | [🎮 Interactive Demo](https://dave-b-b.github.io/react-achievements/?path=/story/introduction--page) | [📦 npm Package](https://www.npmjs.com/package/react-achievements)
|
|
5
|
+
[📚 Documentation](https://dave-b-b.github.io/react-achievements/) | [📦 npm Package](https://www.npmjs.com/package/react-achievements)
|
|
8
6
|
|
|
9
7
|
[](https://www.npmjs.com/package/react-achievements) [-blue.svg)](./LICENSE) [](https://www.typescriptlang.org/)
|
|
10
8
|
|
|
@@ -14,170 +12,219 @@
|
|
|
14
12
|
npm install react-achievements
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
**Requirements:** React
|
|
18
|
-
|
|
19
|
-
---
|
|
15
|
+
**Requirements:** React 16.8+, Node.js 16+
|
|
20
16
|
|
|
21
|
-
## Start Here
|
|
17
|
+
## Start Here
|
|
22
18
|
|
|
23
19
|
```tsx
|
|
24
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
AchievementProvider,
|
|
22
|
+
AchievementsWidget,
|
|
23
|
+
useSimpleAchievements,
|
|
24
|
+
} from 'react-achievements';
|
|
25
25
|
|
|
26
26
|
const achievements = {
|
|
27
27
|
score: {
|
|
28
28
|
100: { title: 'Century!', description: 'Score 100 points', icon: '🏆' },
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
function
|
|
33
|
-
const { track
|
|
32
|
+
function Game() {
|
|
33
|
+
const { track } = useSimpleAchievements();
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</div>
|
|
36
|
+
<button onClick={() => track('score', 100)}>
|
|
37
|
+
Score 100
|
|
38
|
+
</button>
|
|
40
39
|
);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export default function App() {
|
|
44
43
|
return (
|
|
45
|
-
<AchievementProvider achievements={achievements}
|
|
46
|
-
<
|
|
44
|
+
<AchievementProvider achievements={achievements}>
|
|
45
|
+
<Game />
|
|
46
|
+
<AchievementsWidget />
|
|
47
47
|
</AchievementProvider>
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
`AchievementsWidget` reads from context, shows the unlocked count, and opens a modal with locked and unlocked achievements. Use `placement="inline"` to put it in a drawer, sidebar, or navigation area. For an exact visual match, pass `renderTrigger` and use your app's own drawer row, nav item, or profile menu button while the widget still controls the modal.
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
```tsx
|
|
55
|
+
<AchievementsWidget
|
|
56
|
+
placement="inline"
|
|
57
|
+
renderTrigger={({ buttonProps, unlockedCount, totalCount }) => (
|
|
58
|
+
<button {...buttonProps} className="drawer-row">
|
|
59
|
+
Achievements
|
|
60
|
+
<span>{unlockedCount}/{totalCount}</span>
|
|
61
|
+
</button>
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
57
65
|
|
|
58
|
-
|
|
66
|
+
## Common Placements
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
Use the same context-aware UI in whichever surface already fits your app:
|
|
61
69
|
|
|
62
70
|
```tsx
|
|
63
|
-
|
|
64
|
-
import {
|
|
71
|
+
import { useState } from 'react';
|
|
72
|
+
import {
|
|
73
|
+
AchievementsList,
|
|
74
|
+
AchievementsModal,
|
|
75
|
+
AchievementsWidget,
|
|
76
|
+
} from 'react-achievements';
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
100: { title: 'Century!', description: 'Score 100 points', icon: '🏆' },
|
|
69
|
-
}
|
|
70
|
-
};
|
|
78
|
+
// Floating launcher in a corner
|
|
79
|
+
<AchievementsWidget position="bottom-right" />
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
81
|
+
// Inline nav, drawer, sidebar, or profile menu item
|
|
82
|
+
<AchievementsWidget placement="inline" label="Badges" />
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
eventMapping,
|
|
79
|
-
storage: 'local'
|
|
80
|
-
});
|
|
81
|
-
```
|
|
84
|
+
// Existing button or drawer row that opens the built-in modal
|
|
85
|
+
const [open, setOpen] = useState(false);
|
|
82
86
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
import { AchievementProvider } from 'react-achievements';
|
|
86
|
-
import { engine } from './achievementEngine';
|
|
87
|
+
<button onClick={() => setOpen(true)}>View achievements</button>
|
|
88
|
+
<AchievementsModal isOpen={open} onClose={() => setOpen(false)} />
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<AchievementProvider engine={engine} useBuiltInUI={true}>
|
|
91
|
-
<Game />
|
|
92
|
-
</AchievementProvider>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
90
|
+
// Inline achievements page, panel, drawer, or settings section
|
|
91
|
+
<AchievementsList showLocked />
|
|
95
92
|
```
|
|
96
93
|
|
|
97
|
-
|
|
98
|
-
// Game.tsx
|
|
99
|
-
import { useAchievementEngine } from 'react-achievements';
|
|
94
|
+
Storybook includes examples for floating buttons, nav buttons, drawer rows, existing controls that open a modal, dashboard cards, profile menus, and inline lists.
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
96
|
+
Provider-level icons and UI options are shared across notifications, widgets, modals, and lists:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<AchievementProvider
|
|
100
|
+
achievements={achievements}
|
|
101
|
+
icons={{ login: '🔑', streak: '🔥' }}
|
|
102
|
+
ui={{
|
|
103
|
+
theme: 'minimal',
|
|
104
|
+
NotificationComponent: MyNotification,
|
|
105
|
+
ModalComponent: MyAchievementsModal,
|
|
106
|
+
ConfettiComponent: MyConfetti,
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
<App />
|
|
110
|
+
</AchievementProvider>
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
## Hooks
|
|
113
114
|
|
|
114
|
-
|
|
115
|
+
```tsx
|
|
116
|
+
const {
|
|
117
|
+
track,
|
|
118
|
+
increment,
|
|
119
|
+
trackMultiple,
|
|
120
|
+
unlockedIds,
|
|
121
|
+
unlockedAchievements,
|
|
122
|
+
allAchievements,
|
|
123
|
+
unlockedCount,
|
|
124
|
+
totalCount,
|
|
125
|
+
} = useSimpleAchievements();
|
|
126
|
+
```
|
|
115
127
|
|
|
116
|
-
|
|
128
|
+
Deprecated aliases from v3, including `unlocked`, remain available until `4.2`.
|
|
117
129
|
|
|
118
|
-
|
|
130
|
+
## Event-Based Tracking
|
|
119
131
|
|
|
120
|
-
|
|
121
|
-
// achievements.ts
|
|
122
|
-
const achievements = {
|
|
123
|
-
score: {
|
|
124
|
-
100: { title: 'Century!', description: 'Score 100 points', icon: '🏆' },
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
```
|
|
132
|
+
For larger apps, create an engine and emit semantic events:
|
|
128
133
|
|
|
129
134
|
```tsx
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
import {
|
|
136
|
+
AchievementEngine,
|
|
137
|
+
AchievementProvider,
|
|
138
|
+
AchievementsWidget,
|
|
139
|
+
useAchievementEngine,
|
|
140
|
+
} from 'react-achievements';
|
|
141
|
+
|
|
142
|
+
const engine = new AchievementEngine({
|
|
143
|
+
achievements,
|
|
144
|
+
eventMapping: {
|
|
145
|
+
userScored: (data) => ({ score: data.points }),
|
|
146
|
+
},
|
|
147
|
+
storage: 'local',
|
|
148
|
+
});
|
|
132
149
|
|
|
133
|
-
function
|
|
150
|
+
function Game() {
|
|
151
|
+
const engine = useAchievementEngine();
|
|
152
|
+
return <button onClick={() => engine.emit('userScored', { points: 100 })}>Score</button>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default function App() {
|
|
134
156
|
return (
|
|
135
|
-
<AchievementProvider
|
|
157
|
+
<AchievementProvider engine={engine}>
|
|
136
158
|
<Game />
|
|
159
|
+
<AchievementsWidget />
|
|
137
160
|
</AchievementProvider>
|
|
138
161
|
);
|
|
139
162
|
}
|
|
140
163
|
```
|
|
141
164
|
|
|
165
|
+
## Headless Usage
|
|
166
|
+
|
|
167
|
+
Use the DOM-free entry point when building custom UI or preparing a React Native integration:
|
|
168
|
+
|
|
142
169
|
```tsx
|
|
143
|
-
|
|
144
|
-
|
|
170
|
+
import {
|
|
171
|
+
AchievementProvider,
|
|
172
|
+
useAchievementState,
|
|
173
|
+
useSimpleAchievements,
|
|
174
|
+
} from 'react-achievements/headless';
|
|
145
175
|
|
|
146
|
-
function
|
|
147
|
-
const { track
|
|
176
|
+
function CustomAchievementsPanel() {
|
|
177
|
+
const { track } = useSimpleAchievements();
|
|
178
|
+
const { allAchievements, unlockedCount, totalCount } = useAchievementState();
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<section>
|
|
182
|
+
<button onClick={() => track('score', 100)}>Score 100</button>
|
|
183
|
+
<p>{unlockedCount} / {totalCount} unlocked</p>
|
|
184
|
+
|
|
185
|
+
{allAchievements.map((achievement) => (
|
|
186
|
+
<div key={achievement.achievementId}>
|
|
187
|
+
{achievement.isUnlocked ? 'Unlocked' : 'Locked'}: {achievement.achievementTitle}
|
|
188
|
+
</div>
|
|
189
|
+
))}
|
|
190
|
+
</section>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
148
193
|
|
|
194
|
+
export function App() {
|
|
149
195
|
return (
|
|
150
|
-
<
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
</div>
|
|
196
|
+
<AchievementProvider achievements={achievements}>
|
|
197
|
+
<CustomAchievementsPanel />
|
|
198
|
+
</AchievementProvider>
|
|
154
199
|
);
|
|
155
200
|
}
|
|
156
201
|
```
|
|
157
202
|
|
|
158
|
-
|
|
203
|
+
React Native UI components are not included in `4.0`; use `achievements-engine` or the `/headless` React APIs with your own native UI.
|
|
159
204
|
|
|
160
|
-
|
|
205
|
+
## Entry Points
|
|
161
206
|
|
|
162
|
-
|
|
207
|
+
- `react-achievements` - v4 web API with provider, hooks, built-in effects, widget, modal, and list components
|
|
208
|
+
- `react-achievements/web` - explicit web entry point
|
|
209
|
+
- `react-achievements/headless` - provider, hooks, engine, storage, and types without DOM UI
|
|
163
210
|
|
|
164
|
-
|
|
211
|
+
## Migrating From v3
|
|
165
212
|
|
|
166
|
-
|
|
213
|
+
- Built-in UI is now the default; `useBuiltInUI` is a deprecated no-op.
|
|
214
|
+
- `AchievementsWidget` replaces the legacy manual `BadgesButtonWithModal` setup.
|
|
215
|
+
- `useSimpleAchievements()` now returns `unlockedIds`, `unlockedAchievements`, and `allAchievements`.
|
|
216
|
+
- External UI peer dependencies are no longer required.
|
|
217
|
+
- Deprecated v3 component names remain as compatibility wrappers until `4.2`.
|
|
167
218
|
|
|
168
219
|
## License
|
|
169
220
|
|
|
170
|
-
React Achievements is
|
|
171
|
-
|
|
172
|
-
- **Free for Non-Commercial Use** (MIT License) - Personal projects, education, non-profits, open source
|
|
173
|
-
- **Commercial License Required** - Businesses, SaaS, commercial apps, enterprise
|
|
174
|
-
|
|
175
|
-
**[Get Commercial License →](https://github.com/sponsors/dave-b-b)** | **[License Details](./LICENSE)** | **[Commercial Terms](./COMMERCIAL-LICENSE.md)**
|
|
221
|
+
React Achievements is dual-licensed:
|
|
176
222
|
|
|
177
|
-
|
|
223
|
+
- **Free for Non-Commercial Use** (MIT License)
|
|
224
|
+
- **Commercial License Required** for businesses, SaaS, commercial apps, and enterprise use
|
|
178
225
|
|
|
179
|
-
|
|
226
|
+
[Get Commercial License](https://github.com/sponsors/dave-b-b) | [License Details](./LICENSE) | [Commercial Terms](./COMMERCIAL-LICENSE.md)
|
|
180
227
|
|
|
181
228
|
## AI Agents
|
|
182
229
|
|
|
183
|
-
If you're using AI coding agents, see `AGENTS.md` for
|
|
230
|
+
If you're using AI coding agents, see `AGENTS.md` for the recommended v4 integration prompt.
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var achievementsEngine = require('achievements-engine');
|
|
5
|
+
|
|
6
|
+
// Type guard to detect async storage
|
|
7
|
+
function isAsyncStorage(storage) {
|
|
8
|
+
// Check if methods return Promises
|
|
9
|
+
const testResult = storage.getMetrics();
|
|
10
|
+
return testResult && typeof testResult.then === 'function';
|
|
11
|
+
}
|
|
12
|
+
var StorageType;
|
|
13
|
+
(function (StorageType) {
|
|
14
|
+
StorageType["Local"] = "local";
|
|
15
|
+
StorageType["Memory"] = "memory";
|
|
16
|
+
StorageType["IndexedDB"] = "indexeddb";
|
|
17
|
+
StorageType["RestAPI"] = "restapi"; // Asynchronous REST API storage
|
|
18
|
+
})(StorageType || (StorageType = {}));
|
|
19
|
+
|
|
20
|
+
const warnedMessages = new Set();
|
|
21
|
+
function warnDeprecation(message) {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
const isProduction = typeof globalThis !== 'undefined' &&
|
|
24
|
+
((_b = (_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.env) === null || _b === void 0 ? void 0 : _b.NODE_ENV) === 'production';
|
|
25
|
+
if (isProduction || warnedMessages.has(message)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
warnedMessages.add(message);
|
|
29
|
+
console.warn(`[react-achievements] ${message}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const AchievementContext = React.createContext(undefined);
|
|
33
|
+
const getAllAchievementRecord = (engine) => {
|
|
34
|
+
return Object.fromEntries(engine.getAllAchievements().map((achievement) => [
|
|
35
|
+
achievement.achievementId,
|
|
36
|
+
achievement,
|
|
37
|
+
]));
|
|
38
|
+
};
|
|
39
|
+
const AchievementProvider = ({ achievements: achievementsConfig, storage = 'local', children, onError, useBuiltInUI, restApiConfig, engine: externalEngine, eventMapping, icons = {}, }) => {
|
|
40
|
+
if (useBuiltInUI !== undefined) {
|
|
41
|
+
warnDeprecation('`useBuiltInUI` is deprecated and is now a no-op because built-in UI is the default. It will be removed in 4.2.');
|
|
42
|
+
}
|
|
43
|
+
if (achievementsConfig && externalEngine) {
|
|
44
|
+
throw new Error('Cannot provide both "achievements" and "engine" props to AchievementProvider.\n\n' +
|
|
45
|
+
'Choose one pattern:\n' +
|
|
46
|
+
'1. Direct metric tracking: <AchievementProvider achievements={config}>\n' +
|
|
47
|
+
'2. Event-based tracking: <AchievementProvider engine={myEngine}>');
|
|
48
|
+
}
|
|
49
|
+
const isProviderCreatedEngine = Boolean(achievementsConfig);
|
|
50
|
+
const [engine] = React.useState(() => {
|
|
51
|
+
if (externalEngine) {
|
|
52
|
+
return externalEngine;
|
|
53
|
+
}
|
|
54
|
+
if (!achievementsConfig) {
|
|
55
|
+
throw new Error('AchievementProvider requires either "achievements" or "engine" prop.\n\n' +
|
|
56
|
+
'1. Direct metric tracking: <AchievementProvider achievements={config}>\n' +
|
|
57
|
+
'2. Event-based tracking: <AchievementProvider engine={myEngine}>');
|
|
58
|
+
}
|
|
59
|
+
return new achievementsEngine.AchievementEngine({
|
|
60
|
+
achievements: achievementsConfig,
|
|
61
|
+
storage: storage,
|
|
62
|
+
restApiConfig,
|
|
63
|
+
onError: onError,
|
|
64
|
+
eventMapping,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
const [achievementState, setAchievementState] = React.useState(() => ({
|
|
68
|
+
unlocked: [...engine.getUnlocked()],
|
|
69
|
+
all: getAllAchievementRecord(engine),
|
|
70
|
+
}));
|
|
71
|
+
const syncAchievementState = React.useCallback(() => {
|
|
72
|
+
setAchievementState({
|
|
73
|
+
unlocked: [...engine.getUnlocked()],
|
|
74
|
+
all: getAllAchievementRecord(engine),
|
|
75
|
+
});
|
|
76
|
+
}, [engine]);
|
|
77
|
+
React.useEffect(() => {
|
|
78
|
+
return () => {
|
|
79
|
+
if (!externalEngine) {
|
|
80
|
+
engine.destroy();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, [engine, externalEngine]);
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
const unsubscribeUnlocked = engine.on('achievement:unlocked', syncAchievementState);
|
|
86
|
+
const unsubscribeStateChanged = engine.on('state:changed', syncAchievementState);
|
|
87
|
+
return () => {
|
|
88
|
+
unsubscribeUnlocked();
|
|
89
|
+
unsubscribeStateChanged();
|
|
90
|
+
};
|
|
91
|
+
}, [engine, syncAchievementState]);
|
|
92
|
+
const update = (newMetrics) => {
|
|
93
|
+
engine.update(newMetrics);
|
|
94
|
+
};
|
|
95
|
+
const reset = () => {
|
|
96
|
+
engine.reset();
|
|
97
|
+
syncAchievementState();
|
|
98
|
+
};
|
|
99
|
+
const getState = () => {
|
|
100
|
+
const metrics = engine.getMetrics();
|
|
101
|
+
const unlocked = engine.getUnlocked();
|
|
102
|
+
const metricsInArrayFormat = {};
|
|
103
|
+
Object.entries(metrics).forEach(([key, value]) => {
|
|
104
|
+
metricsInArrayFormat[key] = Array.isArray(value) ? value : [value];
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
metrics: metricsInArrayFormat,
|
|
108
|
+
unlocked: [...unlocked],
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
const exportData = () => {
|
|
112
|
+
return engine.export();
|
|
113
|
+
};
|
|
114
|
+
const importData = (jsonString, options) => {
|
|
115
|
+
const result = engine.import(jsonString, options);
|
|
116
|
+
syncAchievementState();
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
const getAllAchievements = () => {
|
|
120
|
+
return engine.getAllAchievements();
|
|
121
|
+
};
|
|
122
|
+
return (React.createElement(AchievementContext.Provider, { value: {
|
|
123
|
+
update,
|
|
124
|
+
achievements: achievementState,
|
|
125
|
+
reset,
|
|
126
|
+
getState,
|
|
127
|
+
exportData,
|
|
128
|
+
importData,
|
|
129
|
+
getAllAchievements,
|
|
130
|
+
engine,
|
|
131
|
+
icons,
|
|
132
|
+
_isLegacyPattern: isProviderCreatedEngine,
|
|
133
|
+
} }, children));
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const useAchievements = () => {
|
|
137
|
+
const context = React.useContext(AchievementContext);
|
|
138
|
+
if (!context) {
|
|
139
|
+
throw new Error('useAchievements must be used within an AchievementProvider');
|
|
140
|
+
}
|
|
141
|
+
return context;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const useAchievementState = () => {
|
|
145
|
+
const { achievements, getAllAchievements, getState } = useAchievements();
|
|
146
|
+
const allAchievements = getAllAchievements();
|
|
147
|
+
const unlockedIds = achievements.unlocked;
|
|
148
|
+
const unlockedAchievementSet = new Set(unlockedIds);
|
|
149
|
+
const unlockedAchievements = allAchievements.filter((achievement) => unlockedAchievementSet.has(achievement.achievementId));
|
|
150
|
+
return {
|
|
151
|
+
unlockedIds,
|
|
152
|
+
unlockedAchievements,
|
|
153
|
+
allAchievements,
|
|
154
|
+
unlockedCount: unlockedIds.length,
|
|
155
|
+
totalCount: allAchievements.length,
|
|
156
|
+
metrics: getState().metrics,
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A simplified hook for achievement tracking.
|
|
162
|
+
* Provides the v4 happy path for direct metric updates plus explicit state names.
|
|
163
|
+
*/
|
|
164
|
+
const useSimpleAchievements = () => {
|
|
165
|
+
const { update, reset, getState, exportData, importData } = useAchievements();
|
|
166
|
+
const achievementState = useAchievementState();
|
|
167
|
+
const track = (metric, value) => update({ [metric]: value });
|
|
168
|
+
const increment = (metric, amount = 1) => {
|
|
169
|
+
const currentState = getState();
|
|
170
|
+
const currentMetricArray = currentState.metrics[metric] || [0];
|
|
171
|
+
const currentValue = Array.isArray(currentMetricArray)
|
|
172
|
+
? currentMetricArray[0]
|
|
173
|
+
: currentMetricArray;
|
|
174
|
+
const newValue = (typeof currentValue === 'number' ? currentValue : 0) + amount;
|
|
175
|
+
update({ [metric]: newValue });
|
|
176
|
+
};
|
|
177
|
+
const trackMultiple = (metrics) => update(metrics);
|
|
178
|
+
return {
|
|
179
|
+
track,
|
|
180
|
+
increment,
|
|
181
|
+
trackMultiple,
|
|
182
|
+
unlockedIds: achievementState.unlockedIds,
|
|
183
|
+
unlockedAchievements: achievementState.unlockedAchievements,
|
|
184
|
+
allAchievements: achievementState.allAchievements,
|
|
185
|
+
unlockedCount: achievementState.unlockedCount,
|
|
186
|
+
totalCount: achievementState.totalCount,
|
|
187
|
+
metrics: achievementState.metrics,
|
|
188
|
+
reset,
|
|
189
|
+
getState,
|
|
190
|
+
exportData,
|
|
191
|
+
importData,
|
|
192
|
+
getAllAchievements: () => achievementState.allAchievements,
|
|
193
|
+
/**
|
|
194
|
+
* @deprecated Use `unlockedIds` instead. This alias will be removed in 4.2.
|
|
195
|
+
*/
|
|
196
|
+
unlocked: achievementState.unlockedIds,
|
|
197
|
+
/**
|
|
198
|
+
* @deprecated Use `allAchievements` instead. This alias will be removed in 4.2.
|
|
199
|
+
*/
|
|
200
|
+
all: achievementState.allAchievements,
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Access the active AchievementEngine instance.
|
|
206
|
+
*
|
|
207
|
+
* In v4 this works with both provider-created engines (`achievements` prop) and
|
|
208
|
+
* injected engines (`engine` prop).
|
|
209
|
+
*/
|
|
210
|
+
const useAchievementEngine = () => {
|
|
211
|
+
const context = React.useContext(AchievementContext);
|
|
212
|
+
if (!context) {
|
|
213
|
+
throw new Error('useAchievementEngine must be used within an AchievementProvider.\n\n' +
|
|
214
|
+
'Wrap your component tree:\n' +
|
|
215
|
+
'<AchievementProvider achievements={achievements}>\n' +
|
|
216
|
+
' <YourComponent />\n' +
|
|
217
|
+
'</AchievementProvider>');
|
|
218
|
+
}
|
|
219
|
+
return context.engine;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
Object.defineProperty(exports, 'AchievementBuilder', {
|
|
223
|
+
enumerable: true,
|
|
224
|
+
get: function () { return achievementsEngine.AchievementBuilder; }
|
|
225
|
+
});
|
|
226
|
+
Object.defineProperty(exports, 'AchievementEngine', {
|
|
227
|
+
enumerable: true,
|
|
228
|
+
get: function () { return achievementsEngine.AchievementEngine; }
|
|
229
|
+
});
|
|
230
|
+
Object.defineProperty(exports, 'AchievementError', {
|
|
231
|
+
enumerable: true,
|
|
232
|
+
get: function () { return achievementsEngine.AchievementError; }
|
|
233
|
+
});
|
|
234
|
+
Object.defineProperty(exports, 'AsyncStorageAdapter', {
|
|
235
|
+
enumerable: true,
|
|
236
|
+
get: function () { return achievementsEngine.AsyncStorageAdapter; }
|
|
237
|
+
});
|
|
238
|
+
Object.defineProperty(exports, 'ConfigurationError', {
|
|
239
|
+
enumerable: true,
|
|
240
|
+
get: function () { return achievementsEngine.ConfigurationError; }
|
|
241
|
+
});
|
|
242
|
+
Object.defineProperty(exports, 'ImportValidationError', {
|
|
243
|
+
enumerable: true,
|
|
244
|
+
get: function () { return achievementsEngine.ImportValidationError; }
|
|
245
|
+
});
|
|
246
|
+
Object.defineProperty(exports, 'IndexedDBStorage', {
|
|
247
|
+
enumerable: true,
|
|
248
|
+
get: function () { return achievementsEngine.IndexedDBStorage; }
|
|
249
|
+
});
|
|
250
|
+
Object.defineProperty(exports, 'LocalStorage', {
|
|
251
|
+
enumerable: true,
|
|
252
|
+
get: function () { return achievementsEngine.LocalStorage; }
|
|
253
|
+
});
|
|
254
|
+
Object.defineProperty(exports, 'MemoryStorage', {
|
|
255
|
+
enumerable: true,
|
|
256
|
+
get: function () { return achievementsEngine.MemoryStorage; }
|
|
257
|
+
});
|
|
258
|
+
Object.defineProperty(exports, 'OfflineQueueStorage', {
|
|
259
|
+
enumerable: true,
|
|
260
|
+
get: function () { return achievementsEngine.OfflineQueueStorage; }
|
|
261
|
+
});
|
|
262
|
+
Object.defineProperty(exports, 'RestApiStorage', {
|
|
263
|
+
enumerable: true,
|
|
264
|
+
get: function () { return achievementsEngine.RestApiStorage; }
|
|
265
|
+
});
|
|
266
|
+
Object.defineProperty(exports, 'StorageError', {
|
|
267
|
+
enumerable: true,
|
|
268
|
+
get: function () { return achievementsEngine.StorageError; }
|
|
269
|
+
});
|
|
270
|
+
Object.defineProperty(exports, 'StorageQuotaError', {
|
|
271
|
+
enumerable: true,
|
|
272
|
+
get: function () { return achievementsEngine.StorageQuotaError; }
|
|
273
|
+
});
|
|
274
|
+
Object.defineProperty(exports, 'StorageType', {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
get: function () { return achievementsEngine.StorageType; }
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(exports, 'SyncError', {
|
|
279
|
+
enumerable: true,
|
|
280
|
+
get: function () { return achievementsEngine.SyncError; }
|
|
281
|
+
});
|
|
282
|
+
Object.defineProperty(exports, 'createConfigHash', {
|
|
283
|
+
enumerable: true,
|
|
284
|
+
get: function () { return achievementsEngine.createConfigHash; }
|
|
285
|
+
});
|
|
286
|
+
Object.defineProperty(exports, 'exportAchievementData', {
|
|
287
|
+
enumerable: true,
|
|
288
|
+
get: function () { return achievementsEngine.exportAchievementData; }
|
|
289
|
+
});
|
|
290
|
+
Object.defineProperty(exports, 'importAchievementData', {
|
|
291
|
+
enumerable: true,
|
|
292
|
+
get: function () { return achievementsEngine.importAchievementData; }
|
|
293
|
+
});
|
|
294
|
+
Object.defineProperty(exports, 'isAchievementError', {
|
|
295
|
+
enumerable: true,
|
|
296
|
+
get: function () { return achievementsEngine.isAchievementError; }
|
|
297
|
+
});
|
|
298
|
+
Object.defineProperty(exports, 'isRecoverableError', {
|
|
299
|
+
enumerable: true,
|
|
300
|
+
get: function () { return achievementsEngine.isRecoverableError; }
|
|
301
|
+
});
|
|
302
|
+
Object.defineProperty(exports, 'isSimpleConfig', {
|
|
303
|
+
enumerable: true,
|
|
304
|
+
get: function () { return achievementsEngine.isSimpleConfig; }
|
|
305
|
+
});
|
|
306
|
+
Object.defineProperty(exports, 'normalizeAchievements', {
|
|
307
|
+
enumerable: true,
|
|
308
|
+
get: function () { return achievementsEngine.normalizeAchievements; }
|
|
309
|
+
});
|
|
310
|
+
exports.AchievementContext = AchievementContext;
|
|
311
|
+
exports.AchievementProvider = AchievementProvider;
|
|
312
|
+
exports.isAsyncStorage = isAsyncStorage;
|
|
313
|
+
exports.useAchievementEngine = useAchievementEngine;
|
|
314
|
+
exports.useAchievementState = useAchievementState;
|
|
315
|
+
exports.useAchievements = useAchievements;
|
|
316
|
+
exports.useSimpleAchievements = useSimpleAchievements;
|
|
317
|
+
//# sourceMappingURL=headless.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless.cjs","sources":["../src/core/types.ts","../src/core/utils/deprecation.ts","../src/providers/AchievementProvider.tsx","../src/hooks/useAchievements.ts","../src/hooks/useAchievementState.ts","../src/hooks/useSimpleAchievements.ts","../src/hooks/useAchievementEngine.ts"],"sourcesContent":[null,null,null,null,null,null,null],"names":["createContext","useState","AchievementEngine","useCallback","useEffect","useContext"],"mappings":"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"}
|