-
-
Notifications
You must be signed in to change notification settings - Fork 209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Broadcast player results screen #1253
Conversation
03d785c
to
e948d04
Compare
Hey Julian, 2 requests.
This is awesome tho! |
e948d04
to
de1b7ba
Compare
de1b7ba
to
1e8253b
Compare
child: CircularProgressIndicator.adaptive(), | ||
), | ||
), | ||
_ => const BroadcastPreview.loading() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this on purpose. It was displaying a flash of shimmer boards in FIDE WC where only one board is displayed.
So it's better to not have it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the board shimmer looks better than the circular indicator.
In the issue #1255 you can see that the flash is a black screen. This is because the circular indicator is not in the Scaffold widget but at the top.
Also I believe this loading is never reached because we are already listen higher in the tree to the round state (broadcast_round_screen).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is a way to know in advance the number of boards, then we can use the shimmer. Otherwise I think it is ugly to see a flash of 10 shimmer boards where in fact only one is show eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having only one board in a round is pretty rare. In the majority of cases I think the board shimmer looks nicer than the circular indicator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the world championship is the most watch broadcast... even if it is not the majority it will be seen much more times than other tournaments with more boards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely agree with Julien here. The shimmer looks and feels much better. Also, the wch match is over, and the next one will take place in about two years.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does unless there is only one or two board and you see a flash of shimmer if the request completes very quickly.
@@ -79,11 +81,15 @@ class _BroadcastGameScreenState extends ConsumerState<BroadcastGameScreen> | |||
Widget build(BuildContext context) { | |||
final broadcastGameState = ref | |||
.watch(broadcastGameControllerProvider(widget.roundId, widget.gameId)); | |||
final broadcastRoundState = | |||
ref.watch(broadcastRoundControllerProvider(widget.roundId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we don't want to rebuild the whole screen if any of the other game of the round is updated, so you need to use .selectAsync
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But see this other comment, we should have all the state in a single controller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used select
instead because selectAsync
returns a Future
which makes it unpractical to use inside a widget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to use selectAsync
you need to create a dedicated provider to retrieve the title. This is the way to do it with riverpod.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But what is the advantage of creating a new provider instead of using select
like this ?
final title = ref.watch(
broadcastRoundControllerProvider(widget.roundId)
.select((round) => round.value?.round.name),
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's mostly a problem of typing. Here you're mixing the async value which represent an asynchronous value which may be not here yet, with a nullable value that you derives from another asynchronous value.
By creating another provider that returns an AsyncValue for the title you're making it clear of what you expect. It is ok to declare a new provider for that. It is like a cached computed value, but async. Doing that is recommended in the riverpod doc (but I don't remember where).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note also that using .value?
directly you're not handling the error, it will be rethrown in the widget build method making it crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok thanks for the detailed explanation. I'll make a title provider.
I believe I have already done this in previous PR but I'll make sure to create a provider in the future
onTap: () { | ||
pushPlatformRoute( | ||
context, | ||
builder: (context) => BroadcastGameScreen( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we can now open a screen from another context, this should be refactored:
mobile/lib/src/view/broadcast/broadcast_game_screen.dart
Lines 372 to 378 in 9a6a400
// 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]!), | |
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this would not work with deep link. The two controllers are independent, they just listen to the same route because there is only one for broadcast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you can load a game by itself you don't want to load the whole round, right? In a tournament where you have 100 board you'd load 99 boards (and listen to their updates) for nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with that but I have done it this way because in the API there are no endpoints to get the player information for a specific board.
Maybe this refactoring can be done in an other branch once there is an API endpoint for that ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes let's do it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is also problematic because you assume you assume the round controller is loaded: select((round) => round.requireValue.games[gameId]!)
I'm not sure it is a good practice to mix select
and requireValue
in riverpod. So far we know it is loaded because the only way to access the game screen was through the round screen.
Now that you can access the game screen from the player result screen directly, can you be sure the round screen will be loaded? If you can access a game from another round it won't be the case. This is the main reason why it must be refactored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the requireValue
. Do you still want to do the refactoring in another branch ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After talking with Thibault, it's decided that it is ok to load the full round to display a single game. So no refactoring after all.
But we need to ensure the round controller is loaded before the game controller in the game screen. (So we can do a requireValue
)
@@ -118,7 +121,7 @@ class _PlayersListState extends ConsumerState<PlayersList> { | |||
@override | |||
Widget build(BuildContext context) { | |||
final double eloWidth = max(MediaQuery.sizeOf(context).width * 0.2, 100); | |||
final double scoreWidth = max(MediaQuery.sizeOf(context).width * 0.15, 70); | |||
final double scoreWidth = max(MediaQuery.sizeOf(context).width * 0.15, 90); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? Have you noticed a case where it was too small?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because I changed the score from 'x/y' to 'x / y'.
color: Theme.of(context).platform == TargetPlatform.iOS | ||
? index.isEven | ||
? (index - 1).isEven |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it was fine like that, you should leave it as is. I want the color appear in this order on iOS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was to have the same color order as in the opening explorer
I think I have addressed all of your comments |
final title = widget.title ?? | ||
(switch (ref.watch(broadcastGameScreenTitleProvider(widget.roundId))) { | ||
AsyncData(value: final title) => title, | ||
_ => 'Broadcast Game', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather have a SizedBox.shrink()
here than a default title.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
3edcebe
to
c5d8fdc
Compare
broadcast_results.webm