From 9a6a4004237aac6fac86c87555d7ecf9da7d49dc Mon Sep 17 00:00:00 2001 From: Vincent Velociter Date: Thu, 12 Dec 2024 14:51:24 +0100 Subject: [PATCH] Fix broadcast clock update issues --- .../broadcast/broadcast_game_controller.dart | 7 ++++- .../model/broadcast/broadcast_repository.dart | 28 +++++++++++++---- .../broadcast/broadcast_round_controller.dart | 1 + .../view/broadcast/broadcast_game_screen.dart | 30 +++++++++++-------- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/lib/src/model/broadcast/broadcast_game_controller.dart b/lib/src/model/broadcast/broadcast_game_controller.dart index 3ac22f1e48..79ffab5cb1 100644 --- a/lib/src/model/broadcast/broadcast_game_controller.dart +++ b/lib/src/model/broadcast/broadcast_game_controller.dart @@ -153,6 +153,8 @@ class BroadcastGameController extends _$BroadcastGameController // check provider is still mounted if (key == _key) { + final curState = state.requireValue; + final wasOnLivePath = curState.broadcastLivePath == curState.currentPath; final game = PgnGame.parsePgn(pgn); final pgnHeaders = IMap(game.headers); final rootComments = @@ -167,14 +169,17 @@ class BroadcastGameController extends _$BroadcastGameController _root = newRoot; + final newCurrentPath = + wasOnLivePath ? broadcastPath : curState.currentPath; state = AsyncData( state.requireValue.copyWith( + currentPath: newCurrentPath, pgnHeaders: pgnHeaders, pgnRootComments: rootComments, broadcastPath: broadcastPath, root: _root.view, lastMove: lastMove, - clocks: _getClocks(state.requireValue.currentPath), + clocks: _getClocks(newCurrentPath), ), ); } diff --git a/lib/src/model/broadcast/broadcast_repository.dart b/lib/src/model/broadcast/broadcast_repository.dart index 67dec06f47..1a6c49a13c 100644 --- a/lib/src/model/broadcast/broadcast_repository.dart +++ b/lib/src/model/broadcast/broadcast_repository.dart @@ -177,29 +177,47 @@ MapEntry gameFromPick( /// The amount of time that the player whose turn it is has been thinking since his last move final thinkTime = pick('thinkTime').asDurationFromSecondsOrNull() ?? Duration.zero; + final fen = + pick('fen').asStringOrNull() ?? Variant.standard.initialPosition.fen; + final playingSide = Setup.parseFen(fen).turn; return MapEntry( pick('id').asBroadcastGameIdOrThrow(), BroadcastGame( id: pick('id').asBroadcastGameIdOrThrow(), players: IMap({ - Side.white: _playerFromPick(pick('players', 0).required()), - Side.black: _playerFromPick(pick('players', 1).required()), + Side.white: _playerFromPick( + pick('players', 0).required(), + isPlaying: playingSide == Side.white, + thinkingTime: thinkTime, + ), + Side.black: _playerFromPick( + pick('players', 1).required(), + isPlaying: playingSide == Side.black, + thinkingTime: thinkTime, + ), }), fen: pick('fen').asStringOrNull() ?? Variant.standard.initialPosition.fen, lastMove: pick('lastMove').asUciMoveOrNull(), status: status, - updatedClockAt: DateTime.now().subtract(thinkTime), + updatedClockAt: DateTime.now(), ), ); } -BroadcastPlayer _playerFromPick(RequiredPick pick) { +BroadcastPlayer _playerFromPick( + RequiredPick pick, { + required bool isPlaying, + required Duration thinkingTime, +}) { + final clock = pick('clock').asDurationFromCentiSecondsOrNull(); + final updatedClock = + clock != null && isPlaying ? clock - thinkingTime : clock; return BroadcastPlayer( name: pick('name').asStringOrThrow(), title: pick('title').asStringOrNull(), rating: pick('rating').asIntOrNull(), - clock: pick('clock').asDurationFromCentiSecondsOrNull(), + clock: updatedClock, federation: pick('fed').asStringOrNull(), fideId: pick('fideId').asFideIdOrNull(), ); diff --git a/lib/src/model/broadcast/broadcast_round_controller.dart b/lib/src/model/broadcast/broadcast_round_controller.dart index 680e26ddd8..fd9569cf77 100644 --- a/lib/src/model/broadcast/broadcast_round_controller.dart +++ b/lib/src/model/broadcast/broadcast_round_controller.dart @@ -181,6 +181,7 @@ class BroadcastRoundController extends _$BroadcastRoundController { ), }, ), + updatedClockAt: DateTime.now(), ), ), ), diff --git a/lib/src/view/broadcast/broadcast_game_screen.dart b/lib/src/view/broadcast/broadcast_game_screen.dart index 55a01f0352..b9b367fe09 100644 --- a/lib/src/view/broadcast/broadcast_game_screen.dart +++ b/lib/src/view/broadcast/broadcast_game_screen.dart @@ -369,7 +369,13 @@ class _PlayerWidget extends ConsumerWidget { final broadcastGameState = ref .watch(broadcastGameControllerProvider(roundId, gameId)) .requireValue; - final clocks = broadcastGameState.clocks; + // TODO + // we'll probably want to remove this and get the game state from a single controller + // this won't work with deep links for instance + final game = ref.watch( + broadcastRoundControllerProvider(roundId) + .select((round) => round.requireValue.games[gameId]!), + ); final isCursorOnLiveMove = broadcastGameState.currentPath == broadcastGameState.broadcastLivePath; final sideToMove = broadcastGameState.position.turn; @@ -377,15 +383,14 @@ class _PlayerWidget extends ConsumerWidget { _PlayerWidgetPosition.bottom => broadcastGameState.pov, _PlayerWidgetPosition.top => broadcastGameState.pov.opposite, }; - final clock = (sideToMove == side) ? clocks?.parentClock : clocks?.clock; - - final game = ref.watch( - broadcastRoundControllerProvider(roundId) - .select((round) => round.requireValue.games[gameId]!), - ); final player = game.players[side]!; + final liveClock = isCursorOnLiveMove ? player.clock : null; final gameStatus = game.status; + final pastClocks = broadcastGameState.clocks; + final pastClock = + (sideToMove == side) ? pastClocks?.parentClock : pastClocks?.clock; + return Container( color: Theme.of(context).platform == TargetPlatform.iOS ? Styles.cupertinoCardColor.resolveFrom(context) @@ -418,7 +423,7 @@ class _PlayerWidget extends ConsumerWidget { const TextStyle().copyWith(fontWeight: FontWeight.bold), ), ), - if (clock != null) + if (liveClock != null || pastClock != null) Container( height: kAnalysisBoardHeaderOrFooterHeight, color: (side == sideToMove) @@ -429,9 +434,9 @@ class _PlayerWidget extends ConsumerWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6.0), child: Center( - child: isCursorOnLiveMove + child: liveClock != null ? CountdownClockBuilder( - timeLeft: clock, + timeLeft: liveClock, active: side == sideToMove, builder: (context, timeLeft) => _Clock( timeLeft: timeLeft, @@ -439,11 +444,10 @@ class _PlayerWidget extends ConsumerWidget { isLive: true, ), tickInterval: const Duration(seconds: 1), - clockUpdatedAt: - side == sideToMove ? game.updatedClockAt : null, + clockUpdatedAt: game.updatedClockAt, ) : _Clock( - timeLeft: clock, + timeLeft: pastClock!, isSideToMove: side == sideToMove, isLive: false, ),