Skip to content

Commit a543f37

Browse files
committed
feat(db): extend BuzzEvent and Game with buzzer config fields, add v4 migration
- BuzzEvent gains: playerName, teamId, isFalseStart, gmDecision, decidedAt - Game gains: autoLockOnFirstCorrect, allowFalseStarts, buzzDeduplication, tiebreakerMode - v4 upgrade back-fills new fields on existing records - Export GmDecision, BuzzDeduplication, TiebreakerMode types Closes #37
1 parent b2ac272 commit a543f37

1 file changed

Lines changed: 62 additions & 4 deletions

File tree

src/db/index.ts

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ export type QuestionType = 'multiple_choice' | 'true_false' | 'open_ended'
66
export type Difficulty = 'easy' | 'medium' | 'hard' | string
77
export type GameStatus = 'waiting' | 'active' | 'paused' | 'ended'
88
export type TransportMode = 'auto' | 'peer' | 'gun'
9+
export type GmDecision = 'Correct' | 'Incorrect' | 'Skip'
10+
export type BuzzDeduplication = 'firstOnly' | 'all'
11+
export type TiebreakerMode = 'serverOrder'
912
export type WidgetType =
1013
| 'buzzer'
1114
| 'question'
@@ -68,17 +71,27 @@ export interface Game {
6871
transportMode: TransportMode
6972
roomId: string | null
7073
passphrase: string | null // Gun.js SEA encryption
71-
scoringEnabled: boolean
74+
// Visibility
7275
showQuestion: boolean
7376
showAnswers: boolean
7477
showMedia: boolean
78+
// Players / teams
7579
maxTeams: number // 0 = unlimited
7680
maxPerTeam: number // 0 = unlimited
7781
allowIndividual: boolean
82+
// Rounds / navigation
7883
roundIds: string[]
7984
currentRoundIdx: number
8085
currentQuestionIdx: number
86+
// Buzzer state
8187
buzzerLocked: boolean
88+
// Buzzer configuration
89+
scoringEnabled: boolean
90+
autoLockOnFirstCorrect: boolean
91+
allowFalseStarts: boolean
92+
buzzDeduplication: BuzzDeduplication
93+
tiebreakerMode: TiebreakerMode
94+
// Timestamps
8295
createdAt: number
8396
updatedAt: number
8497
}
@@ -105,10 +118,16 @@ export interface Player {
105118
export interface BuzzEvent {
106119
id: string
107120
gameId: string
108-
playerId: string
109121
questionId: string
110-
timestamp: number // microsecond precision
111-
correct: boolean | null
122+
playerId: string
123+
playerName: string
124+
teamId: string | null
125+
/** microsecond precision via performance.now() offset from Date.now() */
126+
timestamp: number
127+
/** true when buzz arrived before buzzerLocked was cleared */
128+
isFalseStart: boolean
129+
gmDecision: GmDecision | null
130+
decidedAt: number | null
112131
}
113132

114133
export interface Layout {
@@ -222,6 +241,45 @@ class ViktoraniDB extends Dexie {
222241
delete q['categoryId']
223242
})
224243
})
244+
245+
// v4: full BuzzEvent schema + buzzer config back-fill on Game records
246+
this.version(4)
247+
.stores({
248+
difficulties: 'id, name, order',
249+
tags: 'id, name',
250+
questions: 'id, difficulty, type, createdAt',
251+
rounds: 'id, createdAt',
252+
games: 'id, status, createdAt',
253+
teams: 'id, gameId',
254+
players: 'id, gameId, teamId, deviceId',
255+
buzzEvents: 'id, gameId, playerId, questionId, timestamp',
256+
layouts: 'id, gameId, target',
257+
widgets: 'id, layoutId, order',
258+
notes: 'id, name, createdAt, updatedAt',
259+
timers: 'id, gameId',
260+
gameQuestions: 'id, gameId, roundId, order',
261+
})
262+
.upgrade(async tx => {
263+
await tx
264+
.table('games')
265+
.toCollection()
266+
.modify((g: Record<string, unknown>) => {
267+
if (g['autoLockOnFirstCorrect'] === undefined) g['autoLockOnFirstCorrect'] = false
268+
if (g['allowFalseStarts'] === undefined) g['allowFalseStarts'] = false
269+
if (g['buzzDeduplication'] === undefined) g['buzzDeduplication'] = 'firstOnly'
270+
if (g['tiebreakerMode'] === undefined) g['tiebreakerMode'] = 'serverOrder'
271+
})
272+
await tx
273+
.table('buzzEvents')
274+
.toCollection()
275+
.modify((b: Record<string, unknown>) => {
276+
if (b['playerName'] === undefined) b['playerName'] = 'Unknown'
277+
if (b['teamId'] === undefined) b['teamId'] = null
278+
if (b['isFalseStart'] === undefined) b['isFalseStart'] = false
279+
if (b['gmDecision'] === undefined) b['gmDecision'] = null
280+
if (b['decidedAt'] === undefined) b['decidedAt'] = null
281+
})
282+
})
225283
}
226284
}
227285

0 commit comments

Comments
 (0)