From c9d77f08a92ec81a23ac8e2990213e76855aa4ec Mon Sep 17 00:00:00 2001 From: Noah Date: Sun, 17 May 2026 22:00:24 +0200 Subject: [PATCH 1/4] Broadcast Team Screen --- lib/src/model/broadcast/broadcast.dart | 23 ++ .../model/broadcast/broadcast_providers.dart | 8 + .../model/broadcast/broadcast_repository.dart | 28 ++ .../broadcast_player_results_screen.dart | 22 +- .../view/broadcast/broadcast_team_screen.dart | 377 ++++++++++++++++++ .../view/broadcast/broadcast_teams_tab.dart | 35 +- 6 files changed, 476 insertions(+), 17 deletions(-) create mode 100644 lib/src/view/broadcast/broadcast_team_screen.dart diff --git a/lib/src/model/broadcast/broadcast.dart b/lib/src/model/broadcast/broadcast.dart index 2cf49f89f..f917a62c3 100644 --- a/lib/src/model/broadcast/broadcast.dart +++ b/lib/src/model/broadcast/broadcast.dart @@ -337,3 +337,26 @@ sealed class BroadcastTeamMatch with _$BroadcastTeamMatch { required IList games, }) = _BroadcastTeamMatch; } + +@freezed +sealed class BroadcastTeamStandingMatch with _$BroadcastTeamStandingMatch { + const factory BroadcastTeamStandingMatch({ + required BroadcastRoundId roundId, + required String opponent, + required String points, + required double mp, + required double gp, + }) = _BroadcastTeamStandingMatch; +} + +@freezed +sealed class BroadcastTeamStanding with _$BroadcastTeamStanding { + const factory BroadcastTeamStanding({ + required String name, + required double mp, + required double gp, + required IList matches, + required IList players, + required int? averageRating, + }) = _BroadcastTeamStanding; +} diff --git a/lib/src/model/broadcast/broadcast_providers.dart b/lib/src/model/broadcast/broadcast_providers.dart index 7d1de6cf5..c6616135d 100644 --- a/lib/src/model/broadcast/broadcast_providers.dart +++ b/lib/src/model/broadcast/broadcast_providers.dart @@ -104,3 +104,11 @@ final broadcastTeamMatchesProvider = FutureProvider.autoDispose .family, BroadcastRoundId>((Ref ref, BroadcastRoundId roundId) { return ref.read(broadcastRepositoryProvider).getTeamMatches(roundId); }, name: 'BroadcastTeamMatchesProvider'); + +final broadcastTeamStandingsProvider = FutureProvider.autoDispose + .family, BroadcastTournamentId>(( + Ref ref, + BroadcastTournamentId tournamentId, + ) { + return ref.read(broadcastRepositoryProvider).getTeamStandings(tournamentId); + }, name: 'BroadcastTeamStandingsProvider'); diff --git a/lib/src/model/broadcast/broadcast_repository.dart b/lib/src/model/broadcast/broadcast_repository.dart index b65aa0d49..cd79359a3 100644 --- a/lib/src/model/broadcast/broadcast_repository.dart +++ b/lib/src/model/broadcast/broadcast_repository.dart @@ -94,6 +94,13 @@ class BroadcastRepository { pick(json, 'table').asListOrThrow(_teamMatchFromPick).toIList(), ); } + + Future> getTeamStandings(BroadcastTournamentId tournamentId) { + return client.readJsonList( + Uri(path: 'broadcast/$tournamentId/teams/standings'), + mapper: (json) => _teamStandingFromPick(pick(json).required()), + ); + } } BroadcastList broadcastListFromServerJson(Map json) { @@ -404,3 +411,24 @@ BroadcastTeamGame _teamGameFromPick(RequiredPick pick) { pov: pick('pov').asSideOrThrow(), ); } + +BroadcastTeamStanding _teamStandingFromPick(RequiredPick pick) { + return BroadcastTeamStanding( + name: pick('name').asStringOrThrow(), + mp: pick('mp').asDoubleOrThrow(), + gp: pick('gp').asDoubleOrThrow(), + matches: pick('matches').asListOrEmpty(_teamStandingMatchFromPick).toIList(), + players: pick('players').asListOrEmpty(_playerWithOverallResultFromPick).toIList(), + averageRating: pick('averageRating').asIntOrNull(), + ); +} + +BroadcastTeamStandingMatch _teamStandingMatchFromPick(RequiredPick pick) { + return BroadcastTeamStandingMatch( + roundId: pick('roundId').asBroadcastRoundIdOrThrow(), + opponent: pick('opponent').asStringOrThrow(), + points: pick('points').asStringOrThrow(), + mp: pick('mp').asDoubleOrThrow(), + gp: pick('gp').asDoubleOrThrow(), + ); +} diff --git a/lib/src/view/broadcast/broadcast_player_results_screen.dart b/lib/src/view/broadcast/broadcast_player_results_screen.dart index a998c14e3..d1a963c50 100644 --- a/lib/src/view/broadcast/broadcast_player_results_screen.dart +++ b/lib/src/view/broadcast/broadcast_player_results_screen.dart @@ -16,6 +16,7 @@ import 'package:lichess_mobile/src/utils/navigation.dart'; import 'package:lichess_mobile/src/utils/share.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_game_screen.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_team_screen.dart'; import 'package:lichess_mobile/src/widgets/buttons.dart'; import 'package:lichess_mobile/src/widgets/network_image.dart'; import 'package:lichess_mobile/src/widgets/platform.dart'; @@ -308,13 +309,20 @@ class _OverallStatPlayer extends StatelessWidget { ), const SizedBox(height: 16), if (team != null) ...[ - Row( - children: [ - const SizedBox(width: 100, child: Text('Team')), - Expanded( - child: Text(team.trim(), style: Theme.of(context).textTheme.bodyLarge), - ), - ], + GestureDetector( + onTap: () { + Navigator.of( + context, + ).push(BroadcastTeamScreen.buildRoute(context, tournament.data.id, team)); + }, + child: Row( + children: [ + const SizedBox(width: 100, child: Text('Team')), + Expanded( + child: Text(team, style: Theme.of(context).textTheme.bodyLarge), + ), + ], + ), ), const SizedBox(height: 16), ], diff --git a/lib/src/view/broadcast/broadcast_team_screen.dart b/lib/src/view/broadcast/broadcast_team_screen.dart new file mode 100644 index 000000000..e3f31f801 --- /dev/null +++ b/lib/src/view/broadcast/broadcast_team_screen.dart @@ -0,0 +1,377 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:lichess_mobile/src/model/broadcast/broadcast.dart'; +import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart'; +import 'package:lichess_mobile/src/model/common/id.dart'; +import 'package:lichess_mobile/src/styles/styles.dart'; +import 'package:lichess_mobile/src/theme.dart'; +import 'package:lichess_mobile/src/utils/l10n_context.dart'; +import 'package:lichess_mobile/src/utils/navigation.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_player_results_screen.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_round_screen.dart'; +import 'package:lichess_mobile/src/widgets/network_image.dart'; +import 'package:lichess_mobile/src/widgets/platform.dart'; +import 'package:lichess_mobile/src/widgets/stat_card.dart'; + +class BroadcastTeamScreen extends ConsumerWidget { + const BroadcastTeamScreen({super.key, required this.tournamentId, required this.teamName}); + + final BroadcastTournamentId tournamentId; + final String teamName; + + static Route buildRoute( + BuildContext context, + BroadcastTournamentId tournamentId, + String teamName, + ) { + return buildScreenRoute( + screen: BroadcastTeamScreen(tournamentId: tournamentId, teamName: teamName), + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final standingsAsync = ref.watch(broadcastTeamStandingsProvider(tournamentId)); + final tournamentAsync = ref.watch(broadcastTournamentProvider(tournamentId)); + + return PlatformScaffold( + appBar: PlatformAppBar( + title: Row( + children: [ + const Icon(Icons.groups_3), + const SizedBox(width: 8), + Flexible(child: Text(teamName, overflow: .ellipsis)), + ], + ), + ), + body: switch ((standingsAsync, tournamentAsync)) { + (AsyncData(value: final standings), AsyncData(value: final tournament)) => () { + final team = standings.where((t) => t.name == teamName).firstOrNull; + if (team == null) { + return const Center(child: Text('Team not found')); + } + return _Body(tournament: tournament, team: team); + }(), + (AsyncError(:final error), _) || + (_, AsyncError(:final error)) => Center(child: Text('Cannot load data: $error')), + _ => const Center(child: CircularProgressIndicator.adaptive()), + }, + ); + } +} + +class _Body extends StatelessWidget { + const _Body({required this.tournament, required this.team}); + + final BroadcastTournament tournament; + final BroadcastTeamStanding team; + + @override + Widget build(BuildContext context) { + return ListView( + children: [ + _OverallTeamStat(team: team), + if (team.players.isNotEmpty) ...[ + _SectionHeader(title: context.l10n.players), + ...team.players.asMap().entries.map( + (entry) => _PlayerListTile( + tournament: tournament, + playerResult: entry.value, + index: entry.key, + ), + ), + ], + + if (team.matches.isNotEmpty) ...[ + _SectionHeader(title: context.l10n.broadcastMatchHistory), + _MatchHistoryTable(team: team, tournament: tournament), + ], + const SizedBox(height: 32.0), + ], + ); + } +} + +class _OverallTeamStat extends StatelessWidget { + const _OverallTeamStat({required this.team}); + + final BroadcastTeamStanding team; + + @override + Widget build(BuildContext context) { + final statWidth = + (MediaQuery.sizeOf(context).width - Styles.bodyPadding.horizontal - 10 * 2) / 3; + const cardSpacing = 10.0; + + return Padding( + padding: Styles.bodyPadding.copyWith(top: 16.0, bottom: 16.0), + child: Column( + spacing: cardSpacing, + children: [ + Row( + mainAxisAlignment: .center, + spacing: cardSpacing, + children: [ + SizedBox( + width: statWidth, + child: _StatCard( + context.l10n.broadcastMatches, + value: team.matches.length.toString(), + ), + ), + SizedBox( + width: statWidth, + child: _StatCard( + context.l10n.broadcastMatchPoints, + value: NumberFormat('#.#').format(team.mp), + ), + ), + SizedBox( + width: statWidth, + child: _StatCard( + context.l10n.broadcastGamePoints, + value: NumberFormat('#.#').format(team.gp), + ), + ), + ], + ), + if (team.averageRating != null) + Row( + mainAxisAlignment: .center, + spacing: cardSpacing, + children: [ + SizedBox( + width: statWidth, + child: _StatCard(context.l10n.averageElo, value: team.averageRating.toString()), + ), + ], + ), + ], + ), + ); + } +} + +class _SectionHeader extends StatelessWidget { + const _SectionHeader({required this.title}); + + final String title; + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: ColorScheme.of(context).surfaceDim, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(title, style: Theme.of(context).textTheme.bodyLarge), + ), + ); + } +} + +class _PlayerListTile extends StatelessWidget { + const _PlayerListTile({ + required this.tournament, + required this.playerResult, + required this.index, + }); + + final BroadcastTournament tournament; + final BroadcastPlayerWithOverallResult playerResult; + final int index; + + @override + Widget build(BuildContext context) { + final scoreStr = playerResult.score != null + ? NumberFormat('#.#').format(playerResult.score) + : '-'; + + final pic = playerResult.player.fideId != null + ? tournament.photos?.get(playerResult.player.fideId!) + : null; + + return ListTile( + tileColor: index.isEven ? context.lichessTheme.rowEven : context.lichessTheme.rowOdd, + leading: ClipRRect( + borderRadius: Styles.thumbnailBorderRadius, + child: pic != null + ? HttpNetworkImageWidget(pic.smallUrl, width: 40, height: 40) + : playerResult.player.isBot + ? Image.asset('assets/images/anon-engine.webp', width: 40, height: 40) + : Image.asset('assets/images/anon-face.webp', width: 40, height: 40), + ), + title: BroadcastPlayerWidget(player: playerResult.player, showRating: false), + subtitle: playerResult.player.rating != null + ? Text(playerResult.player.rating.toString()) + : null, + trailing: Column( + mainAxisSize: .min, + crossAxisAlignment: .end, + mainAxisAlignment: .center, + children: [ + Text( + '$scoreStr / ${playerResult.played}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: .bold), + ), + Text( + 'Score', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7), + ), + ), + ], + ), + onTap: () { + Navigator.of(context).push( + BroadcastPlayerResultsScreen.buildRoute( + tournament.data.id, + playerResult.player, + playerResult.player.id!, + ), + ); + }, + ); + } +} + +class _StatCard extends StatelessWidget { + const _StatCard(this.stat, {this.value}); + + final String stat; + final String? value; + + @override + Widget build(BuildContext context) { + return StatCard( + contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0), + stat, + value: value, + ); + } +} + +class _MatchHistoryTable extends StatelessWidget { + const _MatchHistoryTable({required this.team, required this.tournament}); + + final BroadcastTeamStanding team; + final BroadcastTournament tournament; + + @override + Widget build(BuildContext context) { + return Table( + columnWidths: const { + 0: FixedColumnWidth(100), + 1: FlexColumnWidth(), + 2: FixedColumnWidth(70), + 3: FixedColumnWidth(70), + }, + defaultVerticalAlignment: .middle, + children: [ + const TableRow( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Text('Round', style: TextStyle(fontWeight: .bold)), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 12.0), + child: Text('Team', style: TextStyle(fontWeight: .bold)), + ), + Padding( + padding: EdgeInsets.symmetric(vertical: 12.0), + child: Text( + 'MP', + textAlign: .center, + style: TextStyle(fontWeight: .bold), + ), + ), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Text( + 'GP', + textAlign: .center, + style: TextStyle(fontWeight: .bold), + ), + ), + ], + ), + ...team.matches.asMap().entries.map((entry) { + final index = entry.key; + final match = entry.value; + + Color? pointsColor; + if (match.points == '1') { + pointsColor = context.lichessColors.good; + } else if (match.points == '0') { + pointsColor = context.lichessColors.error; + } + + return TableRow( + decoration: BoxDecoration( + color: index.isEven ? context.lichessTheme.rowEven : context.lichessTheme.rowOdd, + ), + children: [ + _TableTapCell( + match: match, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Text((index + 1).toString()), + ), + ), + TableRowInkWell( + onTap: () { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournament.data.id, match.opponent), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text(match.opponent, maxLines: 2, overflow: .ellipsis), + ), + ), + _TableTapCell( + match: match, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Text( + match.mp.toString(), + textAlign: .center, + style: TextStyle(fontWeight: .bold, color: pointsColor), + ), + ), + ), + _TableTapCell( + match: match, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + child: Text(NumberFormat('#.#').format(match.gp), textAlign: .center), + ), + ), + ], + ); + }), + ], + ); + } +} + +class _TableTapCell extends StatelessWidget { + const _TableTapCell({required this.match, required this.child}); + + final BroadcastTeamStandingMatch match; + final Widget child; + + @override + Widget build(BuildContext context) { + return TableRowInkWell( + onTap: () { + Navigator.of( + context, + ).push(BroadcastRoundScreenLoading.buildRoute(match.roundId, initialTab: .teams)); + }, + child: child, + ); + } +} diff --git a/lib/src/view/broadcast/broadcast_teams_tab.dart b/lib/src/view/broadcast/broadcast_teams_tab.dart index 943d3ac24..dab82d4da 100644 --- a/lib/src/view/broadcast/broadcast_teams_tab.dart +++ b/lib/src/view/broadcast/broadcast_teams_tab.dart @@ -14,6 +14,7 @@ import 'package:lichess_mobile/src/theme.dart'; import 'package:lichess_mobile/src/utils/screen.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_game_screen.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_team_screen.dart'; import 'package:lichess_mobile/src/view/engine/engine_gauge.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -142,11 +143,18 @@ class _TeamMatchCard extends StatelessWidget { child: Row( children: [ Expanded( - child: Text( - match.team1.name, - maxLines: _kTeamNameMaxLines, - textAlign: TextAlign.center, - style: const TextStyle(fontWeight: FontWeight.bold), + child: GestureDetector( + onTap: () { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournamentId, match.team1.name), + ); + }, + child: Text( + match.team1.name, + maxLines: _kTeamNameMaxLines, + textAlign: TextAlign.center, + style: const TextStyle(fontWeight: FontWeight.bold), + ), ), ), Container( @@ -174,11 +182,18 @@ class _TeamMatchCard extends StatelessWidget { ), ), Expanded( - child: Text( - match.team2.name, - maxLines: _kTeamNameMaxLines, - textAlign: TextAlign.center, - style: const TextStyle(fontWeight: FontWeight.bold), + child: GestureDetector( + onTap: () { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournamentId, match.team2.name), + ); + }, + child: Text( + match.team2.name, + maxLines: _kTeamNameMaxLines, + textAlign: TextAlign.center, + style: const TextStyle(fontWeight: FontWeight.bold), + ), ), ), ], From 3728130bf5f6d3a7b9bcaefc43d0594bcc151e84 Mon Sep 17 00:00:00 2001 From: Noah Date: Mon, 18 May 2026 19:21:23 +0200 Subject: [PATCH 2/4] code review --- lib/src/view/broadcast/broadcast_team_screen.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/view/broadcast/broadcast_team_screen.dart b/lib/src/view/broadcast/broadcast_team_screen.dart index e3f31f801..eec7e6bff 100644 --- a/lib/src/view/broadcast/broadcast_team_screen.dart +++ b/lib/src/view/broadcast/broadcast_team_screen.dart @@ -224,6 +224,7 @@ class _PlayerListTile extends StatelessWidget { ], ), onTap: () { + if (playerResult.player.id == null) return; Navigator.of(context).push( BroadcastPlayerResultsScreen.buildRoute( tournament.data.id, @@ -336,7 +337,7 @@ class _MatchHistoryTable extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 12.0), child: Text( - match.mp.toString(), + NumberFormat('#.#').format(match.mp), textAlign: .center, style: TextStyle(fontWeight: .bold, color: pointsColor), ), From 84b821e97d1ec50e4a3eafd75c08789ebc77aa5e Mon Sep 17 00:00:00 2001 From: Noah Date: Mon, 18 May 2026 20:12:13 +0200 Subject: [PATCH 3/4] cache team standings for some time --- lib/src/model/broadcast/broadcast_providers.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/model/broadcast/broadcast_providers.dart b/lib/src/model/broadcast/broadcast_providers.dart index c6616135d..f6f0c6a58 100644 --- a/lib/src/model/broadcast/broadcast_providers.dart +++ b/lib/src/model/broadcast/broadcast_providers.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lichess_mobile/src/model/broadcast/broadcast.dart'; import 'package:lichess_mobile/src/model/broadcast/broadcast_repository.dart'; import 'package:lichess_mobile/src/model/common/id.dart'; +import 'package:lichess_mobile/src/network/http.dart'; /// A provider that fetches a paginated list of broadcasts. final broadcastsPaginatorProvider = @@ -110,5 +111,8 @@ final broadcastTeamStandingsProvider = FutureProvider.autoDispose Ref ref, BroadcastTournamentId tournamentId, ) { - return ref.read(broadcastRepositoryProvider).getTeamStandings(tournamentId); + return ref.withClientCacheFor( + (client) => ref.read(broadcastRepositoryProvider).getTeamStandings(tournamentId), + const Duration(seconds: 30), + ); }, name: 'BroadcastTeamStandingsProvider'); From c61309368353beec0ff95cc4064d3daa7ded02c6 Mon Sep 17 00:00:00 2001 From: Noah Date: Mon, 18 May 2026 20:40:52 +0200 Subject: [PATCH 4/4] only show the team scores when they are enabled --- lib/src/model/broadcast/broadcast.dart | 1 + .../model/broadcast/broadcast_repository.dart | 1 + .../broadcast_player_results_screen.dart | 8 +++-- .../broadcast/broadcast_round_screen.dart | 1 + .../view/broadcast/broadcast_teams_tab.dart | 31 ++++++++++++++----- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/lib/src/model/broadcast/broadcast.dart b/lib/src/model/broadcast/broadcast.dart index f917a62c3..a8896c3dc 100644 --- a/lib/src/model/broadcast/broadcast.dart +++ b/lib/src/model/broadcast/broadcast.dart @@ -107,6 +107,7 @@ sealed class BroadcastTournamentData with _$BroadcastTournamentData { // PRIVATE=-1, NORMAL=3, HIGH=4, BEST=5 int? tier, bool? teamTable, + bool? showTeamScores, required BroadcastTournamentInformation information, }) = _BroadcastTournamentData; } diff --git a/lib/src/model/broadcast/broadcast_repository.dart b/lib/src/model/broadcast/broadcast_repository.dart index cd79359a3..48fbd2086 100644 --- a/lib/src/model/broadcast/broadcast_repository.dart +++ b/lib/src/model/broadcast/broadcast_repository.dart @@ -137,6 +137,7 @@ BroadcastTournamentData _tournamentDataFromPick(RequiredPick pick) => BroadcastT imageUrl: pick('image').asStringOrNull(), description: pick('description').asStringOrNull(), teamTable: pick('teamTable').asBoolOrFalse(), + showTeamScores: pick('showTeamScores').asBoolOrFalse(), information: ( format: pick('info', 'format').asStringOrNull(), timeControl: pick('info', 'tc').asStringOrNull(), diff --git a/lib/src/view/broadcast/broadcast_player_results_screen.dart b/lib/src/view/broadcast/broadcast_player_results_screen.dart index d1a963c50..233be7973 100644 --- a/lib/src/view/broadcast/broadcast_player_results_screen.dart +++ b/lib/src/view/broadcast/broadcast_player_results_screen.dart @@ -311,9 +311,11 @@ class _OverallStatPlayer extends StatelessWidget { if (team != null) ...[ GestureDetector( onTap: () { - Navigator.of( - context, - ).push(BroadcastTeamScreen.buildRoute(context, tournament.data.id, team)); + if (tournament.data.showTeamScores == true) { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournament.data.id, team), + ); + } }, child: Row( children: [ diff --git a/lib/src/view/broadcast/broadcast_round_screen.dart b/lib/src/view/broadcast/broadcast_round_screen.dart index 9c7a7abfb..08cb5ce87 100644 --- a/lib/src/view/broadcast/broadcast_round_screen.dart +++ b/lib/src/view/broadcast/broadcast_round_screen.dart @@ -249,6 +249,7 @@ class _BroadcastRoundScreenState extends ConsumerState roundId: _selectedRoundId ?? value.defaultRoundId, tournamentId: _selectedTournamentId, tournamentSlug: value.data.slug, + showTeamScores: value.data.showTeamScores == true, ), _ => const SizedBox.shrink(), }, diff --git a/lib/src/view/broadcast/broadcast_teams_tab.dart b/lib/src/view/broadcast/broadcast_teams_tab.dart index dab82d4da..b5f9438d5 100644 --- a/lib/src/view/broadcast/broadcast_teams_tab.dart +++ b/lib/src/view/broadcast/broadcast_teams_tab.dart @@ -34,11 +34,13 @@ class BroadcastTeamsTab extends ConsumerWidget { required this.roundId, required this.tournamentId, required this.tournamentSlug, + this.showTeamScores = false, }); final BroadcastRoundId roundId; final BroadcastTournamentId tournamentId; final String tournamentSlug; + final bool showTeamScores; @override Widget build(BuildContext context, WidgetRef ref) { @@ -50,6 +52,7 @@ class BroadcastTeamsTab extends ConsumerWidget { roundId, tournamentId, tournamentSlug, + showTeamScores, ), AsyncError(:final error) => Center(child: Text('Cannot load teams data: $error')), _ => const Center(child: CircularProgressIndicator.adaptive()), @@ -58,12 +61,19 @@ class BroadcastTeamsTab extends ConsumerWidget { } class BroadcastTeamsList extends ConsumerWidget { - const BroadcastTeamsList(this.teamMatches, this.roundId, this.tournamentId, this.tournamentSlug); + const BroadcastTeamsList( + this.teamMatches, + this.roundId, + this.tournamentId, + this.tournamentSlug, + this.showTeamScores, + ); final IList teamMatches; final BroadcastRoundId roundId; final BroadcastTournamentId tournamentId; final String tournamentSlug; + final bool showTeamScores; @override Widget build(BuildContext context, WidgetRef ref) { @@ -87,6 +97,7 @@ class BroadcastTeamsList extends ConsumerWidget { title: value.round.name, showEvaluationGauge: showEvaluationGauges, customScoring: value.round.customScoring, + showTeamScores: showTeamScores, ); }, ), @@ -107,6 +118,7 @@ class _TeamMatchCard extends StatelessWidget { required this.title, required this.showEvaluationGauge, required this.customScoring, + required this.showTeamScores, }); final BroadcastTeamMatch match; @@ -118,6 +130,7 @@ class _TeamMatchCard extends StatelessWidget { final String title; final bool showEvaluationGauge; final BroadcastCustomScoring? customScoring; + final bool showTeamScores; bool get matchFinished => games.everyEntry((e) => e.value.isOver); BroadcastResult? get matchStatus => matchFinished @@ -145,9 +158,11 @@ class _TeamMatchCard extends StatelessWidget { Expanded( child: GestureDetector( onTap: () { - Navigator.of(context).push( - BroadcastTeamScreen.buildRoute(context, tournamentId, match.team1.name), - ); + if (showTeamScores) { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournamentId, match.team1.name), + ); + } }, child: Text( match.team1.name, @@ -184,9 +199,11 @@ class _TeamMatchCard extends StatelessWidget { Expanded( child: GestureDetector( onTap: () { - Navigator.of(context).push( - BroadcastTeamScreen.buildRoute(context, tournamentId, match.team2.name), - ); + if (showTeamScores) { + Navigator.of(context).push( + BroadcastTeamScreen.buildRoute(context, tournamentId, match.team2.name), + ); + } }, child: Text( match.team2.name,