@popp0102/questify 0.9.2 → 0.9.4
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 +30 -28
- package/dist/questify.js.map +1 -1
- package/package.json +1 -1
package/dist/questify.js
CHANGED
|
@@ -7,8 +7,8 @@ const g = {}, F = {
|
|
|
7
7
|
load: async (i) => g[i]
|
|
8
8
|
};
|
|
9
9
|
class y {
|
|
10
|
-
constructor(t, e, r, s,
|
|
11
|
-
this.id = t, this.type = e, this.title = r, this.solution = s, this.blueprint =
|
|
10
|
+
constructor(t, e, r, s, u) {
|
|
11
|
+
this.id = t, this.type = e, this.title = r, this.solution = s, this.blueprint = u, this.isSolved = !1;
|
|
12
12
|
}
|
|
13
13
|
clone({ isSolved: t }) {
|
|
14
14
|
const e = Object.create(y.prototype);
|
|
@@ -34,7 +34,7 @@ class h {
|
|
|
34
34
|
return null;
|
|
35
35
|
if (r.solution === e.trim().toLowerCase()) {
|
|
36
36
|
const s = this.trials.map(
|
|
37
|
-
(
|
|
37
|
+
(u) => u.id === t ? u.markAsSolved() : u
|
|
38
38
|
);
|
|
39
39
|
return this.clone({ trials: s });
|
|
40
40
|
}
|
|
@@ -113,28 +113,28 @@ const b = Q({
|
|
|
113
113
|
}
|
|
114
114
|
});
|
|
115
115
|
function R({ huntConfigs: i, persister: t, children: e }) {
|
|
116
|
-
const r = t || F, [s,
|
|
116
|
+
const r = t || F, [s, u] = I(i.map((o) => new h(o))), [n, l] = I(new H());
|
|
117
117
|
q(() => {
|
|
118
118
|
async function o() {
|
|
119
119
|
const d = await Promise.all(
|
|
120
|
-
i.map(async (
|
|
121
|
-
const m = `questify-${
|
|
120
|
+
i.map(async (c) => {
|
|
121
|
+
const m = `questify-${c.id}-solved-trials`, p = await r.load(m), S = new h(c);
|
|
122
122
|
if (p) {
|
|
123
123
|
const f = JSON.parse(p), v = S.trials.map((w) => f.includes(w.id) ? w.markAsSolved() : w);
|
|
124
|
-
return h.constructFromTrials(
|
|
124
|
+
return h.constructFromTrials(c.id, c.name, v);
|
|
125
125
|
}
|
|
126
126
|
return S;
|
|
127
127
|
})
|
|
128
128
|
);
|
|
129
|
-
|
|
129
|
+
u(d);
|
|
130
130
|
}
|
|
131
131
|
o();
|
|
132
132
|
}, [r, i]);
|
|
133
|
-
function
|
|
133
|
+
function a() {
|
|
134
134
|
return n.activeHuntId === null ? null : s.find((o) => o.id === n.activeHuntId) || null;
|
|
135
135
|
}
|
|
136
136
|
function O() {
|
|
137
|
-
const o =
|
|
137
|
+
const o = a();
|
|
138
138
|
return !o || n.activeTrialIndex === null ? null : o.trials[n.activeTrialIndex] || null;
|
|
139
139
|
}
|
|
140
140
|
function A() {
|
|
@@ -147,7 +147,7 @@ function R({ huntConfigs: i, persister: t, children: e }) {
|
|
|
147
147
|
return n.isOnActiveTrial();
|
|
148
148
|
}
|
|
149
149
|
function L(o) {
|
|
150
|
-
if (!s.find((
|
|
150
|
+
if (!s.find((c) => c.id === o))
|
|
151
151
|
throw new Error(`Hunt with id "${o}" not found`);
|
|
152
152
|
l(n.moveToHunt(o));
|
|
153
153
|
}
|
|
@@ -158,7 +158,7 @@ function R({ huntConfigs: i, persister: t, children: e }) {
|
|
|
158
158
|
l(n.moveToTrialSelectScreen());
|
|
159
159
|
}
|
|
160
160
|
function $(o) {
|
|
161
|
-
const d =
|
|
161
|
+
const d = a();
|
|
162
162
|
if (!d)
|
|
163
163
|
throw new Error("Cannot move to trial when no hunt is selected");
|
|
164
164
|
if (o < 0 || o >= d.trials.length)
|
|
@@ -166,14 +166,14 @@ function R({ huntConfigs: i, persister: t, children: e }) {
|
|
|
166
166
|
l(n.moveToTrialScreen(o));
|
|
167
167
|
}
|
|
168
168
|
async function j(o) {
|
|
169
|
-
const d = O(),
|
|
170
|
-
if (!d || !
|
|
169
|
+
const d = O(), c = a();
|
|
170
|
+
if (!d || !c)
|
|
171
171
|
return !1;
|
|
172
|
-
const m =
|
|
172
|
+
const m = c.submitAnswer(d.id, o);
|
|
173
173
|
if (m) {
|
|
174
|
-
const p = s.map((v) => v.id ===
|
|
175
|
-
|
|
176
|
-
const S = `questify-${
|
|
174
|
+
const p = s.map((v) => v.id === c.id ? m : v);
|
|
175
|
+
u(p);
|
|
176
|
+
const S = `questify-${c.id}-solved-trials`, f = m.trials.filter((v) => v.isSolved).map((v) => v.id);
|
|
177
177
|
return await r.store(S, JSON.stringify(f)), !0;
|
|
178
178
|
}
|
|
179
179
|
return !1;
|
|
@@ -181,7 +181,7 @@ function R({ huntConfigs: i, persister: t, children: e }) {
|
|
|
181
181
|
const N = {
|
|
182
182
|
hunts: s,
|
|
183
183
|
playerLocation: n,
|
|
184
|
-
activeHunt:
|
|
184
|
+
activeHunt: a,
|
|
185
185
|
activeTrial: O,
|
|
186
186
|
isOnHuntSelection: A,
|
|
187
187
|
isOnTrialSelection: C,
|
|
@@ -213,7 +213,7 @@ function V() {
|
|
|
213
213
|
const i = B(b);
|
|
214
214
|
if (!i.hunts || i.hunts.length === 0)
|
|
215
215
|
throw new Error("No hunts found. Make sure you pass huntConfigs to the QuestifyProvider");
|
|
216
|
-
function t(r, s,
|
|
216
|
+
function t(r, s, u) {
|
|
217
217
|
if (i.isOnHuntSelection()) {
|
|
218
218
|
const n = i.hunts.map((l) => ({
|
|
219
219
|
id: l.id,
|
|
@@ -227,12 +227,13 @@ function V() {
|
|
|
227
227
|
const n = i.activeHunt();
|
|
228
228
|
if (!n)
|
|
229
229
|
throw new Error("Cannot render trial selection without an active hunt");
|
|
230
|
-
const l = n.trials.map((
|
|
231
|
-
id:
|
|
232
|
-
title:
|
|
233
|
-
type:
|
|
234
|
-
isSolved:
|
|
235
|
-
|
|
230
|
+
const l = n.trials.map((a) => ({
|
|
231
|
+
id: a.id,
|
|
232
|
+
title: a.title,
|
|
233
|
+
type: a.type,
|
|
234
|
+
isSolved: a.isSolved,
|
|
235
|
+
solution: a.solution,
|
|
236
|
+
onSelectTrial: () => i.moveToTrialScreen(a.id)
|
|
236
237
|
}));
|
|
237
238
|
return /* @__PURE__ */ T(
|
|
238
239
|
s,
|
|
@@ -251,11 +252,12 @@ function V() {
|
|
|
251
252
|
if (!l)
|
|
252
253
|
throw new Error(`No trial component registered for type: ${n.type}`);
|
|
253
254
|
return /* @__PURE__ */ T(
|
|
254
|
-
|
|
255
|
+
u,
|
|
255
256
|
{
|
|
256
257
|
title: n.title,
|
|
257
258
|
onBack: i.moveToTrialSelectScreen,
|
|
258
|
-
|
|
259
|
+
isSolved: n.isSolved,
|
|
260
|
+
TrialComponent: () => /* @__PURE__ */ T(l, { ...n.blueprint, title: n.title })
|
|
259
261
|
}
|
|
260
262
|
);
|
|
261
263
|
}
|
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 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 trials: Trial[];\n progressSummary: ProgressSummary;\n\n constructor(config: HuntConfig) {\n this.id = config.id;\n this.name = config.name;\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[]): Hunt {\n const hunt = Object.create(Hunt.prototype);\n hunt.id = id;\n hunt.name = name;\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.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 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 TrialComponent={() => <TrialComponent {...trial.blueprint} title={trial.title} solved={trial.isSolved} />}\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","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/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 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 trials: Trial[];\n progressSummary: ProgressSummary;\n\n constructor(config: HuntConfig) {\n this.id = config.id;\n this.name = config.name;\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[]): Hunt {\n const hunt = Object.create(Hunt.prototype);\n hunt.id = id;\n hunt.name = name;\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.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","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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"}
|