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