@@ -16,8 +16,22 @@ import {
1616 MetaInfo ,
1717 MetaInfoWithTimestamp ,
1818 PlayerWithTimestamp ,
19+ ScoreboardState as ScoreboardHubState ,
1920} from "../types" ;
2021
22+ type ScoreboardIncomingState = Partial < Omit < ScoreboardHubState , "colors" > > & {
23+ colors ?: ScoreboardColorsDto | Partial < ColorPreset > ;
24+ color ?: ScoreboardColorsDto | Partial < ColorPreset > ;
25+ } ;
26+
27+ type PendingServerCommand = {
28+ method : string ;
29+ data : ScoreboardDto | boolean ;
30+ } ;
31+
32+ const pendingServerCommands : PendingServerCommand [ ] = [ ] ;
33+ const MAX_PENDING_SERVER_COMMANDS = 40 ;
34+
2135// Интерфейс состояния
2236export interface ScoreboardState {
2337 _connection : HubConnection ;
@@ -62,14 +76,13 @@ export interface ScoreboardActions {
6276 reset : ( ) => void ;
6377
6478 // Действия для получения состояния с сервера
65- handleReceiveState : ( state : ScoreboardState ) => void ;
79+ handleReceiveState : ( state : ScoreboardIncomingState ) => void ;
6680
6781 // Внутренние действия (не экспортируются)
6882 _sendToServer : (
6983 method : string ,
70- data : ScoreboardDto | boolean ,
71- updateId ?: string
72- ) => boolean ;
84+ data : ScoreboardDto | boolean
85+ ) => Promise < boolean > ;
7386 _createServerState : (
7487 updatedPlayer1 ?: PlayerWithTimestamp ,
7588 updatedPlayer2 ?: PlayerWithTimestamp ,
@@ -124,18 +137,67 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
124137 // Инициализируем соединение
125138 const connection = initialState . _connection ;
126139
127- const firstActiveFunction = ( state : ScoreboardState ) => {
140+ const queueServerCommand = (
141+ method : string ,
142+ data : ScoreboardDto | boolean
143+ ) => {
144+ const nextCommand : PendingServerCommand = { method, data } ;
145+ const existingCommandIndex = pendingServerCommands . findIndex (
146+ pendingCommand => pendingCommand . method === method
147+ ) ;
148+
149+ if ( existingCommandIndex >= 0 ) {
150+ pendingServerCommands [ existingCommandIndex ] = nextCommand ;
151+ } else {
152+ pendingServerCommands . push ( nextCommand ) ;
153+ if ( pendingServerCommands . length > MAX_PENDING_SERVER_COMMANDS ) {
154+ pendingServerCommands . shift ( ) ;
155+ }
156+ }
157+ } ;
158+
159+ const flushPendingServerCommands = async ( ) => {
160+ if ( connection . state !== HubConnectionState . Connected ) {
161+ return ;
162+ }
163+
164+ while ( pendingServerCommands . length > 0 ) {
165+ const pendingCommand = pendingServerCommands . shift ( ) ;
166+ if ( pendingCommand ) {
167+ const sendResult = await get ( ) . _sendToServer (
168+ pendingCommand . method ,
169+ pendingCommand . data
170+ ) ;
171+
172+ if ( ! sendResult ) {
173+ break ;
174+ }
175+ }
176+ }
177+ } ;
178+
179+ const firstActiveFunction = ( state : ScoreboardIncomingState ) => {
128180 get ( ) . handleReceiveState ( state ) ;
129181 connection . off ( "ReceiveState" , firstActiveFunction ) ;
130182 } ;
131183
132184 connection . on ( "ReceiveState" , firstActiveFunction ) ;
133185
134- // Запускаем соединение
135- connection . start ( ) . catch ( err => {
136- console . error ( "Error starting SignalR connection:" , err ) ;
186+ connection . onreconnected ( ( ) => {
187+ console . log ( "Scoreboard SignalR reconnected. Flushing queued updates..." ) ;
188+ void flushPendingServerCommands ( ) ;
137189 } ) ;
138190
191+ // Запускаем соединение
192+ connection
193+ . start ( )
194+ . then ( ( ) => {
195+ void flushPendingServerCommands ( ) ;
196+ } )
197+ . catch ( err => {
198+ console . error ( "Error starting SignalR connection:" , err ) ;
199+ } ) ;
200+
139201 return {
140202 ...initialState ,
141203 _connection : connection ,
@@ -152,7 +214,7 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
152214
153215 // Отправляем на сервер
154216 const serverState = get ( ) . _createServerState ( updatedPlayer ) ;
155- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
217+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
156218 } ,
157219
158220 setPlayer2 : playerUpdate => {
@@ -167,7 +229,7 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
167229
168230 // Отправляем на сервер
169231 const serverState = get ( ) . _createServerState ( undefined , updatedPlayer ) ;
170- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
232+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
171233 } ,
172234
173235 swapPlayers : ( ) => {
@@ -184,7 +246,7 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
184246
185247 // Отправляем на сервер
186248 const serverState = get ( ) . _createServerState ( newPlayer1 , newPlayer2 ) ;
187- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
249+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
188250 } ,
189251
190252 // Действия с мета информацией
@@ -204,7 +266,7 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
204266 undefined ,
205267 updatedMeta
206268 ) ;
207- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
269+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
208270 } ,
209271
210272 // Действия с цветами
@@ -225,7 +287,7 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
225287 undefined ,
226288 updatedColor
227289 ) ;
228- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
290+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
229291 } ,
230292
231293 handleColorChange : colorUpdate => {
@@ -250,15 +312,15 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
250312 undefined ,
251313 updatedLayout
252314 ) ;
253- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
315+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
254316 } ,
255317
256318 // Действия с видимостью
257319 setVisibility : isVisible => {
258320 set ( { isVisible } ) ;
259321
260322 // Отправляем на сервер
261- get ( ) . _sendToServer ( "SetVisibility" , isVisible ) ;
323+ void get ( ) . _sendToServer ( "SetVisibility" , isVisible ) ;
262324 } ,
263325
264326 setAnimationDuration : duration => {
@@ -274,61 +336,84 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
274336 undefined ,
275337 duration
276338 ) ;
277- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
339+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
278340 } ,
279341
280342 // Действия сброса
281343 reset : ( ) => {
282- set ( initialState ) ;
344+ set ( { ... initialState , _connection : connection } ) ;
283345
284346 // Отправляем на сервер
285347 const serverState = get ( ) . _createServerState ( ) ;
286- get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
348+ void get ( ) . _sendToServer ( "UpdateState" , serverState ) ;
287349 } ,
288350
289351 // Действия для получения состояния с сервера
290352 handleReceiveState : state => {
353+ const receiveTime = Date . now ( ) ;
354+
291355 if ( state . player1 ) {
292- set ( { player1 : { ...state . player1 , _receivedAt : Date . now ( ) } } ) ;
356+ set ( { player1 : { ...state . player1 , _receivedAt : receiveTime } } ) ;
293357 }
294358 if ( state . player2 ) {
295- set ( { player2 : { ...state . player2 , _receivedAt : Date . now ( ) } } ) ;
359+ set ( { player2 : { ...state . player2 , _receivedAt : receiveTime } } ) ;
296360 }
297361 if ( state . meta ) {
298- set ( { meta : { ...state . meta , _receivedAt : Date . now ( ) } } ) ;
362+ set ( { meta : { ...state . meta , _receivedAt : receiveTime } } ) ;
299363 }
300- if ( state . color ) {
301- set ( { color : { ...state . color , _lastEdit : Date . now ( ) } } ) ;
364+
365+ const incomingColor = state . colors ?? state . color ;
366+ if ( incomingColor ) {
367+ const currentColor = get ( ) . color ;
368+ set ( {
369+ color : {
370+ ...currentColor ,
371+ ...incomingColor ,
372+ name : incomingColor . name ?? currentColor . name ?? "Custom" ,
373+ _lastEdit : receiveTime ,
374+ _receivedAt : receiveTime ,
375+ } ,
376+ } ) ;
302377 }
378+
303379 if ( state . layout ) {
304380 set ( { layout : state . layout } ) ;
305381 }
382+
306383 if ( typeof state . isVisible === "boolean" ) {
307384 set ( { isVisible : state . isVisible } ) ;
308385 }
309- if ( state . animationDuration ) {
386+
387+ if ( typeof state . animationDuration === "number" ) {
310388 set ( { animationDuration : state . animationDuration } ) ;
311389 }
312390 } ,
313391
314392 // Внутренние действия
315- _sendToServer : ( method , data ) => {
393+ _sendToServer : async ( method , data ) => {
394+ let result = false ;
395+
316396 try {
317397 if ( ! connection || connection . state !== HubConnectionState . Connected ) {
398+ queueServerCommand ( method , data ) ;
318399 console . log (
319- " SignalR connection not available, using local state only"
400+ ` SignalR connection state is ' ${ connection . state } '. Queued ${ method } .`
320401 ) ;
321- return true ;
402+ } else {
403+ console . log ( `Sending ${ method } :` , data ) ;
404+ await connection . invoke ( method , data ) ;
405+ console . log ( `Successfully sent ${ method } ` ) ;
406+ result = true ;
322407 }
323-
324- console . log ( `Sending ${ method } :` , data ) ;
325- connection . invoke ( method , data ) ;
326- console . log ( `Successfully sent ${ method } ` ) ;
327- return true ;
328408 } catch ( error ) {
329- console . error ( `Error sending ${ method } :` , error ) ;
330- return false ;
409+ queueServerCommand ( method , data ) ;
410+ console . error (
411+ `Error sending ${ method } . Command queued for retry.` ,
412+ error
413+ ) ;
331414 }
415+
416+ return result ;
332417 } ,
333418
334419 _createServerState : (
@@ -356,14 +441,14 @@ export const useScoreboardStore = create<ScoreboardStore>((set, get) => {
356441 } ) ;
357442
358443 return {
359- player1 : updatedPlayer1 || state . player1 ,
360- player2 : updatedPlayer2 || state . player2 ,
361- meta : updatedMeta || state . meta ,
362- colors : convertColorPresetToDto ( updatedColor || state . color ) ,
363- layout : updatedLayout || state . layout ,
444+ player1 : updatedPlayer1 ?? state . player1 ,
445+ player2 : updatedPlayer2 ?? state . player2 ,
446+ meta : updatedMeta ?? state . meta ,
447+ colors : convertColorPresetToDto ( updatedColor ?? state . color ) ,
448+ layout : updatedLayout ?? state . layout ,
364449 isVisible :
365450 updatedVisibility !== undefined ? updatedVisibility : state . isVisible ,
366- animationDuration : updatedAnimationDuration || state . animationDuration ,
451+ animationDuration : updatedAnimationDuration ?? state . animationDuration ,
367452 } ;
368453 } ,
369454 } ;
0 commit comments