diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0b3ae7eacd..a69ed89dcb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -65,6 +65,7 @@ + diff --git a/lib/src/app_links.dart b/lib/src/app_links.dart index 36a9d7949d..7aa7d2bf7d 100644 --- a/lib/src/app_links.dart +++ b/lib/src/app_links.dart @@ -3,6 +3,7 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:flutter/widgets.dart'; import 'package:lichess_mobile/src/model/common/id.dart'; import 'package:lichess_mobile/src/model/puzzle/puzzle_angle.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_game_screen.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_round_screen.dart'; import 'package:lichess_mobile/src/view/game/archived_game_screen.dart'; import 'package:lichess_mobile/src/view/puzzle/puzzle_screen.dart'; @@ -22,9 +23,15 @@ Route? resolveAppLinkUri(BuildContext context, Uri appLinkUri) { final id = appLinkUri.pathSegments[1]; return StudyScreen.buildRoute(context, StudyId(id)); case 'broadcast': - final id = appLinkUri.pathSegments[3]; - final tab = BroadcastRoundTab.tabOrNullFromString(appLinkUri.fragment); - return BroadcastRoundScreenLoading.buildRoute(context, BroadcastRoundId(id), initialTab: tab); + final roundId = BroadcastRoundId(appLinkUri.pathSegments[3]); + if (appLinkUri.pathSegments.length > 4) { + final gameId = BroadcastGameId(appLinkUri.pathSegments[4]); + return BroadcastGameScreen.buildRoute(context, roundId: roundId, gameId: gameId); + } else { + final tab = BroadcastRoundTab.tabOrNullFromString(appLinkUri.fragment); + return BroadcastRoundScreenLoading.buildRoute(context, roundId, initialTab: tab); + } + case 'training': final id = appLinkUri.pathSegments[1]; return PuzzleScreen.buildRoute( diff --git a/lib/src/view/broadcast/broadcast_game_screen.dart b/lib/src/view/broadcast/broadcast_game_screen.dart index 848a1e61c5..e75b5ebdd8 100644 --- a/lib/src/view/broadcast/broadcast_game_screen.dart +++ b/lib/src/view/broadcast/broadcast_game_screen.dart @@ -29,7 +29,7 @@ import 'package:lichess_mobile/src/widgets/pgn.dart'; import 'package:lichess_mobile/src/widgets/platform_scaffold.dart'; class BroadcastGameScreen extends ConsumerStatefulWidget { - final BroadcastTournamentId tournamentId; + final BroadcastTournamentId? tournamentId; final BroadcastRoundId roundId; final BroadcastGameId gameId; final String? tournamentSlug; @@ -37,7 +37,7 @@ class BroadcastGameScreen extends ConsumerStatefulWidget { final String? title; const BroadcastGameScreen({ - required this.tournamentId, + this.tournamentId, required this.roundId, required this.gameId, this.tournamentSlug, @@ -47,7 +47,7 @@ class BroadcastGameScreen extends ConsumerStatefulWidget { static Route buildRoute( BuildContext context, { - required BroadcastTournamentId tournamentId, + BroadcastTournamentId? tournamentId, required BroadcastRoundId roundId, required BroadcastGameId gameId, String? tournamentSlug, @@ -146,7 +146,7 @@ class _Body extends ConsumerWidget { required this.tabController, }); - final BroadcastTournamentId tournamentId; + final BroadcastTournamentId? tournamentId; final BroadcastRoundId roundId; final BroadcastGameId gameId; final String? tournamentSlug; @@ -379,13 +379,13 @@ enum _PlayerWidgetPosition { bottom, top } class _PlayerWidget extends ConsumerWidget { const _PlayerWidget({ - required this.tournamentId, + this.tournamentId, required this.roundId, required this.gameId, required this.widgetPosition, }); - final BroadcastTournamentId tournamentId; + final BroadcastTournamentId? tournamentId; final BroadcastRoundId roundId; final BroadcastGameId gameId; final _PlayerWidgetPosition widgetPosition; @@ -415,13 +415,21 @@ class _PlayerWidget extends ConsumerWidget { return GestureDetector( onTap: () { Navigator.of(context).push( - BroadcastPlayerResultsScreen.buildRoute( - context, - tournamentId, - (player.fideId != null) ? player.fideId!.toString() : player.name, - playerTitle: player.title, - playerName: player.name, - ), + (tournamentId != null) + ? BroadcastPlayerResultsScreen.buildRoute( + context, + tournamentId!, + (player.fideId != null) ? player.fideId!.toString() : player.name, + playerTitle: player.title, + playerName: player.name, + ) + : BroadcastPlayerResultsScreenLoading.buildRoute( + context, + roundId, + (player.fideId != null) ? player.fideId!.toString() : player.name, + playerTitle: player.title, + playerName: player.name, + ), ); }, child: Container( diff --git a/lib/src/view/broadcast/broadcast_player_results_screen.dart b/lib/src/view/broadcast/broadcast_player_results_screen.dart index f17d421c72..7c5e828165 100644 --- a/lib/src/view/broadcast/broadcast_player_results_screen.dart +++ b/lib/src/view/broadcast/broadcast_player_results_screen.dart @@ -12,11 +12,66 @@ import 'package:lichess_mobile/src/styles/styles.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_game_screen.dart'; +import 'package:lichess_mobile/src/view/broadcast/broadcast_player_screen_providers.dart'; import 'package:lichess_mobile/src/view/broadcast/broadcast_player_widget.dart'; import 'package:lichess_mobile/src/widgets/platform_scaffold.dart'; import 'package:lichess_mobile/src/widgets/progression_widget.dart'; import 'package:lichess_mobile/src/widgets/stat_card.dart'; +class BroadcastPlayerResultsScreenLoading extends ConsumerWidget { + final BroadcastRoundId roundId; + final String playerId; + final String? playerTitle; + final String playerName; + + const BroadcastPlayerResultsScreenLoading( + this.roundId, + this.playerId, { + required this.playerName, + this.playerTitle, + }); + + static Route buildRoute( + BuildContext context, + BroadcastRoundId roundId, + String playerId, { + String? playerTitle, + required String playerName, + }) { + return buildScreenRoute( + context, + screen: BroadcastPlayerResultsScreenLoading( + roundId, + playerId, + playerTitle: playerTitle, + playerName: playerName, + ), + ); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final tournamentId = ref.watch(broadcastTournamentIdProvider(roundId)); + + return switch (tournamentId) { + AsyncData(:final value) => BroadcastPlayerResultsScreen( + value, + playerId, + playerTitle: playerTitle, + playerName: playerName, + ), + AsyncError(:final error) => PlatformScaffold( + appBarTitle: const Text(''), + body: Center(child: Text('Cannot load round data: $error')), + ), + _ => const PlatformScaffold( + appBarTitle: Text(''), + body: Center(child: CircularProgressIndicator.adaptive()), + ), + }; + } +} + class BroadcastPlayerResultsScreen extends StatelessWidget { final BroadcastTournamentId tournamentId; final String playerId; diff --git a/lib/src/view/broadcast/broadcast_player_screen_providers.dart b/lib/src/view/broadcast/broadcast_player_screen_providers.dart new file mode 100644 index 0000000000..c5c26c24d3 --- /dev/null +++ b/lib/src/view/broadcast/broadcast_player_screen_providers.dart @@ -0,0 +1,11 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:lichess_mobile/src/model/broadcast/broadcast_providers.dart'; +import 'package:lichess_mobile/src/model/common/id.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'broadcast_player_screen_providers.g.dart'; + +@riverpod +Future broadcastTournamentId(Ref ref, BroadcastRoundId roundId) { + return ref.watch(broadcastRoundProvider(roundId).selectAsync((round) => round.tournament.id)); +}