This commit is contained in:
2026-10-23 03:02:55 +07:00
parent e2f8a3302e
commit 1ce8efeef5
21 changed files with 561 additions and 46 deletions
+39 -10
View File
@@ -13,7 +13,9 @@ import type {
TimerState,
ScreenStep,
} from '../src/types/screen.js'
import { parsePlan } from './screen-plan.js'
import { timerLabels, validQR } from '../src/utils/presentation.js'
import { parseTeams } from './teams.js'
import { parsePlan, parseTimerType } from './screen-plan.js'
import { parseResults } from './results.js'
import type { LocalPackage } from './package-store.js'
@@ -36,10 +38,12 @@ export function parseCommand(value: unknown): Command {
else if (
c.action === 'mode' &&
Number.isInteger(c.mode) &&
number(c.mode, 1, 8) &&
[1, 2, 4, 6, 7, 8].includes(c.mode)
number(c.mode, 1, 10) &&
[1, 2, 4, 6, 7, 8, 9, 10].includes(c.mode)
)
intent = { action: 'mode', mode: c.mode as ScreenMode }
else if (c.action === 'qr' && typeof c.qrUrl === 'string' && typeof c.text === 'string' && validQR(c.qrUrl, c.text))
intent = { action: 'qr', qrUrl: c.qrUrl, text: c.text }
else if (c.action === 'step' && Number.isSafeInteger(c.index) && number(c.index, 0))
intent = { action: 'step', index: c.index }
else if (
@@ -58,8 +62,9 @@ export function parseCommand(value: unknown): Command {
)
intent = {
action: 'timer-set',
label: c.label.trim() || 'Перерыв',
label: c.label.trim() || timerLabels[parseTimerType(c.timerType)],
durationSeconds: c.durationSeconds,
timerType: parseTimerType(c.timerType),
}
else if (
typeof c.action === 'string' &&
@@ -178,6 +183,7 @@ export class Controller {
scene: { revision: 0, mode: 1, stepIndex: null, text: '', status: 'idle', error: '' },
timer: initialTimer(),
results: null,
teams: null,
channels: { main: initialChannel(), music: initialChannel() },
samples: [],
}
@@ -301,6 +307,7 @@ export class Controller {
this.state.timer = initialTimer()
this.timerDeadline = null
this.state.results = null
this.state.teams = null
this.state.channels = {
main: { ...initialChannel(), source: catalog.media[0] ?? null },
music: initialChannel(),
@@ -372,11 +379,12 @@ export class Controller {
this.broadcast()
}
}
private configureTimer(label: string, seconds: number) {
private configureTimer(label: string, seconds: number, timerType = 0) {
this.timerDeadline = null
this.state.timer = {
revision: this.state.timer.revision + 1,
label,
timerType: parseTimerType(timerType),
durationMs: seconds * 1000,
remainingMs: seconds * 1000,
status: 'stopped',
@@ -436,10 +444,17 @@ export class Controller {
markStep(index: number) {
const step = this.state.catalog.steps[index]
const scene = this.state.scene
if (!step || step.mode !== scene.mode || (step.mode === 8 && step.text !== scene.text)) return
if (
!step ||
step.mode !== scene.mode ||
([8, 9].includes(step.mode) && step.text !== scene.text) ||
(step.mode === 9 && step.qrUrl !== scene.qrUrl)
)
return
if (
step.mode === 7 &&
(step.timerLabel !== this.state.timer.label ||
((step.timerType ?? 0) !== (this.state.timer.timerType ?? 0) ||
step.timerLabel !== this.state.timer.label ||
step.timerDurationSeconds * 1000 !== this.state.timer.durationMs)
)
return
@@ -447,6 +462,15 @@ export class Controller {
scene.stepIndex = index
this.broadcast()
}
setTeams(value: unknown) {
const key = this.state.catalog.key
if (value !== null && !key) throw new Error('Сначала выберите пакет игры')
const snapshot = value === null ? null : parseTeams(value, key!)
const changed = !isDeepStrictEqual(this.state.teams?.teams, snapshot?.teams)
this.state.teams = snapshot
if (changed && this.state.scene.mode === 10) this.invalidateScene()
this.broadcast()
}
setResults(value: unknown) {
const key = this.state.catalog.key
if (value !== null && !key) throw new Error('Сначала выберите пакет игры')
@@ -487,6 +511,10 @@ export class Controller {
case 'mode':
this.mode(command.mode)
return true
case 'qr':
this.state.scene.qrUrl = command.qrUrl
this.mode(9, null, command.text)
return true
case 'text':
this.mode(8, null, command.text)
return true
@@ -494,12 +522,13 @@ export class Controller {
const step = this.state.catalog.steps[command.index]
if (!step) throw new Error('Шаг плана не найден')
if (step.mode === 7)
this.configureTimer(step.timerLabel || 'Перерыв', step.timerDurationSeconds)
this.configureTimer(step.timerLabel || timerLabels[step.timerType ?? 0], step.timerDurationSeconds, step.timerType)
this.state.scene.qrUrl = step.qrUrl ?? ''
this.mode(step.mode, command.index, step.text)
return true
}
case 'timer-set':
this.configureTimer(command.label, command.durationSeconds)
this.configureTimer(command.label, command.durationSeconds, command.timerType)
this.mode(7)
return true
case 'timer-start':
@@ -509,7 +538,7 @@ export class Controller {
this.updateTimer()
const t = this.state.timer
if (command.action === 'timer-reset') {
this.configureTimer(t.label, t.durationMs / 1000)
this.configureTimer(t.label, t.durationMs / 1000, t.timerType)
return true
}
if (command.action === 'timer-pause') {