usegamigameapi 1.0.14 → 1.0.15
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 +99 -66
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# useGameAPI
|
|
2
2
|
|
|
3
|
-
A React hook for managing interactive quiz/game flows through iframe
|
|
3
|
+
A React hook for managing interactive quiz/game flows through iframe
|
|
4
|
+
communication using postMessage. This hook handles question flow, answer
|
|
5
|
+
submission, result tracking, and seamless communication between a parent window
|
|
6
|
+
and an iframe.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
@@ -15,25 +18,26 @@ npm install usegamigameapi
|
|
|
15
18
|
## Basic Usage
|
|
16
19
|
|
|
17
20
|
```tsx
|
|
18
|
-
import { useGameAPI } from
|
|
21
|
+
import { useGameAPI } from "usegamigameapi";
|
|
19
22
|
|
|
20
23
|
function QuizComponent() {
|
|
21
24
|
const {
|
|
22
25
|
quiz,
|
|
26
|
+
username,
|
|
23
27
|
selectedAnswer,
|
|
24
28
|
handleAnswerSelect,
|
|
25
29
|
updateAnswer,
|
|
26
30
|
handleContinue,
|
|
27
31
|
isCompleted,
|
|
28
|
-
currentResult
|
|
32
|
+
currentResult,
|
|
29
33
|
} = useGameAPI({
|
|
30
34
|
onAnswerCorrect: ({ currentQuestionIndex }) => {
|
|
31
|
-
console.log(
|
|
35
|
+
console.log("Correct answer at question", currentQuestionIndex);
|
|
32
36
|
// Trigger confetti or other celebrations
|
|
33
37
|
},
|
|
34
38
|
onAnswerIncorrect: () => {
|
|
35
|
-
console.log(
|
|
36
|
-
}
|
|
39
|
+
console.log("Incorrect answer");
|
|
40
|
+
},
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
// Render your quiz UI using the returned state and methods
|
|
@@ -46,9 +50,9 @@ function QuizComponent() {
|
|
|
46
50
|
|
|
47
51
|
The `useGameAPI` hook accepts an object with the following optional callbacks:
|
|
48
52
|
|
|
49
|
-
| Parameter
|
|
50
|
-
|
|
51
|
-
| `onAnswerCorrect`
|
|
53
|
+
| Parameter | Type | Description |
|
|
54
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
|
55
|
+
| `onAnswerCorrect` | `(params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void` | Callback fired when the user submits a correct answer |
|
|
52
56
|
| `onAnswerIncorrect` | `(params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void` | Callback fired when the user submits an incorrect answer |
|
|
53
57
|
|
|
54
58
|
### Return Values
|
|
@@ -57,42 +61,44 @@ The hook returns an object with the following properties:
|
|
|
57
61
|
|
|
58
62
|
#### State Data
|
|
59
63
|
|
|
60
|
-
| Property
|
|
61
|
-
|
|
62
|
-
| `quiz`
|
|
63
|
-
| `currentResult`
|
|
64
|
-
| `answers`
|
|
65
|
-
| `correctCount`
|
|
66
|
-
| `currentQuestionIndex` | `number`
|
|
64
|
+
| Property | Type | Description |
|
|
65
|
+
| ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| `quiz` | `any` | The current question data received from the parent window (contains text, audio URL, answers array, etc.) |
|
|
67
|
+
| `currentResult` | `any` | The result object after submitting an answer (contains `isCorrect`, `isLastQuestion`, `correctAnswerId`, `incorrectAnswerId`, and explanation) |
|
|
68
|
+
| `answers` | `(boolean \| null)[]` | Array of answer history where each index corresponds to a question (true = correct, false = incorrect, null = unanswered) |
|
|
69
|
+
| `correctCount` | `number` | Total count of correctly answered questions |
|
|
70
|
+
| `currentQuestionIndex` | `number` | Zero-based index of the current question |
|
|
71
|
+
| `username` | `string` | The username received from the parent window during initialization |
|
|
67
72
|
|
|
68
73
|
#### State Status
|
|
69
74
|
|
|
70
|
-
| Property
|
|
71
|
-
|
|
72
|
-
| `selectedAnswer` | `{ id: number; content: string } \| null` | The currently selected answer object
|
|
73
|
-
| `isSubmitting`
|
|
74
|
-
| `hasSubmitted`
|
|
75
|
-
| `isCompleted`
|
|
75
|
+
| Property | Type | Description |
|
|
76
|
+
| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
77
|
+
| `selectedAnswer` | `{ id: number; content: string } \| null` | The currently selected answer object |
|
|
78
|
+
| `isSubmitting` | `boolean` | Indicates whether an answer submission is in progress |
|
|
79
|
+
| `hasSubmitted` | `boolean` | Indicates whether the current question has been submitted and is waiting for the Continue action |
|
|
80
|
+
| `isCompleted` | `boolean` | Indicates whether all questions have been completed |
|
|
76
81
|
|
|
77
82
|
#### Methods
|
|
78
83
|
|
|
79
|
-
| Method
|
|
80
|
-
|
|
81
|
-
| `handleAnswerSelect` | `(answer: { id: number; content: string }) => void` | Selects an answer for the current question. Only works if not submitting and not already submitted
|
|
82
|
-
| `updateAnswer`
|
|
83
|
-
| `handleContinue`
|
|
84
|
-
| `finish`
|
|
84
|
+
| Method | Type | Description |
|
|
85
|
+
| -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `handleAnswerSelect` | `(answer: { id: number; content: string }) => void` | Selects an answer for the current question. Only works if not submitting and not already submitted |
|
|
87
|
+
| `updateAnswer` | `() => Promise<void>` | Submits the selected answer to the parent window. Returns a promise that resolves immediately |
|
|
88
|
+
| `handleContinue` | `() => void` | Moves to the next question or marks the quiz as completed if it's the last question. Only works after an answer has been submitted |
|
|
89
|
+
| `finish` | `() => void` | Sends a finish signal to the parent window (typically used to resize iframe) |
|
|
85
90
|
|
|
86
91
|
## Usage Examples
|
|
87
92
|
|
|
88
93
|
### Complete Quiz Flow Example
|
|
89
94
|
|
|
90
95
|
```tsx
|
|
91
|
-
import { useGameAPI } from
|
|
96
|
+
import { useGameAPI } from "usegamigameapi";
|
|
92
97
|
|
|
93
98
|
function GameQuiz() {
|
|
94
99
|
const {
|
|
95
100
|
quiz,
|
|
101
|
+
username,
|
|
96
102
|
selectedAnswer,
|
|
97
103
|
currentResult,
|
|
98
104
|
answers,
|
|
@@ -104,16 +110,22 @@ function GameQuiz() {
|
|
|
104
110
|
handleAnswerSelect,
|
|
105
111
|
updateAnswer,
|
|
106
112
|
handleContinue,
|
|
107
|
-
finish
|
|
113
|
+
finish,
|
|
108
114
|
} = useGameAPI({
|
|
109
115
|
onAnswerCorrect: ({ currentQuestionIndex, correctAnswerId }) => {
|
|
110
116
|
// Show success animation or confetti
|
|
111
|
-
console.log(`Question ${currentQuestionIndex + 1} answered correctly!`, {
|
|
117
|
+
console.log(`Question ${currentQuestionIndex + 1} answered correctly!`, {
|
|
118
|
+
correctAnswerId,
|
|
119
|
+
});
|
|
112
120
|
},
|
|
113
|
-
onAnswerIncorrect: ({
|
|
121
|
+
onAnswerIncorrect: ({
|
|
122
|
+
currentQuestionIndex,
|
|
123
|
+
correctAnswerId,
|
|
124
|
+
incorrectAnswerId,
|
|
125
|
+
}) => {
|
|
114
126
|
// Show error feedback
|
|
115
|
-
console.log(
|
|
116
|
-
}
|
|
127
|
+
console.log("Incorrect answer", { correctAnswerId, incorrectAnswerId });
|
|
128
|
+
},
|
|
117
129
|
});
|
|
118
130
|
|
|
119
131
|
// Show loading state while waiting for first question
|
|
@@ -126,7 +138,9 @@ function GameQuiz() {
|
|
|
126
138
|
return (
|
|
127
139
|
<div>
|
|
128
140
|
<h2>Quiz Completed!</h2>
|
|
129
|
-
<p>
|
|
141
|
+
<p>
|
|
142
|
+
You got {correctCount} out of {answers.length} questions correct.
|
|
143
|
+
</p>
|
|
130
144
|
<button onClick={finish}>Finish</button>
|
|
131
145
|
</div>
|
|
132
146
|
);
|
|
@@ -137,14 +151,15 @@ function GameQuiz() {
|
|
|
137
151
|
{/* Progress bar */}
|
|
138
152
|
<div>
|
|
139
153
|
Question {currentQuestionIndex + 1} of {answers.length + 1}
|
|
140
|
-
<div style={{ display:
|
|
154
|
+
<div style={{ display: "flex", gap: "4px" }}>
|
|
141
155
|
{answers.map((answer, idx) => (
|
|
142
156
|
<div
|
|
143
157
|
key={idx}
|
|
144
158
|
style={{
|
|
145
|
-
width:
|
|
146
|
-
height:
|
|
147
|
-
backgroundColor:
|
|
159
|
+
width: "20px",
|
|
160
|
+
height: "4px",
|
|
161
|
+
backgroundColor:
|
|
162
|
+
answer === true ? "green" : answer === false ? "red" : "gray",
|
|
148
163
|
}}
|
|
149
164
|
/>
|
|
150
165
|
))}
|
|
@@ -163,10 +178,10 @@ function GameQuiz() {
|
|
|
163
178
|
onClick={() => handleAnswerSelect(answer)}
|
|
164
179
|
disabled={isSubmitting || hasSubmitted}
|
|
165
180
|
style={{
|
|
166
|
-
backgroundColor:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
backgroundColor:
|
|
182
|
+
selectedAnswer?.id === answer.id ? "#007bff" : "#f0f0f0",
|
|
183
|
+
opacity: hasSubmitted ? 0.6 : 1,
|
|
184
|
+
}}>
|
|
170
185
|
{answer.content}
|
|
171
186
|
</button>
|
|
172
187
|
))}
|
|
@@ -176,8 +191,7 @@ function GameQuiz() {
|
|
|
176
191
|
{!hasSubmitted && (
|
|
177
192
|
<button
|
|
178
193
|
onClick={updateAnswer}
|
|
179
|
-
disabled={!selectedAnswer || isSubmitting}
|
|
180
|
-
>
|
|
194
|
+
disabled={!selectedAnswer || isSubmitting}>
|
|
181
195
|
Submit Answer
|
|
182
196
|
</button>
|
|
183
197
|
)}
|
|
@@ -185,12 +199,10 @@ function GameQuiz() {
|
|
|
185
199
|
{/* Result display and Continue button */}
|
|
186
200
|
{hasSubmitted && currentResult && (
|
|
187
201
|
<div>
|
|
188
|
-
<p>
|
|
189
|
-
{currentResult.isCorrect ? '✓ Correct!' : '✗ Incorrect'}
|
|
190
|
-
</p>
|
|
202
|
+
<p>{currentResult.isCorrect ? "✓ Correct!" : "✗ Incorrect"}</p>
|
|
191
203
|
{currentResult.explanation && <p>{currentResult.explanation}</p>}
|
|
192
204
|
<button onClick={handleContinue}>
|
|
193
|
-
{currentResult.isLastQuestion ?
|
|
205
|
+
{currentResult.isLastQuestion ? "Finish Quiz" : "Next Question"}
|
|
194
206
|
</button>
|
|
195
207
|
</div>
|
|
196
208
|
)}
|
|
@@ -205,10 +217,17 @@ function GameQuiz() {
|
|
|
205
217
|
### Minimal Example
|
|
206
218
|
|
|
207
219
|
```tsx
|
|
208
|
-
import { useGameAPI } from
|
|
220
|
+
import { useGameAPI } from "usegamigameapi";
|
|
209
221
|
|
|
210
222
|
function SimpleQuiz() {
|
|
211
|
-
const {
|
|
223
|
+
const {
|
|
224
|
+
quiz,
|
|
225
|
+
handleAnswerSelect,
|
|
226
|
+
updateAnswer,
|
|
227
|
+
handleContinue,
|
|
228
|
+
selectedAnswer,
|
|
229
|
+
hasSubmitted,
|
|
230
|
+
} = useGameAPI();
|
|
212
231
|
|
|
213
232
|
return (
|
|
214
233
|
<div>
|
|
@@ -217,8 +236,7 @@ function SimpleQuiz() {
|
|
|
217
236
|
<button
|
|
218
237
|
key={answer.id}
|
|
219
238
|
onClick={() => handleAnswerSelect(answer)}
|
|
220
|
-
disabled={hasSubmitted}
|
|
221
|
-
>
|
|
239
|
+
disabled={hasSubmitted}>
|
|
222
240
|
{answer.content}
|
|
223
241
|
</button>
|
|
224
242
|
))}
|
|
@@ -227,9 +245,7 @@ function SimpleQuiz() {
|
|
|
227
245
|
Submit
|
|
228
246
|
</button>
|
|
229
247
|
)}
|
|
230
|
-
{hasSubmitted &&
|
|
231
|
-
<button onClick={handleContinue}>Continue</button>
|
|
232
|
-
)}
|
|
248
|
+
{hasSubmitted && <button onClick={handleContinue}>Continue</button>}
|
|
233
249
|
</div>
|
|
234
250
|
);
|
|
235
251
|
}
|
|
@@ -237,21 +253,34 @@ function SimpleQuiz() {
|
|
|
237
253
|
|
|
238
254
|
## Architecture
|
|
239
255
|
|
|
240
|
-
This hook uses a **bridge pattern** to facilitate communication between a parent
|
|
256
|
+
This hook uses a **bridge pattern** to facilitate communication between a parent
|
|
257
|
+
window (Main Web) and a child iframe (Sub Web) through `window.postMessage`. The
|
|
258
|
+
communication flow works as follows:
|
|
241
259
|
|
|
242
260
|
### Communication Flow
|
|
243
261
|
|
|
244
|
-
1. **Initialization**: When the hook mounts, it
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
262
|
+
1. **Initialization**: When the hook mounts, it listens for `INIT` message from
|
|
263
|
+
the parent window containing game information (including `username`)
|
|
264
|
+
2. **Start Game**: After initialization, the hook sends `SHOW_LO5` to request
|
|
265
|
+
the first question
|
|
266
|
+
3. **Question Received**: Parent responds with `RETURN_LO5` containing question
|
|
267
|
+
data
|
|
268
|
+
4. **Answer Submission**: User selects an answer and calls `updateAnswer()`,
|
|
269
|
+
which sends `UPDATE_ANSWER` with the answer ID
|
|
270
|
+
5. **Result Received**: Parent responds with `RETURN_UPDATE_ANSWER` containing
|
|
271
|
+
`isCorrect`, `isLastQuestion`, `correctAnswerId`, `incorrectAnswerId`, and
|
|
272
|
+
result data
|
|
273
|
+
6. **Continue**: User calls `handleContinue()` which either requests the next
|
|
274
|
+
question (`SHOW_LO5`) or marks the quiz as completed
|
|
275
|
+
7. **Finish**: When complete, `finish()` sends `FINISH` to signal completion
|
|
276
|
+
(typically for iframe resizing)
|
|
250
277
|
|
|
251
278
|
### Action Types
|
|
252
279
|
|
|
253
280
|
The bridge uses the following action constants (defined internally):
|
|
254
281
|
|
|
282
|
+
- `INIT`: Main → Sub - Initialize game with user information (includes
|
|
283
|
+
`username`)
|
|
255
284
|
- `SHOW_LO5`: Sub → Main - Request a question
|
|
256
285
|
- `RETURN_LO5`: Main → Sub - Send question data
|
|
257
286
|
- `UPDATE_ANSWER`: Sub → Main - Submit selected answer
|
|
@@ -260,11 +289,15 @@ The bridge uses the following action constants (defined internally):
|
|
|
260
289
|
|
|
261
290
|
## Notes
|
|
262
291
|
|
|
263
|
-
- The hook automatically
|
|
292
|
+
- The hook automatically listens for `INIT` message from the parent window when
|
|
293
|
+
mounted to receive game initialization data (including username)
|
|
294
|
+
- The hook automatically requests the first question after initialization
|
|
264
295
|
- Answer history is tracked in the `answers` array, indexed by question number
|
|
265
|
-
- The hook uses both `useState` and `useRef` to ensure correct closure behavior
|
|
296
|
+
- The hook uses both `useState` and `useRef` to ensure correct closure behavior
|
|
297
|
+
across renders
|
|
266
298
|
- All communication is asynchronous through postMessage events
|
|
267
|
-
- Make sure your parent window is set up to
|
|
299
|
+
- Make sure your parent window is set up to send `INIT` message with game data
|
|
300
|
+
and handle the corresponding postMessage events
|
|
268
301
|
|
|
269
302
|
## License
|
|
270
303
|
|