Improve heart rate device reconnect flow

This commit is contained in:
2026-03-24 17:17:29 +08:00
parent 71ad6c6535
commit 0ccf7daf50
8 changed files with 831 additions and 72 deletions

View File

@@ -13,12 +13,14 @@ export interface AudioCueConfig {
volume: number
loop: boolean
loopGapMs: number
backgroundMode: 'disabled' | 'guidance'
}
export interface GameAudioConfig {
enabled: boolean
masterVolume: number
obeyMuteSwitch: boolean
backgroundAudioEnabled: boolean
approachDistanceMeters: number
cues: Record<AudioCueKey, AudioCueConfig>
}
@@ -28,12 +30,14 @@ export interface PartialAudioCueConfig {
volume?: number
loop?: boolean
loopGapMs?: number
backgroundMode?: 'disabled' | 'guidance'
}
export interface GameAudioConfigOverrides {
enabled?: boolean
masterVolume?: number
obeyMuteSwitch?: boolean
backgroundAudioEnabled?: boolean
approachDistanceMeters?: number
cues?: Partial<Record<AudioCueKey, PartialAudioCueConfig>>
}
@@ -42,6 +46,7 @@ export const DEFAULT_GAME_AUDIO_CONFIG: GameAudioConfig = {
enabled: true,
masterVolume: 1,
obeyMuteSwitch: true,
backgroundAudioEnabled: true,
approachDistanceMeters: 20,
cues: {
session_started: {
@@ -49,48 +54,56 @@ export const DEFAULT_GAME_AUDIO_CONFIG: GameAudioConfig = {
volume: 0.78,
loop: false,
loopGapMs: 0,
backgroundMode: 'disabled',
},
'control_completed:start': {
src: '/assets/sounds/start-complete.wav',
volume: 0.84,
loop: false,
loopGapMs: 0,
backgroundMode: 'disabled',
},
'control_completed:control': {
src: '/assets/sounds/control-complete.wav',
volume: 0.8,
loop: false,
loopGapMs: 0,
backgroundMode: 'disabled',
},
'control_completed:finish': {
src: '/assets/sounds/finish-complete.wav',
volume: 0.92,
loop: false,
loopGapMs: 0,
backgroundMode: 'disabled',
},
'punch_feedback:warning': {
src: '/assets/sounds/warning.wav',
volume: 0.72,
loop: false,
loopGapMs: 0,
backgroundMode: 'disabled',
},
'guidance:searching': {
src: '/assets/sounds/guidance-searching.wav',
volume: 0.48,
loop: true,
loopGapMs: 1800,
backgroundMode: 'guidance',
},
'guidance:approaching': {
src: '/assets/sounds/guidance-approaching.wav',
volume: 0.58,
loop: true,
loopGapMs: 950,
backgroundMode: 'guidance',
},
'guidance:ready': {
src: '/assets/sounds/guidance-ready.wav',
volume: 0.68,
loop: true,
loopGapMs: 650,
backgroundMode: 'guidance',
},
},
}
@@ -143,6 +156,10 @@ export function mergeGameAudioConfig(overrides?: GameAudioConfigOverrides | null
if (cue.loopGapMs !== undefined) {
cues[key].loopGapMs = clampGap(Number(cue.loopGapMs), cues[key].loopGapMs)
}
if (cue.backgroundMode === 'disabled' || cue.backgroundMode === 'guidance') {
cues[key].backgroundMode = cue.backgroundMode
}
}
}
@@ -150,6 +167,9 @@ export function mergeGameAudioConfig(overrides?: GameAudioConfigOverrides | null
enabled: overrides && overrides.enabled !== undefined ? !!overrides.enabled : DEFAULT_GAME_AUDIO_CONFIG.enabled,
masterVolume: clampVolume(Number(overrides && overrides.masterVolume), DEFAULT_GAME_AUDIO_CONFIG.masterVolume),
obeyMuteSwitch: overrides && overrides.obeyMuteSwitch !== undefined ? !!overrides.obeyMuteSwitch : DEFAULT_GAME_AUDIO_CONFIG.obeyMuteSwitch,
backgroundAudioEnabled: overrides && overrides.backgroundAudioEnabled !== undefined
? !!overrides.backgroundAudioEnabled
: true,
approachDistanceMeters: clampDistance(Number(overrides && overrides.approachDistanceMeters), DEFAULT_GAME_AUDIO_CONFIG.approachDistanceMeters),
cues,
}