@@ -15,6 +15,7 @@ import {
1515import {
1616 buildAdeptsQuizRelayPayload ,
1717 deriveThemesAndQuestionsFromQuizIncoming ,
18+ normalizeQuizDonations ,
1819} from "@/lib/adeptsQuizSocketRelay" ;
1920import {
2021 getAdeptsCommandSocket ,
@@ -45,6 +46,8 @@ export type GameState = {
4546 quizBoardHoverCell ?: QuizBoardHoverCell ;
4647 dataVersion ?: number ;
4748 boardRoom ?: string ;
49+ /** Пожертвования по 5 местам; общие для всех раундов квиза. */
50+ donations : ( number | null ) [ ] ;
4851} ;
4952
5053const DEFAULT_PLAYERS : Player [ ] = Array . from ( { length : 5 } , ( _ , i ) => ( {
@@ -69,16 +72,31 @@ function emptyGrid(): Pick<GameState, "themes" | "questions"> {
6972 } ;
7073}
7174
75+ const DEFAULT_DONATIONS : ( number | null ) [ ] = [ null , null , null , null , null ] ;
76+
7277const DEFAULT_CORE : Omit < GameState , "themes" | "questions" > = {
7378 activeQuizCard : null ,
7479 currentTurnSeat : 0 ,
7580 quizBoardHoverCell : null ,
7681 players : DEFAULT_PLAYERS ,
7782 dataVersion : undefined ,
7883 boardRoom : undefined ,
84+ donations : [ ...DEFAULT_DONATIONS ] ,
7985} ;
8086
8187const PLAYERS_KEY = "adepts-shared-players" ;
88+ const DONATIONS_KEY = "adepts-shared-donations" ;
89+
90+ function loadSharedDonations ( ) : ( number | null ) [ ] {
91+ try {
92+ const s = localStorage . getItem ( DONATIONS_KEY ) ;
93+ if ( ! s ) return [ ...DEFAULT_DONATIONS ] ;
94+ const parsed = JSON . parse ( s ) as unknown ;
95+ return normalizeQuizDonations ( parsed ) ?? [ ...DEFAULT_DONATIONS ] ;
96+ } catch {
97+ return [ ...DEFAULT_DONATIONS ] ;
98+ }
99+ }
82100
83101type BoardRuntime = {
84102 storageKey : string ;
@@ -327,6 +345,7 @@ function loadInitialState(boardId: AdeptsBoardId): GameState {
327345 ...DEFAULT_CORE ,
328346 ...emptyGrid ( ) ,
329347 players : rosterPlayers ,
348+ donations : loadSharedDonations ( ) ,
330349 } ) ;
331350 }
332351 }
@@ -340,11 +359,13 @@ function loadInitialState(boardId: AdeptsBoardId): GameState {
340359 ...emptyGrid ( ) ,
341360 players : rosterPlayers ,
342361 dataVersion : rt . dataVersion ,
362+ donations : loadSharedDonations ( ) ,
343363 } ) ;
344364 }
345365 return migrateCatalog ( boardId , {
346366 ...parsed ,
347367 players : rosterPlayers ,
368+ donations : normalizeQuizDonations ( parsed . donations ) ?? loadSharedDonations ( ) ,
348369 activeQuizCard : parsed . activeQuizCard ?? null ,
349370 currentTurnSeat : Number . isInteger ( parsed . currentTurnSeat )
350371 ? ( ( Number ( parsed . currentTurnSeat ) % 5 ) + 5 ) % 5
@@ -366,12 +387,14 @@ function loadInitialState(boardId: AdeptsBoardId): GameState {
366387 ...emptyGrid ( ) ,
367388 players : rosterPlayers ,
368389 dataVersion : rt . dataVersion ,
390+ donations : loadSharedDonations ( ) ,
369391 } ) ;
370392 }
371393 return migrateCatalog ( boardId , {
372394 ...DEFAULT_CORE ,
373395 ...emptyGrid ( ) ,
374396 players : rosterPlayers ,
397+ donations : loadSharedDonations ( ) ,
375398 } ) ;
376399}
377400
@@ -457,6 +480,7 @@ export function useGameState(boardId: AdeptsBoardId) {
457480 boardId : rec [ "boardId" ] ,
458481 players : rec [ "players" ] ,
459482 currentTurnSeat : rec [ "currentTurnSeat" ] ,
483+ donations : rec [ "donations" ] ,
460484 // Reset all board-specific fields to safe defaults
461485 activeQuizCard : null ,
462486 quizBoardHoverCell : null ,
@@ -501,13 +525,17 @@ export function useGameState(boardId: AdeptsBoardId) {
501525 hoverFromRelay = null ;
502526 }
503527
528+ const incomingDonations = normalizeQuizDonations ( recToUse [ "donations" ] ) ;
529+ const nextDonations = incomingDonations ?? prev . donations ;
530+
504531 let nextState = withClosedActiveQuizIfCellUsed (
505532 migrateCatalog ( boardId , {
506533 ...prev ,
507534 boardRoom : typeof recToUse [ "boardRoom" ] === "string" ? recToUse [ "boardRoom" ] : prev . boardRoom ,
508535 themes,
509536 questions,
510537 players : merged ,
538+ donations : nextDonations ,
511539 activeQuizCard : rawCard ,
512540 // Relay is authoritative: never let host+Roster merge stomp server `currentTurnSeat`.
513541 currentTurnSeat :
@@ -568,6 +596,7 @@ export function useGameState(boardId: AdeptsBoardId) {
568596 useEffect ( ( ) => {
569597 localStorage . setItem ( rt . storageKey , JSON . stringify ( state ) ) ;
570598 localStorage . setItem ( PLAYERS_KEY , JSON . stringify ( state . players ) ) ;
599+ localStorage . setItem ( DONATIONS_KEY , JSON . stringify ( state . donations ) ) ;
571600
572601 /** Consume before `catalogReady` / `allowQuizPush` returns — otherwise skip stays true and a later `hostQuizRelay` can stomp relay (host pick then no modal). */
573602 const skipThisCommit = skipEmitRef . current ;
@@ -601,6 +630,15 @@ export function useGameState(boardId: AdeptsBoardId) {
601630 setState ( ( prev ) => ( { ...prev , players } ) ) ;
602631 } catch { }
603632 }
633+ if ( e . key === DONATIONS_KEY && e . newValue ) {
634+ try {
635+ const d = normalizeQuizDonations ( JSON . parse ( e . newValue ) ) ;
636+ if ( d ) {
637+ skipEmitRef . current = true ;
638+ setState ( ( prev ) => ( { ...prev , donations : d } ) ) ;
639+ }
640+ } catch { }
641+ }
604642 } ;
605643 window . addEventListener ( "storage" , handler ) ;
606644 return ( ) => window . removeEventListener ( "storage" , handler ) ;
@@ -768,6 +806,8 @@ export function useGameState(boardId: AdeptsBoardId) {
768806 players : DEFAULT_PLAYERS . map ( ( p ) => ( { ...p } ) ) ,
769807 dataVersion : rt . dataVersion ,
770808 boardRoom : getAdeptsSessionId ( ) ,
809+ donations :
810+ prev . donations ?. length === 5 ? [ ...prev . donations ] : [ ...DEFAULT_DONATIONS ] ,
771811 } )
772812 ) ;
773813 } , [ boardId , rt . dataVersion ] ) ;
0 commit comments