/
voting
/
frontend
Обзор
Документация
Войти
/
voting
/
frontend
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
main
src/utils/userFlowHelpers.ts
132 строки
4 KB
TimPulin
043-wire-voting-screen-vote
19 июл 2026, 19:00
19 июл 2026, 19:00
0ca0a67
Код
Авторство
О чём код?
import type { CurrentNominationNominationDto, EventScreenType, ResultScreenType } from '../api/types' import type { Nominee } from '../types' import type { ParsedUserFlowSseEvent, SseEventData, SseScreenType, UserFlowSnapshot, UserFlowSseEnvelope } from '../types/user-flow' export interface VotingScreenNominationProps { title: string candidates: Nominee[] isClosed: boolean nominationId: string } /** Map API nomination DTO → VotingScreen UI props (no photo URL resolution). */ export function mapNominationToVotingProps( nomination: CurrentNominationNominationDto ): VotingScreenNominationProps { return { title: nomination.name, candidates: nomination.nominees.map((nominee) => ({ id: nominee.id, fullName: nominee.name, photoUrl: null })), isClosed: false, nominationId: nomination.id } } const SSE_SCREEN_TYPES = new Set<string>(['nomination', 'nominationResult', 'finalResult']) const REST_RESULT_SCREEN_TYPES = new Set<string>(['NOMINATION_RESULT', 'FINAL_RESULT']) export function isSseScreenType(value: unknown): value is SseScreenType { return typeof value === 'string' && SSE_SCREEN_TYPES.has(value) } export function isSseResultScreenType( value: SseScreenType ): value is 'nominationResult' | 'finalResult' { return value === 'nominationResult' || value === 'finalResult' } export function isResultScreenType(value: unknown): value is ResultScreenType { return typeof value === 'string' && REST_RESULT_SCREEN_TYPES.has(value) } /** Map API event.currentScreenType → SseScreenType. Returns null if unknown. */ export function mapEventScreenTypeToSseScreenType( screenType: EventScreenType | string ): SseScreenType | null { if (screenType === 'NOMINATION') return 'nomination' if (screenType === 'NOMINATION_RESULT') return 'nominationResult' if (screenType === 'FINAL_RESULT') return 'finalResult' return null } /** Map SSE screenType → REST ResultScreenType for store.resultScreenType. */ export function mapSseScreenTypeToResultScreenType( screenType: SseScreenType ): ResultScreenType | null { if (screenType === 'nominationResult') return 'NOMINATION_RESULT' if (screenType === 'finalResult') return 'FINAL_RESULT' return null } function parseSseEventData(data: unknown): SseEventData | null { if (!data || typeof data !== 'object') return null const d = data as { screenType?: unknown; nominationId?: unknown } if (!isSseScreenType(d.screenType)) return null if (d.nominationId !== null && typeof d.nominationId !== 'string') return null if (typeof d.nominationId === 'string' && d.nominationId.length === 0) { return null } return { screenType: d.screenType, nominationId: d.nominationId } } /** * Validate and narrow SSE message envelope. * Returns null for unpublished / invalid payloads (ignore in v1). */ export function parseUserFlowSseEnvelope(raw: unknown): ParsedUserFlowSseEvent | null { if (!raw || typeof raw !== 'object') return null const envelope = raw as UserFlowSseEnvelope if (typeof envelope.type !== 'string') return null if (envelope.type === 'nominationChanged') { const data = parseSseEventData(envelope.data) if (data?.screenType !== 'nomination') return null return { type: 'nominationChanged', data } } if (envelope.type === 'showResult') { const data = parseSseEventData(envelope.data) if (!data || !isSseResultScreenType(data.screenType)) return null return { type: 'showResult', data } } // votingOpened / votingClosed / eventStatusChanged — ignore in v1 return null } export type RouterScreen = SseScreenType | 'loading' /** * Decide which stub the router should render. * - nomination + active nomination → 'nomination' * - nominationResult / finalResult → same screen * - otherwise → 'loading' (hydrate, null nomination, missing snapshot, errors) */ export function resolveRouterScreen(input: { currentScreen: SseScreenType | null snapshot: UserFlowSnapshot | null isHydrating: boolean }): RouterScreen { if (input.isHydrating) return 'loading' if (input.currentScreen === 'nominationResult' || input.currentScreen === 'finalResult') { return input.currentScreen } if (input.currentScreen === 'nomination' && input.snapshot?.nomination != null) { return 'nomination' } return 'loading' }