@popp0102/questify 0.10.0 → 0.11.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/dist/questify.js +168 -173
- package/dist/questify.js.map +1 -1
- package/package.json +1 -1
package/dist/questify.js
CHANGED
|
@@ -1,282 +1,277 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
const I = {},
|
|
4
|
-
store: async (
|
|
5
|
-
I[
|
|
1
|
+
import { jsx as p } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as q, useState as O, useEffect as B, useContext as F } from "react";
|
|
3
|
+
const I = {}, J = {
|
|
4
|
+
store: async (r, e) => {
|
|
5
|
+
I[r] = e;
|
|
6
6
|
},
|
|
7
|
-
load: async (
|
|
7
|
+
load: async (r) => I[r]
|
|
8
8
|
};
|
|
9
|
-
class
|
|
10
|
-
constructor(
|
|
11
|
-
this.id =
|
|
9
|
+
class w {
|
|
10
|
+
constructor(e, t, s, l, c) {
|
|
11
|
+
this.id = e, this.type = t, this.title = s, this.solution = l, this.blueprint = c, this.isSolved = !1;
|
|
12
12
|
}
|
|
13
|
-
clone({ isSolved:
|
|
14
|
-
const
|
|
15
|
-
return
|
|
13
|
+
clone({ isSolved: e }) {
|
|
14
|
+
const t = Object.create(w.prototype);
|
|
15
|
+
return t.id = this.id, t.type = this.type, t.title = this.title, t.solution = this.solution, t.blueprint = this.blueprint, t.isSolved = e, t;
|
|
16
16
|
}
|
|
17
17
|
markAsSolved() {
|
|
18
18
|
return this.clone({ isSolved: !0 });
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
class
|
|
22
|
-
constructor(
|
|
23
|
-
this.id =
|
|
24
|
-
(
|
|
21
|
+
class h {
|
|
22
|
+
constructor(e) {
|
|
23
|
+
this.id = e.id, this.name = e.name, this.icon = e.icon, this.subtitle = e.subtitle, this.description = e.description, this.levels = e.levels.map(
|
|
24
|
+
(t, s) => new w(s, t.type, t.title, t.solution, t.blueprint)
|
|
25
25
|
), this.updateStatus();
|
|
26
26
|
}
|
|
27
|
-
static
|
|
28
|
-
const
|
|
29
|
-
return
|
|
27
|
+
static constructFromLevels(e, t, s, l, c, n) {
|
|
28
|
+
const i = Object.create(h.prototype);
|
|
29
|
+
return i.id = e, i.name = t, i.icon = l, i.subtitle = c, i.description = n, i.levels = s, i.updateStatus(), i;
|
|
30
30
|
}
|
|
31
|
-
submitAnswer(
|
|
32
|
-
const s = this.
|
|
31
|
+
submitAnswer(e, t) {
|
|
32
|
+
const s = this.levels.find((l) => l.id === e);
|
|
33
33
|
if (!s)
|
|
34
34
|
return null;
|
|
35
|
-
if (s.solution ===
|
|
36
|
-
const l = this.
|
|
37
|
-
(
|
|
35
|
+
if (s.solution === t.trim().toLowerCase()) {
|
|
36
|
+
const l = this.levels.map(
|
|
37
|
+
(c) => c.id === e ? c.markAsSolved() : c
|
|
38
38
|
);
|
|
39
|
-
return this.clone({
|
|
39
|
+
return this.clone({ levels: l });
|
|
40
40
|
}
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
updateStatus() {
|
|
44
|
-
const
|
|
44
|
+
const e = this.levels.filter((l) => l.isSolved).length, t = this.levels.length;
|
|
45
45
|
let s;
|
|
46
|
-
|
|
46
|
+
e === 0 ? s = "not-started" : e === t ? s = "completed" : s = "in-progress", this.progressSummary = {
|
|
47
47
|
status: s,
|
|
48
|
-
solvedCount:
|
|
49
|
-
|
|
50
|
-
percentageComplete:
|
|
48
|
+
solvedCount: e,
|
|
49
|
+
totalLevels: t,
|
|
50
|
+
percentageComplete: t > 0 ? e / t * 100 : 0
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
-
clone(
|
|
54
|
-
const
|
|
55
|
-
return
|
|
53
|
+
clone(e) {
|
|
54
|
+
const t = Object.create(h.prototype);
|
|
55
|
+
return t.id = this.id, t.name = this.name, t.icon = this.icon, t.subtitle = this.subtitle, t.description = this.description, t.levels = e.levels ?? this.levels, t.updateStatus(), t;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
class
|
|
59
|
-
constructor(
|
|
60
|
-
this.
|
|
58
|
+
class y {
|
|
59
|
+
constructor(e = null, t = null) {
|
|
60
|
+
this.activeAdventureId = e, this.activeLevelIndex = t;
|
|
61
61
|
}
|
|
62
62
|
clone() {
|
|
63
|
-
const
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
isOnHuntSelection() {
|
|
67
|
-
return this.activeHuntId === null;
|
|
63
|
+
const e = Object.create(y.prototype);
|
|
64
|
+
return e.activeAdventureId = this.activeAdventureId, e.activeLevelIndex = this.activeLevelIndex, e;
|
|
68
65
|
}
|
|
69
|
-
|
|
70
|
-
return this.
|
|
66
|
+
isOnAdventureSelection() {
|
|
67
|
+
return this.activeAdventureId === null;
|
|
71
68
|
}
|
|
72
|
-
|
|
73
|
-
return this.
|
|
69
|
+
isOnLevelSelection() {
|
|
70
|
+
return this.activeAdventureId !== null && this.activeLevelIndex === null;
|
|
74
71
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return t.activeHuntId = null, t.activeTrialIndex = null, t;
|
|
72
|
+
isOnActiveLevel() {
|
|
73
|
+
return this.activeAdventureId !== null && this.activeLevelIndex !== null;
|
|
78
74
|
}
|
|
79
|
-
|
|
75
|
+
moveToAdventureSelection() {
|
|
80
76
|
const e = this.clone();
|
|
81
|
-
return e.
|
|
77
|
+
return e.activeAdventureId = null, e.activeLevelIndex = null, e;
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
if (this.activeHuntId === null)
|
|
85
|
-
throw new Error("Cannot move to trial select when no hunt is active");
|
|
79
|
+
moveToAdventure(e) {
|
|
86
80
|
const t = this.clone();
|
|
87
|
-
return t.
|
|
81
|
+
return t.activeAdventureId = e, t.activeLevelIndex = null, t;
|
|
88
82
|
}
|
|
89
|
-
|
|
90
|
-
if (this.
|
|
91
|
-
throw new Error("Cannot move to
|
|
83
|
+
moveToLevelSelectScreen() {
|
|
84
|
+
if (this.activeAdventureId === null)
|
|
85
|
+
throw new Error("Cannot move to level select when no adventure is active");
|
|
92
86
|
const e = this.clone();
|
|
93
|
-
return e.
|
|
87
|
+
return e.activeLevelIndex = null, e;
|
|
88
|
+
}
|
|
89
|
+
moveToLevelScreen(e) {
|
|
90
|
+
if (this.activeAdventureId === null)
|
|
91
|
+
throw new Error("Cannot move to level when no adventure is active");
|
|
92
|
+
const t = this.clone();
|
|
93
|
+
return t.activeLevelIndex = e, t;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
const
|
|
97
|
-
|
|
96
|
+
const T = q({
|
|
97
|
+
adventures: [],
|
|
98
98
|
playerLocation: null,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
activeAdventure: () => null,
|
|
100
|
+
activeLevel: () => null,
|
|
101
|
+
isOnAdventureSelection: () => !1,
|
|
102
|
+
isOnLevelSelection: () => !1,
|
|
103
|
+
isOnActiveLevel: () => !1,
|
|
104
|
+
activateAdventure: () => {
|
|
105
105
|
},
|
|
106
|
-
|
|
106
|
+
moveToAdventureSelection: () => {
|
|
107
107
|
},
|
|
108
|
-
|
|
108
|
+
moveToLevelSelectScreen: () => {
|
|
109
109
|
},
|
|
110
|
-
|
|
110
|
+
moveToLevelScreen: () => {
|
|
111
111
|
},
|
|
112
112
|
submitAnswer: () => {
|
|
113
113
|
}
|
|
114
114
|
});
|
|
115
|
-
function
|
|
116
|
-
const s =
|
|
117
|
-
|
|
115
|
+
function V({ adventureConfigs: r, persister: e, children: t }) {
|
|
116
|
+
const s = e || J, [l, c] = O(r.map((o) => new h(o))), [n, i] = O(new y());
|
|
117
|
+
B(() => {
|
|
118
118
|
async function o() {
|
|
119
119
|
const d = await Promise.all(
|
|
120
|
-
|
|
121
|
-
const m = `questify-${u.id}-solved-
|
|
122
|
-
if (
|
|
123
|
-
const
|
|
124
|
-
return
|
|
120
|
+
r.map(async (u) => {
|
|
121
|
+
const m = `questify-${u.id}-solved-levels`, L = await s.load(m), S = new h(u);
|
|
122
|
+
if (L) {
|
|
123
|
+
const A = JSON.parse(L), a = S.levels.map((f) => A.includes(f.id) ? f.markAsSolved() : f);
|
|
124
|
+
return h.constructFromLevels(u.id, u.name, a);
|
|
125
125
|
}
|
|
126
126
|
return S;
|
|
127
127
|
})
|
|
128
128
|
);
|
|
129
|
-
|
|
129
|
+
c(d);
|
|
130
130
|
}
|
|
131
131
|
o();
|
|
132
|
-
}, [s,
|
|
133
|
-
function
|
|
134
|
-
return n.
|
|
132
|
+
}, [s, r]);
|
|
133
|
+
function v() {
|
|
134
|
+
return n.activeAdventureId === null ? null : l.find((o) => o.id === n.activeAdventureId) || null;
|
|
135
135
|
}
|
|
136
136
|
function b() {
|
|
137
|
-
const o =
|
|
138
|
-
return !o || n.
|
|
137
|
+
const o = v();
|
|
138
|
+
return !o || n.activeLevelIndex === null ? null : o.levels[n.activeLevelIndex] || null;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
141
|
-
return n.
|
|
140
|
+
function g() {
|
|
141
|
+
return n.isOnAdventureSelection();
|
|
142
142
|
}
|
|
143
143
|
function C() {
|
|
144
|
-
return n.
|
|
144
|
+
return n.isOnLevelSelection();
|
|
145
145
|
}
|
|
146
146
|
function E() {
|
|
147
|
-
return n.
|
|
147
|
+
return n.isOnActiveLevel();
|
|
148
148
|
}
|
|
149
|
-
function
|
|
149
|
+
function P(o) {
|
|
150
150
|
if (!l.find((u) => u.id === o))
|
|
151
|
-
throw new Error(`
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
function P() {
|
|
155
|
-
r(n.moveToHuntSelection());
|
|
151
|
+
throw new Error(`Adventure with id "${o}" not found`);
|
|
152
|
+
i(n.moveToAdventure(o));
|
|
156
153
|
}
|
|
157
154
|
function k() {
|
|
158
|
-
|
|
155
|
+
i(n.moveToAdventureSelection());
|
|
159
156
|
}
|
|
160
|
-
function $(
|
|
161
|
-
|
|
157
|
+
function $() {
|
|
158
|
+
i(n.moveToLevelSelectScreen());
|
|
159
|
+
}
|
|
160
|
+
function j(o) {
|
|
161
|
+
const d = v();
|
|
162
162
|
if (!d)
|
|
163
|
-
throw new Error("Cannot move to
|
|
164
|
-
if (o < 0 || o >= d.
|
|
165
|
-
throw new Error(`
|
|
166
|
-
|
|
163
|
+
throw new Error("Cannot move to level when no adventure is selected");
|
|
164
|
+
if (o < 0 || o >= d.levels.length)
|
|
165
|
+
throw new Error(`Level index ${o} is out of bounds for adventure "${d.id}"`);
|
|
166
|
+
i(n.moveToLevelScreen(o));
|
|
167
167
|
}
|
|
168
|
-
async function
|
|
169
|
-
const d = b(), u =
|
|
168
|
+
async function N(o) {
|
|
169
|
+
const d = b(), u = v();
|
|
170
170
|
if (!d || !u)
|
|
171
171
|
return !1;
|
|
172
172
|
const m = u.submitAnswer(d.id, o);
|
|
173
173
|
if (m) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
const S = `questify-${u.id}-solved-
|
|
177
|
-
return await s.store(S, JSON.stringify(
|
|
174
|
+
const L = l.map((a) => a.id === u.id ? m : a);
|
|
175
|
+
c(L);
|
|
176
|
+
const S = `questify-${u.id}-solved-levels`, A = m.levels.filter((a) => a.isSolved).map((a) => a.id);
|
|
177
|
+
return await s.store(S, JSON.stringify(A)), !0;
|
|
178
178
|
}
|
|
179
179
|
return !1;
|
|
180
180
|
}
|
|
181
|
-
const
|
|
182
|
-
|
|
181
|
+
const Q = {
|
|
182
|
+
adventures: l,
|
|
183
183
|
playerLocation: n,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
submitAnswer:
|
|
184
|
+
activeAdventure: v,
|
|
185
|
+
activeLevel: b,
|
|
186
|
+
isOnAdventureSelection: g,
|
|
187
|
+
isOnLevelSelection: C,
|
|
188
|
+
isOnActiveLevel: E,
|
|
189
|
+
activateAdventure: P,
|
|
190
|
+
moveToAdventureSelection: k,
|
|
191
|
+
moveToLevelSelectScreen: $,
|
|
192
|
+
moveToLevelScreen: j,
|
|
193
|
+
submitAnswer: N
|
|
194
194
|
};
|
|
195
|
-
return /* @__PURE__ */ T
|
|
195
|
+
return /* @__PURE__ */ p(T.Provider, { value: Q, children: t });
|
|
196
196
|
}
|
|
197
|
-
class
|
|
197
|
+
class K {
|
|
198
198
|
constructor() {
|
|
199
199
|
this.registry = /* @__PURE__ */ new Map();
|
|
200
200
|
}
|
|
201
|
-
register(
|
|
202
|
-
this.registry.set(
|
|
201
|
+
register(e, t) {
|
|
202
|
+
this.registry.set(e, t);
|
|
203
203
|
}
|
|
204
|
-
get(
|
|
205
|
-
return this.registry.get(
|
|
204
|
+
get(e) {
|
|
205
|
+
return this.registry.get(e);
|
|
206
206
|
}
|
|
207
|
-
has(
|
|
208
|
-
return this.registry.has(
|
|
207
|
+
has(e) {
|
|
208
|
+
return this.registry.has(e);
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
const x = new
|
|
212
|
-
function
|
|
213
|
-
const
|
|
214
|
-
if (!
|
|
215
|
-
throw new Error("No
|
|
216
|
-
function
|
|
217
|
-
if (
|
|
218
|
-
const n =
|
|
219
|
-
|
|
220
|
-
name: r.name,
|
|
221
|
-
progressSummary: r.progressSummary,
|
|
222
|
-
onSelectHunt: () => i.activateHunt(r.id)
|
|
223
|
-
}));
|
|
224
|
-
return /* @__PURE__ */ T(s, { hunts: n });
|
|
211
|
+
const x = new K();
|
|
212
|
+
function z() {
|
|
213
|
+
const r = F(T);
|
|
214
|
+
if (!r.adventures || r.adventures.length === 0)
|
|
215
|
+
throw new Error("No adventures found. Make sure you pass adventureConfigs to the QuestifyProvider");
|
|
216
|
+
function e(s, l, c) {
|
|
217
|
+
if (r.isOnAdventureSelection()) {
|
|
218
|
+
const n = r.adventures.map((i) => ({ ...i, onSelectAdventure: () => r.activateAdventure(i.id) }));
|
|
219
|
+
return /* @__PURE__ */ p(s, { adventures: n });
|
|
225
220
|
}
|
|
226
|
-
if (
|
|
227
|
-
const n =
|
|
221
|
+
if (r.isOnLevelSelection()) {
|
|
222
|
+
const n = r.activeAdventure();
|
|
228
223
|
if (!n)
|
|
229
|
-
throw new Error("Cannot render
|
|
230
|
-
const
|
|
231
|
-
id:
|
|
232
|
-
title:
|
|
233
|
-
type:
|
|
234
|
-
isSolved:
|
|
235
|
-
solution:
|
|
236
|
-
|
|
224
|
+
throw new Error("Cannot render level selection without an active adventure");
|
|
225
|
+
const i = n.levels.map((v) => ({
|
|
226
|
+
id: v.id,
|
|
227
|
+
title: v.title,
|
|
228
|
+
type: v.type,
|
|
229
|
+
isSolved: v.isSolved,
|
|
230
|
+
solution: v.solution,
|
|
231
|
+
onSelectLevel: () => r.moveToLevelScreen(v.id)
|
|
237
232
|
}));
|
|
238
|
-
return /* @__PURE__ */
|
|
233
|
+
return /* @__PURE__ */ p(
|
|
239
234
|
l,
|
|
240
235
|
{
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
onBack:
|
|
236
|
+
adventure: n,
|
|
237
|
+
levels: i,
|
|
238
|
+
onBack: r.moveToAdventureSelection
|
|
244
239
|
}
|
|
245
240
|
);
|
|
246
241
|
}
|
|
247
|
-
if (
|
|
248
|
-
const n =
|
|
242
|
+
if (r.isOnActiveLevel()) {
|
|
243
|
+
const n = r.activeLevel();
|
|
249
244
|
if (!n)
|
|
250
|
-
throw new Error("Cannot render
|
|
251
|
-
const
|
|
252
|
-
if (!
|
|
253
|
-
throw new Error(`No
|
|
254
|
-
return /* @__PURE__ */
|
|
255
|
-
|
|
245
|
+
throw new Error("Cannot render level screen without an active level");
|
|
246
|
+
const i = x.get(n.type);
|
|
247
|
+
if (!i)
|
|
248
|
+
throw new Error(`No level component registered for type: ${n.type}`);
|
|
249
|
+
return /* @__PURE__ */ p(
|
|
250
|
+
c,
|
|
256
251
|
{
|
|
257
252
|
title: n.title,
|
|
258
|
-
onBack:
|
|
253
|
+
onBack: r.moveToLevelSelectScreen,
|
|
259
254
|
isSolved: n.isSolved,
|
|
260
|
-
|
|
255
|
+
LevelComponent: () => /* @__PURE__ */ p(i, { ...n.blueprint, title: n.title })
|
|
261
256
|
}
|
|
262
257
|
);
|
|
263
258
|
}
|
|
264
259
|
throw new Error("Invalid player location state");
|
|
265
260
|
}
|
|
266
|
-
function
|
|
267
|
-
return
|
|
261
|
+
function t(s) {
|
|
262
|
+
return r.submitAnswer(s);
|
|
268
263
|
}
|
|
269
264
|
return {
|
|
270
|
-
renderScreen:
|
|
271
|
-
submitAnswer:
|
|
265
|
+
renderScreen: e,
|
|
266
|
+
submitAnswer: t
|
|
272
267
|
};
|
|
273
268
|
}
|
|
274
|
-
const
|
|
275
|
-
x.register(
|
|
269
|
+
const D = (r, e) => {
|
|
270
|
+
x.register(r, e);
|
|
276
271
|
};
|
|
277
272
|
export {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
V as QuestifyProvider,
|
|
274
|
+
D as registerLevelComponent,
|
|
275
|
+
z as useQuestify
|
|
281
276
|
};
|
|
282
277
|
//# sourceMappingURL=questify.js.map
|
package/dist/questify.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"questify.js","sources":["../lib/store/MemoryPersister.ts","../lib/models/Trial.ts","../lib/models/Hunt.ts","../lib/models/PlayerLocation.ts","../lib/store/QuestifyContext.tsx","../lib/registry/TrialRegistry.ts","../lib/hooks/useQuestify.tsx","../lib/index.ts"],"sourcesContent":["const storage = {};\nexport const memoryPersister = {\n store: async (key, value) => {\n storage[key] = value;\n },\n load: async (key) => {\n return storage[key];\n },\n};\n","class Trial {\n id: number;\n type: string;\n title: string;\n solution: string;\n blueprint: Record<string, any>;\n isSolved: boolean;\n\n constructor(id: number, type: string, title: string, solution: string, blueprint: Record<string, any>) {\n this.id = id;\n this.type = type;\n this.title = title;\n this.solution = solution;\n this.blueprint = blueprint;\n this.isSolved = false;\n }\n\n private clone({ isSolved }: { isSolved: boolean }): Trial {\n const cloned = Object.create(Trial.prototype);\n cloned.id = this.id;\n cloned.type = this.type;\n cloned.title = this.title;\n cloned.solution = this.solution;\n cloned.blueprint = this.blueprint;\n cloned.isSolved = isSolved;\n return cloned;\n }\n\n markAsSolved(): Trial {\n return this.clone({ isSolved: true });\n }\n}\n\nexport default Trial;\n","import Trial from '@models/Trial';\n\nenum HuntStatus {\n NOT_STARTED = 'not-started',\n IN_PROGRESS = 'in-progress',\n COMPLETED = 'completed'\n}\n\ninterface TrialConfig {\n type: string;\n title: string;\n solution: string;\n blueprint: Record<string, any>;\n}\n\ninterface HuntConfig {\n id: string;\n name: string;\n icon?: string;\n subtitle?: string;\n description?: string;\n trials: TrialConfig[];\n}\n\ninterface ProgressSummary {\n status: HuntStatus;\n solvedCount: number;\n totalTrials: number;\n percentageComplete: number;\n}\n\nclass Hunt {\n id: string;\n name: string;\n icon?: string;\n subtitle?: string;\n description?: string;\n trials: Trial[];\n progressSummary: ProgressSummary;\n\n constructor(config: HuntConfig) {\n this.id = config.id;\n this.name = config.name;\n this.icon = config.icon;\n this.subtitle = config.subtitle;\n this.description = config.description;\n this.trials = config.trials.map((trialConfig, index) =>\n new Trial(index, trialConfig.type, trialConfig.title, trialConfig.solution, trialConfig.blueprint)\n );\n this.updateStatus();\n }\n\n static constructFromTrials(id: string, name: string, trials: Trial[], icon?: string, subtitle?: string, description?: string): Hunt {\n const hunt = Object.create(Hunt.prototype);\n hunt.id = id;\n hunt.name = name;\n hunt.icon = icon;\n hunt.subtitle = subtitle;\n hunt.description = description;\n hunt.trials = trials;\n hunt.updateStatus();\n return hunt;\n }\n\n submitAnswer(trialId: number, answer: string): Hunt | null {\n const trial = this.trials.find(trial => trial.id === trialId);\n\n if (!trial) {\n return null;\n }\n\n if (trial.solution === answer.trim().toLowerCase()) {\n const updatedTrials = this.trials.map(trial =>\n trial.id === trialId ? trial.markAsSolved() : trial\n );\n return this.clone({ trials: updatedTrials });\n }\n\n return null;\n }\n\n private updateStatus(): void {\n const solvedCount = this.trials.filter(trial => trial.isSolved).length;\n const totalTrials = this.trials.length;\n\n let status: HuntStatus;\n if (solvedCount === 0) {\n status = HuntStatus.NOT_STARTED;\n } else if (solvedCount === totalTrials) {\n status = HuntStatus.COMPLETED;\n } else {\n status = HuntStatus.IN_PROGRESS;\n }\n\n this.progressSummary = {\n status,\n solvedCount,\n totalTrials,\n percentageComplete: totalTrials > 0 ? (solvedCount / totalTrials) * 100 : 0\n };\n }\n\n private clone(updates: Partial<{ trials: Trial[] }>): Hunt {\n const cloned = Object.create(Hunt.prototype);\n cloned.id = this.id;\n cloned.name = this.name;\n cloned.icon = this.icon;\n cloned.subtitle = this.subtitle;\n cloned.description = this.description;\n cloned.trials = updates.trials ?? this.trials;\n cloned.updateStatus();\n return cloned;\n }\n}\n\nexport default Hunt;\n","class PlayerLocation {\n activeHuntId: string | null;\n activeTrialIndex: number | null;\n\n constructor(huntId: string | null = null, trialIndex: number | null = null) {\n this.activeHuntId = huntId;\n this.activeTrialIndex = trialIndex;\n }\n\n private clone(): PlayerLocation {\n const cloned = Object.create(PlayerLocation.prototype);\n cloned.activeHuntId = this.activeHuntId;\n cloned.activeTrialIndex = this.activeTrialIndex;\n return cloned;\n }\n\n isOnHuntSelection(): boolean {\n return this.activeHuntId === null;\n }\n\n isOnTrialSelection(): boolean {\n return this.activeHuntId !== null && this.activeTrialIndex === null;\n }\n\n isOnActiveTrial(): boolean {\n return this.activeHuntId !== null && this.activeTrialIndex !== null;\n }\n\n moveToHuntSelection(): PlayerLocation {\n const newLocation = this.clone();\n newLocation.activeHuntId = null;\n newLocation.activeTrialIndex = null;\n return newLocation;\n }\n\n moveToHunt(huntId: string): PlayerLocation {\n const newLocation = this.clone();\n newLocation.activeHuntId = huntId;\n newLocation.activeTrialIndex = null;\n return newLocation;\n }\n\n moveToTrialSelectScreen(): PlayerLocation {\n if (this.activeHuntId === null) {\n throw new Error('Cannot move to trial select when no hunt is active');\n }\n\n const newLocation = this.clone();\n newLocation.activeTrialIndex = null;\n return newLocation;\n }\n\n moveToTrialScreen(trialIdx: number): PlayerLocation {\n if (this.activeHuntId === null) {\n throw new Error('Cannot move to trial when no hunt is active');\n }\n\n const newLocation = this.clone();\n newLocation.activeTrialIndex = trialIdx;\n return newLocation;\n }\n}\n\nexport default PlayerLocation;\n","import { useState, useEffect, createContext } from \"react\";\nimport { memoryPersister } from '@store/MemoryPersister';\nimport Hunt from '@models/Hunt';\nimport PlayerLocation from '@models/PlayerLocation';\n\nexport const QuestifyContext = createContext({\n hunts: [],\n playerLocation: null,\n activeHunt: () => null,\n activeTrial: () => null,\n isOnHuntSelection: () => false,\n isOnTrialSelection: () => false,\n isOnActiveTrial: () => false,\n activateHunt: () => {},\n moveToHuntSelection: () => {},\n moveToTrialSelectScreen: () => {},\n moveToTrialScreen: () => {},\n submitAnswer: () => {},\n});\n\nexport default function QuestifyProvider({ huntConfigs, persister, children }) {\n const progressPersister = persister || memoryPersister;\n const [hunts, setHunts] = useState(huntConfigs.map(config => new Hunt(config)));\n const [playerLocation, setPlayerLocation] = useState(new PlayerLocation());\n\n useEffect(() => {\n async function loadProgress() {\n const restoredHunts = await Promise.all(\n huntConfigs.map(async (config) => {\n const storageKey = `questify-${config.id}-solved-trials`;\n const savedTrialsStr = await progressPersister.load(storageKey);\n const restoredHunt = new Hunt(config);\n\n if (savedTrialsStr) {\n const solvedTrialIds = JSON.parse(savedTrialsStr);\n const updatedTrials = restoredHunt.trials.map(trial => solvedTrialIds.includes(trial.id) ? trial.markAsSolved() : trial);\n return Hunt.constructFromTrials(config.id, config.name, updatedTrials);\n }\n\n return restoredHunt;\n })\n );\n\n setHunts(restoredHunts);\n }\n\n loadProgress();\n }, [progressPersister, huntConfigs]);\n\n function activeHunt() {\n if (playerLocation.activeHuntId === null) {\n return null;\n }\n return hunts.find(hunt => hunt.id === playerLocation.activeHuntId) || null;\n }\n\n function activeTrial() {\n const hunt = activeHunt();\n if (!hunt || playerLocation.activeTrialIndex === null) {\n return null;\n }\n return hunt.trials[playerLocation.activeTrialIndex] || null;\n }\n\n function isOnHuntSelection() {\n return playerLocation.isOnHuntSelection();\n }\n\n function isOnTrialSelection() {\n return playerLocation.isOnTrialSelection();\n }\n\n function isOnActiveTrial() {\n return playerLocation.isOnActiveTrial();\n }\n\n function activateHunt(huntId) {\n const hunt = hunts.find(hunt => hunt.id === huntId);\n if (!hunt) {\n throw new Error(`Hunt with id \"${huntId}\" not found`);\n }\n setPlayerLocation(playerLocation.moveToHunt(huntId));\n }\n\n function moveToHuntSelection() {\n setPlayerLocation(playerLocation.moveToHuntSelection());\n }\n\n function moveToTrialSelectScreen() {\n setPlayerLocation(playerLocation.moveToTrialSelectScreen());\n }\n\n function moveToTrialScreen(trialIdx) {\n const hunt = activeHunt();\n if (!hunt) {\n throw new Error('Cannot move to trial when no hunt is selected');\n }\n if (trialIdx < 0 || trialIdx >= hunt.trials.length) {\n throw new Error(`Trial index ${trialIdx} is out of bounds for hunt \"${hunt.id}\"`);\n }\n setPlayerLocation(playerLocation.moveToTrialScreen(trialIdx));\n }\n\n async function handleSubmitAnswer(answer) {\n const trial = activeTrial();\n const currentHunt = activeHunt();\n\n if (!trial || !currentHunt) {\n return false;\n }\n\n const newHunt = currentHunt.submitAnswer(trial.id, answer);\n\n if (newHunt) {\n const updatedHunts = hunts.map(hunt => hunt.id === currentHunt.id ? newHunt : hunt);\n setHunts(updatedHunts);\n\n const storageKey = `questify-${currentHunt.id}-solved-trials`;\n const solvedTrialIds = newHunt.trials.filter(trial => trial.isSolved).map(trial => trial.id);\n await progressPersister.store(storageKey, JSON.stringify(solvedTrialIds));\n return true;\n }\n\n return false;\n }\n\n const questifyCtxValue = {\n hunts: hunts,\n playerLocation: playerLocation,\n activeHunt: activeHunt,\n activeTrial: activeTrial,\n isOnHuntSelection: isOnHuntSelection,\n isOnTrialSelection: isOnTrialSelection,\n isOnActiveTrial: isOnActiveTrial,\n activateHunt: activateHunt,\n moveToHuntSelection: moveToHuntSelection,\n moveToTrialSelectScreen: moveToTrialSelectScreen,\n moveToTrialScreen: moveToTrialScreen,\n submitAnswer: handleSubmitAnswer,\n };\n\n return (\n <QuestifyContext.Provider value={questifyCtxValue}>\n {children}\n </QuestifyContext.Provider>\n );\n}\n","type TrialComponent = React.ComponentType<any>;\n\nclass TrialRegistry {\n private registry: Map<string, TrialComponent> = new Map();\n\n register(type: string, component: TrialComponent): void {\n this.registry.set(type, component);\n }\n\n get(type: string): TrialComponent | undefined {\n return this.registry.get(type);\n }\n\n has(type: string): boolean {\n return this.registry.has(type);\n }\n}\n\nexport const trialRegistry = new TrialRegistry();\n","import { useContext } from \"react\";\nimport { QuestifyContext } from \"@store/QuestifyContext\";\nimport { trialRegistry } from \"@registry/TrialRegistry\";\n\nexport function useQuestify() {\n const context = useContext(QuestifyContext);\n\n if (!context.hunts || context.hunts.length === 0) {\n throw new Error(\"No hunts found. Make sure you pass huntConfigs to the QuestifyProvider\");\n }\n\n function renderScreen(HuntSelectComponent, TrialSelectComponent, TrialScreenComponent) {\n if (context.isOnHuntSelection()) {\n const hunts = context.hunts.map((hunt) => ({\n id: hunt.id,\n name: hunt.name,\n progressSummary: hunt.progressSummary,\n onSelectHunt: () => context.activateHunt(hunt.id)\n }));\n return <HuntSelectComponent hunts={hunts} />;\n }\n\n if (context.isOnTrialSelection()) {\n const activeHunt = context.activeHunt();\n if (!activeHunt) {\n throw new Error(\"Cannot render trial selection without an active hunt\");\n }\n\n const trials = activeHunt.trials.map((trial) => ({\n id: trial.id,\n title: trial.title,\n type: trial.type,\n isSolved: trial.isSolved,\n solution: trial.solution,\n onSelectTrial: () => context.moveToTrialScreen(trial.id)\n }));\n\n return <TrialSelectComponent\n hunt={activeHunt}\n trials={trials}\n onBack={context.moveToHuntSelection}\n />;\n }\n\n if (context.isOnActiveTrial()) {\n const trial = context.activeTrial();\n if (!trial) {\n throw new Error(\"Cannot render trial screen without an active trial\");\n }\n\n const TrialComponent = trialRegistry.get(trial.type);\n\n if (!TrialComponent) {\n throw new Error(`No trial component registered for type: ${trial.type}`);\n }\n\n return <TrialScreenComponent\n title={trial.title}\n onBack={context.moveToTrialSelectScreen}\n isSolved={trial.isSolved} \n TrialComponent={() => <TrialComponent {...trial.blueprint} title={trial.title}/>}\n />;\n }\n\n throw new Error(\"Invalid player location state\");\n }\n\n function submitAnswer(answer) {\n return context.submitAnswer(answer);\n }\n\n return {\n renderScreen: renderScreen,\n submitAnswer: submitAnswer,\n };\n}\n","import { useQuestify } from '@hooks/useQuestify';\nimport QuestifyProvider from '@store/QuestifyContext';\nimport { trialRegistry } from '@registry/TrialRegistry';\n\nexport const registerTrialComponent = (type: string, component: React.ComponentType<any>) => {\n trialRegistry.register(type, component);\n};\n\nexport { QuestifyProvider, useQuestify };\n"],"names":["storage","memoryPersister","key","value","Trial","id","type","title","solution","blueprint","isSolved","cloned","Hunt","config","trialConfig","index","name","trials","icon","subtitle","description","hunt","trialId","answer","trial","updatedTrials","solvedCount","totalTrials","status","updates","PlayerLocation","huntId","trialIndex","newLocation","trialIdx","QuestifyContext","createContext","QuestifyProvider","huntConfigs","persister","children","progressPersister","hunts","setHunts","useState","playerLocation","setPlayerLocation","useEffect","loadProgress","restoredHunts","storageKey","savedTrialsStr","restoredHunt","solvedTrialIds","activeHunt","activeTrial","isOnHuntSelection","isOnTrialSelection","isOnActiveTrial","activateHunt","moveToHuntSelection","moveToTrialSelectScreen","moveToTrialScreen","handleSubmitAnswer","currentHunt","newHunt","updatedHunts","questifyCtxValue","TrialRegistry","component","trialRegistry","useQuestify","context","useContext","renderScreen","HuntSelectComponent","TrialSelectComponent","TrialScreenComponent","jsx","TrialComponent","submitAnswer","registerTrialComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"questify.js","sources":["../lib/store/MemoryPersister.ts","../lib/models/Level.ts","../lib/models/Adventure.ts","../lib/models/PlayerLocation.ts","../lib/store/QuestifyContext.tsx","../lib/registry/LevelRegistry.ts","../lib/hooks/useQuestify.tsx","../lib/index.ts"],"sourcesContent":["const storage = {};\nexport const memoryPersister = {\n store: async (key, value) => {\n storage[key] = value;\n },\n load: async (key) => {\n return storage[key];\n },\n};\n","class Level {\n id: number;\n type: string;\n title: string;\n solution: string;\n blueprint: Record<string, any>;\n isSolved: boolean;\n\n constructor(id: number, type: string, title: string, solution: string, blueprint: Record<string, any>) {\n this.id = id;\n this.type = type;\n this.title = title;\n this.solution = solution;\n this.blueprint = blueprint;\n this.isSolved = false;\n }\n\n private clone({ isSolved }: { isSolved: boolean }): Level {\n const cloned = Object.create(Level.prototype);\n cloned.id = this.id;\n cloned.type = this.type;\n cloned.title = this.title;\n cloned.solution = this.solution;\n cloned.blueprint = this.blueprint;\n cloned.isSolved = isSolved;\n return cloned;\n }\n\n markAsSolved(): Level {\n return this.clone({ isSolved: true });\n }\n}\n\nexport default Level;\n","import Level from '@models/Level';\n\nenum AdventureStatus {\n NOT_STARTED = 'not-started',\n IN_PROGRESS = 'in-progress',\n COMPLETED = 'completed'\n}\n\ninterface LevelConfig {\n type: string;\n title: string;\n solution: string;\n blueprint: Record<string, any>;\n}\n\ninterface AdventureConfig {\n id: string;\n name: string;\n icon?: string;\n subtitle?: string;\n description?: string;\n levels: LevelConfig[];\n}\n\ninterface ProgressSummary {\n status: AdventureStatus;\n solvedCount: number;\n totalLevels: number;\n percentageComplete: number;\n}\n\nclass Adventure {\n id: string;\n name: string;\n icon?: string;\n subtitle?: string;\n description?: string;\n levels: Level[];\n progressSummary: ProgressSummary;\n\n constructor(config: AdventureConfig) {\n this.id = config.id;\n this.name = config.name;\n this.icon = config.icon;\n this.subtitle = config.subtitle;\n this.description = config.description;\n this.levels = config.levels.map((levelConfig, index) =>\n new Level(index, levelConfig.type, levelConfig.title, levelConfig.solution, levelConfig.blueprint)\n );\n this.updateStatus();\n }\n\n static constructFromLevels(id: string, name: string, levels: Level[], icon?: string, subtitle?: string, description?: string): Adventure {\n const adventure = Object.create(Adventure.prototype);\n adventure.id = id;\n adventure.name = name;\n adventure.icon = icon;\n adventure.subtitle = subtitle;\n adventure.description = description;\n adventure.levels = levels;\n adventure.updateStatus();\n return adventure;\n }\n\n submitAnswer(levelId: number, answer: string): Adventure | null {\n const level = this.levels.find(level => level.id === levelId);\n\n if (!level) {\n return null;\n }\n\n if (level.solution === answer.trim().toLowerCase()) {\n const updatedLevels = this.levels.map(level =>\n level.id === levelId ? level.markAsSolved() : level\n );\n return this.clone({ levels: updatedLevels });\n }\n\n return null;\n }\n\n private updateStatus(): void {\n const solvedCount = this.levels.filter(level => level.isSolved).length;\n const totalLevels = this.levels.length;\n\n let status: AdventureStatus;\n if (solvedCount === 0) {\n status = AdventureStatus.NOT_STARTED;\n } else if (solvedCount === totalLevels) {\n status = AdventureStatus.COMPLETED;\n } else {\n status = AdventureStatus.IN_PROGRESS;\n }\n\n this.progressSummary = {\n status,\n solvedCount,\n totalLevels,\n percentageComplete: totalLevels > 0 ? (solvedCount / totalLevels) * 100 : 0\n };\n }\n\n private clone(updates: Partial<{ levels: Level[] }>): Adventure {\n const cloned = Object.create(Adventure.prototype);\n cloned.id = this.id;\n cloned.name = this.name;\n cloned.icon = this.icon;\n cloned.subtitle = this.subtitle;\n cloned.description = this.description;\n cloned.levels = updates.levels ?? this.levels;\n cloned.updateStatus();\n return cloned;\n }\n}\n\nexport default Adventure;\nexport { Adventure, AdventureStatus, AdventureConfig, LevelConfig, ProgressSummary };\n","class PlayerLocation {\n activeAdventureId: string | null;\n activeLevelIndex: number | null;\n\n constructor(adventureId: string | null = null, levelIndex: number | null = null) {\n this.activeAdventureId = adventureId;\n this.activeLevelIndex = levelIndex;\n }\n\n private clone(): PlayerLocation {\n const cloned = Object.create(PlayerLocation.prototype);\n cloned.activeAdventureId = this.activeAdventureId;\n cloned.activeLevelIndex = this.activeLevelIndex;\n return cloned;\n }\n\n isOnAdventureSelection(): boolean {\n return this.activeAdventureId === null;\n }\n\n isOnLevelSelection(): boolean {\n return this.activeAdventureId !== null && this.activeLevelIndex === null;\n }\n\n isOnActiveLevel(): boolean {\n return this.activeAdventureId !== null && this.activeLevelIndex !== null;\n }\n\n moveToAdventureSelection(): PlayerLocation {\n const newLocation = this.clone();\n newLocation.activeAdventureId = null;\n newLocation.activeLevelIndex = null;\n return newLocation;\n }\n\n moveToAdventure(adventureId: string): PlayerLocation {\n const newLocation = this.clone();\n newLocation.activeAdventureId = adventureId;\n newLocation.activeLevelIndex = null;\n return newLocation;\n }\n\n moveToLevelSelectScreen(): PlayerLocation {\n if (this.activeAdventureId === null) {\n throw new Error('Cannot move to level select when no adventure is active');\n }\n\n const newLocation = this.clone();\n newLocation.activeLevelIndex = null;\n return newLocation;\n }\n\n moveToLevelScreen(levelIdx: number): PlayerLocation {\n if (this.activeAdventureId === null) {\n throw new Error('Cannot move to level when no adventure is active');\n }\n\n const newLocation = this.clone();\n newLocation.activeLevelIndex = levelIdx;\n return newLocation;\n }\n}\n\nexport default PlayerLocation;\n","import { useState, useEffect, createContext } from \"react\";\nimport { memoryPersister } from '@store/MemoryPersister';\nimport Adventure from '@models/Adventure';\nimport PlayerLocation from '@models/PlayerLocation';\n\nexport const QuestifyContext = createContext({\n adventures: [],\n playerLocation: null,\n activeAdventure: () => null,\n activeLevel: () => null,\n isOnAdventureSelection: () => false,\n isOnLevelSelection: () => false,\n isOnActiveLevel: () => false,\n activateAdventure: () => {},\n moveToAdventureSelection: () => {},\n moveToLevelSelectScreen: () => {},\n moveToLevelScreen: () => {},\n submitAnswer: () => {},\n});\n\nexport default function QuestifyProvider({ adventureConfigs, persister, children }) {\n const progressPersister = persister || memoryPersister;\n const [adventures, setAdventures] = useState(adventureConfigs.map(config => new Adventure(config)));\n const [playerLocation, setPlayerLocation] = useState(new PlayerLocation());\n\n useEffect(() => {\n async function loadProgress() {\n const restoredAdventures = await Promise.all(\n adventureConfigs.map(async (config) => {\n const storageKey = `questify-${config.id}-solved-levels`;\n const savedLevelsStr = await progressPersister.load(storageKey);\n const restoredAdventure = new Adventure(config);\n\n if (savedLevelsStr) {\n const solvedLevelIds = JSON.parse(savedLevelsStr);\n const updatedLevels = restoredAdventure.levels.map(level => solvedLevelIds.includes(level.id) ? level.markAsSolved() : level);\n return Adventure.constructFromLevels(config.id, config.name, updatedLevels);\n }\n\n return restoredAdventure;\n })\n );\n\n setAdventures(restoredAdventures);\n }\n\n loadProgress();\n }, [progressPersister, adventureConfigs]);\n\n function activeAdventure() {\n if (playerLocation.activeAdventureId === null) {\n return null;\n }\n return adventures.find(adventure => adventure.id === playerLocation.activeAdventureId) || null;\n }\n\n function activeLevel() {\n const adventure = activeAdventure();\n if (!adventure || playerLocation.activeLevelIndex === null) {\n return null;\n }\n return adventure.levels[playerLocation.activeLevelIndex] || null;\n }\n\n function isOnAdventureSelection() {\n return playerLocation.isOnAdventureSelection();\n }\n\n function isOnLevelSelection() {\n return playerLocation.isOnLevelSelection();\n }\n\n function isOnActiveLevel() {\n return playerLocation.isOnActiveLevel();\n }\n\n function activateAdventure(adventureId) {\n const adventure = adventures.find(adventure => adventure.id === adventureId);\n if (!adventure) {\n throw new Error(`Adventure with id \"${adventureId}\" not found`);\n }\n setPlayerLocation(playerLocation.moveToAdventure(adventureId));\n }\n\n function moveToAdventureSelection() {\n setPlayerLocation(playerLocation.moveToAdventureSelection());\n }\n\n function moveToLevelSelectScreen() {\n setPlayerLocation(playerLocation.moveToLevelSelectScreen());\n }\n\n function moveToLevelScreen(levelIdx) {\n const adventure = activeAdventure();\n if (!adventure) {\n throw new Error('Cannot move to level when no adventure is selected');\n }\n if (levelIdx < 0 || levelIdx >= adventure.levels.length) {\n throw new Error(`Level index ${levelIdx} is out of bounds for adventure \"${adventure.id}\"`);\n }\n setPlayerLocation(playerLocation.moveToLevelScreen(levelIdx));\n }\n\n async function handleSubmitAnswer(answer) {\n const level = activeLevel();\n const currentAdventure = activeAdventure();\n\n if (!level || !currentAdventure) {\n return false;\n }\n\n const newAdventure = currentAdventure.submitAnswer(level.id, answer);\n\n if (newAdventure) {\n const updatedAdventures = adventures.map(adventure => adventure.id === currentAdventure.id ? newAdventure : adventure);\n setAdventures(updatedAdventures);\n\n const storageKey = `questify-${currentAdventure.id}-solved-levels`;\n const solvedLevelIds = newAdventure.levels.filter(level => level.isSolved).map(level => level.id);\n await progressPersister.store(storageKey, JSON.stringify(solvedLevelIds));\n return true;\n }\n\n return false;\n }\n\n const questifyCtxValue = {\n adventures: adventures,\n playerLocation: playerLocation,\n activeAdventure: activeAdventure,\n activeLevel: activeLevel,\n isOnAdventureSelection: isOnAdventureSelection,\n isOnLevelSelection: isOnLevelSelection,\n isOnActiveLevel: isOnActiveLevel,\n activateAdventure: activateAdventure,\n moveToAdventureSelection: moveToAdventureSelection,\n moveToLevelSelectScreen: moveToLevelSelectScreen,\n moveToLevelScreen: moveToLevelScreen,\n submitAnswer: handleSubmitAnswer,\n };\n\n return (\n <QuestifyContext.Provider value={questifyCtxValue}>\n {children}\n </QuestifyContext.Provider>\n );\n}\n","type LevelComponent = React.ComponentType<any>;\n\nclass LevelRegistry {\n private registry: Map<string, LevelComponent> = new Map();\n\n register(type: string, component: LevelComponent): void {\n this.registry.set(type, component);\n }\n\n get(type: string): LevelComponent | undefined {\n return this.registry.get(type);\n }\n\n has(type: string): boolean {\n return this.registry.has(type);\n }\n}\n\nexport const levelRegistry = new LevelRegistry();\nexport { LevelRegistry, LevelComponent };\n","import { useContext } from \"react\";\nimport { QuestifyContext } from \"@store/QuestifyContext\";\nimport { levelRegistry } from \"@registry/LevelRegistry\";\n\nexport function useQuestify() {\n const context = useContext(QuestifyContext);\n\n if (!context.adventures || context.adventures.length === 0) {\n throw new Error(\"No adventures found. Make sure you pass adventureConfigs to the QuestifyProvider\");\n }\n\n function renderScreen(AdventureSelectComponent, LevelSelectComponent, LevelScreenComponent) {\n if (context.isOnAdventureSelection()) {\n const adventures = context.adventures.map((adventure) => ({ ...adventure, onSelectAdventure: () => context.activateAdventure(adventure.id) }));\n return <AdventureSelectComponent adventures={adventures} />;\n }\n\n if (context.isOnLevelSelection()) {\n const activeAdventure = context.activeAdventure();\n if (!activeAdventure) {\n throw new Error(\"Cannot render level selection without an active adventure\");\n }\n\n const levels = activeAdventure.levels.map((level) => ({\n id: level.id,\n title: level.title,\n type: level.type,\n isSolved: level.isSolved,\n solution: level.solution,\n onSelectLevel: () => context.moveToLevelScreen(level.id)\n }));\n\n return <LevelSelectComponent\n adventure={activeAdventure}\n levels={levels}\n onBack={context.moveToAdventureSelection}\n />;\n }\n\n if (context.isOnActiveLevel()) {\n const level = context.activeLevel();\n if (!level) {\n throw new Error(\"Cannot render level screen without an active level\");\n }\n\n const LevelComponent = levelRegistry.get(level.type);\n\n if (!LevelComponent) {\n throw new Error(`No level component registered for type: ${level.type}`);\n }\n\n return <LevelScreenComponent\n title={level.title}\n onBack={context.moveToLevelSelectScreen}\n isSolved={level.isSolved}\n LevelComponent={() => <LevelComponent {...level.blueprint} title={level.title}/>}\n />;\n }\n\n throw new Error(\"Invalid player location state\");\n }\n\n function submitAnswer(answer) {\n return context.submitAnswer(answer);\n }\n\n return {\n renderScreen: renderScreen,\n submitAnswer: submitAnswer,\n };\n}\n","import { useQuestify } from '@hooks/useQuestify';\nimport QuestifyProvider from '@store/QuestifyContext';\nimport { levelRegistry } from '@registry/LevelRegistry';\n\nexport const registerLevelComponent = (type: string, component: React.ComponentType<any>) => {\n levelRegistry.register(type, component);\n};\n\nexport { QuestifyProvider, useQuestify };\n"],"names":["storage","memoryPersister","key","value","Level","id","type","title","solution","blueprint","isSolved","cloned","Adventure","config","levelConfig","index","name","levels","icon","subtitle","description","adventure","levelId","answer","level","updatedLevels","solvedCount","totalLevels","status","updates","PlayerLocation","adventureId","levelIndex","newLocation","levelIdx","QuestifyContext","createContext","QuestifyProvider","adventureConfigs","persister","children","progressPersister","adventures","setAdventures","useState","playerLocation","setPlayerLocation","useEffect","loadProgress","restoredAdventures","storageKey","savedLevelsStr","restoredAdventure","solvedLevelIds","activeAdventure","activeLevel","isOnAdventureSelection","isOnLevelSelection","isOnActiveLevel","activateAdventure","moveToAdventureSelection","moveToLevelSelectScreen","moveToLevelScreen","handleSubmitAnswer","currentAdventure","newAdventure","updatedAdventures","questifyCtxValue","LevelRegistry","component","levelRegistry","useQuestify","context","useContext","renderScreen","AdventureSelectComponent","LevelSelectComponent","LevelScreenComponent","jsx","LevelComponent","submitAnswer","registerLevelComponent"],"mappings":"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"}
|