Skip to content

Commit

Permalink
feat: split TeamMatch from Bout Screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustl22 committed Jan 16, 2024
1 parent a07ec1e commit 320c34d
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 54 deletions.
75 changes: 52 additions & 23 deletions wrestling_scoreboard_client/lib/ui/display/bout/bout_display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,30 @@ import 'package:wrestling_scoreboard_client/ui/display/bout/technical_points.dar
import 'package:wrestling_scoreboard_client/ui/display/bout/time_display.dart';
import 'package:wrestling_scoreboard_client/ui/display/common.dart';
import 'package:wrestling_scoreboard_client/ui/models/participant_state_model.dart';
import 'package:wrestling_scoreboard_client/ui/overview/team_match/team_match_bout_overview.dart';
import 'package:wrestling_scoreboard_client/ui/overview/team_match/team_match_overview.dart';
import 'package:wrestling_scoreboard_client/util/audio/audio.dart';
import 'package:wrestling_scoreboard_client/util/network/data_provider.dart';
import 'package:wrestling_scoreboard_client/util/print/pdf/score_sheet.dart';
import 'package:wrestling_scoreboard_client/util/units.dart';
import 'package:wrestling_scoreboard_common/common.dart';

void navigateToBoutScreen(BuildContext context, TeamMatch match, Bout bout) async {
context.push('/${TeamMatchOverview.route}/${match.id}/${BoutDisplay.route}/${bout.id}');
void navigateToTeamMatchBoutScreen(BuildContext context, TeamMatch match, TeamMatchBout bout) async {
context.push('/${TeamMatchOverview.route}/${match.id}/${TeamMatchBoutDisplay.route}/${bout.id}');
}

/// Class to load a single bout, while also consider the previous and the next bout.
/// So must load the whole list of bouts to keep track of what comes next.
/// TODO: This may can be done server side with its own request in the future.
class BoutDisplay extends StatelessWidget {
static const route = 'bout';
class TeamMatchBoutDisplay extends StatelessWidget {
static const route = 'team_match_bout';
final int matchId;
final int boutId;
final int teamMatchBoutId;
final TeamMatch? initialMatch;

const BoutDisplay({
const TeamMatchBoutDisplay({
required this.matchId,
required this.boutId,
required this.teamMatchBoutId,
this.initialMatch,
super.key,
});
Expand All @@ -53,24 +54,37 @@ class BoutDisplay extends StatelessWidget {
id: matchId,
initialData: initialMatch,
builder: (context, match) {
return ManyConsumer<Bout, TeamMatch>(
return ManyConsumer<TeamMatchBout, TeamMatch>(
filterObject: match,
builder: (context, bouts) {
if (bouts.isEmpty) {
builder: (context, teamMatchBouts) {
if (teamMatchBouts.isEmpty) {
return Center(
child: Text(
localizations.noItems,
style: Theme.of(context).textTheme.bodySmall,
),
);
}
final currentBout = bouts.singleWhere((element) => element.id == boutId);
final currentBoutIndex = bouts.indexOf(currentBout);
final teamMatchBout = teamMatchBouts.singleWhere((element) => element.id == teamMatchBoutId);
final teamMatchBoutIndex = teamMatchBouts.indexOf(teamMatchBout);
// Use bout to get the actual state, but use teamMatchBout for navigation.
return SingleConsumer<Bout>(
id: currentBout.id,
initialData: currentBout,
id: teamMatchBout.bout.id,
initialData: teamMatchBout.bout,
builder: (context, bout) {
return BoutScreen(match: match, bouts: bouts, boutIndex: currentBoutIndex, bout: bout);
return BoutScreen(
wrestlingEvent: match,
boutConfig: match.league?.boutConfig ?? const BoutConfig(),
bouts: teamMatchBouts.map((e) => e.bout).toList(),
boutIndex: teamMatchBoutIndex,
bout: bout,
onPressBoutInfo: (BuildContext context) {
context.push('/${TeamMatchBoutOverview.route}/${teamMatchBout.id}');
},
navigateToBoutByIndex: (context, index) => navigateToTeamMatchBoutScreen(context, match, teamMatchBouts[index]),
home: match.home.team,
guest: match.guest.team,
);
});
});
});
Expand All @@ -80,16 +94,28 @@ class BoutDisplay extends StatelessWidget {
/// Initialize with default values, but do not synchronize with live data, as during a bout the connection could be interrupted. So the client always sends data, but never should receive any.
/// If closing and reopening screen, data should be updated though.
class BoutScreen extends ConsumerStatefulWidget {
final TeamMatch match;
final WrestlingEvent wrestlingEvent;
final List<Bout> bouts;
final Bout bout;

// TODO: may overwrite in settings to be more flexible
final BoutConfig boutConfig;
final Team home;
final Team guest;
final int boutIndex;
final void Function(BuildContext context) onPressBoutInfo;
final void Function(BuildContext context, int boutIndex) navigateToBoutByIndex;

const BoutScreen({
required this.match,
required this.bouts,
required this.bout,
required this.boutIndex,
required this.home,
required this.guest,
required this.onPressBoutInfo,
required this.navigateToBoutByIndex,
required this.boutConfig,
required this.wrestlingEvent,
super.key,
});

Expand All @@ -114,8 +140,7 @@ class BoutState extends ConsumerState<BoutScreen> {
initState() {
super.initState();
HornSound();
// TODO: may overwrite in settings to be more flexible
boutConfig = widget.match.league?.boutConfig ?? const BoutConfig();
boutConfig = widget.boutConfig;
bout = widget.bout;
_r = ParticipantStateModel(bout.r);
_b = ParticipantStateModel(bout.b);
Expand Down Expand Up @@ -200,12 +225,12 @@ class BoutState extends ConsumerState<BoutScreen> {
void handleAction(BoutScreenActionIntent intent) {
intent.handle(
stopwatch,
widget.match,
widget.bouts,
getActions,
widget.boutIndex,
doAction,
context: context,
navigateToBoutByIndex: widget.navigateToBoutByIndex,
);
}

Expand Down Expand Up @@ -377,16 +402,20 @@ class BoutState extends ConsumerState<BoutScreen> {
Printing.sharePdf(bytes: bytes);
},
);
final infoAction = IconButton(
icon: const Icon(Icons.info),
onPressed: () => widget.onPressBoutInfo(context),
);
return ManyConsumer<BoutAction, Bout>(
filterObject: bout,
builder: (context, actions) {
return BoutActionHandler(
stopwatch: stopwatch,
match: widget.match,
getActions: () async => actions,
bouts: widget.bouts,
boutIndex: widget.boutIndex,
doAction: doAction,
navigateToBoutByIndex: widget.navigateToBoutByIndex,
child: Consumer(builder: (context, ref, child) {
return LoadingBuilder<WindowState>(
future: ref.watch(windowStateNotifierProvider),
Expand All @@ -395,13 +424,13 @@ class BoutState extends ConsumerState<BoutScreen> {
return Scaffold(
appBar: isFullScreen
? null
: AppBar(actions: [shareAction, CommonElements.getFullScreenAction(context, ref)]),
: AppBar(actions: [infoAction, shareAction, CommonElements.getFullScreenAction(context, ref)]),
body: SingleChildScrollView(
child: Column(
children: [
row(
padding: bottomPadding,
children: CommonElements.getTeamHeader(widget.match, widget.bouts, context)
children: CommonElements.getTeamHeader(widget.home, widget.guest, widget.bouts, context)
.asMap()
.entries
.map((entry) => Expanded(flex: flexWidths[entry.key], child: entry.value))
Expand Down
26 changes: 16 additions & 10 deletions wrestling_scoreboard_client/lib/ui/display/bout/bout_shortcuts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:wrestling_scoreboard_client/ui/display/bout/bout_display.dart';
import 'package:wrestling_scoreboard_client/util/audio/audio.dart';
import 'package:wrestling_scoreboard_client/util/network/data_provider.dart';
import 'package:wrestling_scoreboard_common/common.dart';
Expand Down Expand Up @@ -102,9 +101,15 @@ class BoutScreenActionIntent extends Intent {
const BoutScreenActionIntent.blueUndo() : type = BoutScreenActions.blueUndo;
final BoutScreenActions type;

Future<void> handle(ObservableStopwatch stopwatch, TeamMatch match, List<Bout> bouts,
Future<List<BoutAction>> Function() getActions, int boutIndex, Function(BoutScreenActions action) doAction,
{BuildContext? context}) async {
Future<void> handle(
ObservableStopwatch stopwatch,
List<Bout> bouts,
Future<List<BoutAction>> Function() getActions,
int boutIndex,
Function(BoutScreenActions action) doAction, {
BuildContext? context,
required void Function(BuildContext context, int boutIndex) navigateToBoutByIndex,
}) async {
final bout = bouts[boutIndex];
switch (type) {
case BoutScreenActions.startStop:
Expand All @@ -128,7 +133,7 @@ class BoutScreenActionIntent extends Intent {
int index = boutIndex + 1;
if (index < bouts.length) {
context.pop();
navigateToBoutScreen(context, match, bouts[index]);
navigateToBoutByIndex(context, index);
}
}
break;
Expand All @@ -137,7 +142,7 @@ class BoutScreenActionIntent extends Intent {
int index = boutIndex - 1;
if (index >= 0) {
context.pop();
navigateToBoutScreen(context, match, bouts[index]);
navigateToBoutByIndex(context, index);
}
}
break;
Expand Down Expand Up @@ -268,24 +273,25 @@ class BoutScreenActionIntent extends Intent {
class BoutActionHandler extends StatelessWidget {
final Widget child;
final ObservableStopwatch stopwatch;
final TeamMatch match;
final List<Bout> bouts;
final Future<List<BoutAction>> Function() getActions;
final int boutIndex;
final Function(BoutScreenActions action) doAction;
final void Function(BuildContext context, int boutIndex) navigateToBoutByIndex;

const BoutActionHandler(
{required this.child,
required this.stopwatch,
required this.match,
required this.bouts,
required this.getActions,
required this.boutIndex,
required this.doAction,
super.key});
super.key,
required this.navigateToBoutByIndex});

Future<void> handleIntent(BoutScreenActionIntent intent, {BuildContext? context}) async {
await intent.handle(stopwatch, match, bouts, getActions, boutIndex, doAction, context: context);
await intent.handle(stopwatch, bouts, getActions, boutIndex, doAction,
context: context, navigateToBoutByIndex: navigateToBoutByIndex);
}

@override
Expand Down
6 changes: 3 additions & 3 deletions wrestling_scoreboard_client/lib/ui/display/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:wrestling_scoreboard_client/ui/shortcuts/app_shortcuts.dart';
import 'package:wrestling_scoreboard_common/common.dart';

class CommonElements {
static List<Widget> getTeamHeader(TeamMatch match, List<Bout> bouts, BuildContext context) {
static List<Widget> getTeamHeader(Team home, Team guest, List<Bout> bouts, BuildContext context) {
final width = MediaQuery.of(context).size.width;
final padding = width / 100;
final edgeInsets = EdgeInsets.all(padding);
Expand All @@ -16,7 +16,7 @@ class CommonElements {
padding: edgeInsets,
child: Center(
child: ScaledText(
match.home.team.name,
home.name,
fontSize: 28,
minFontSize: 16,
),
Expand Down Expand Up @@ -56,7 +56,7 @@ class CommonElements {
padding: edgeInsets,
child: Center(
child: ScaledText(
match.guest.team.name,
guest.name,
fontSize: 28,
minFontSize: 16,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ class MatchDisplay extends StatelessWidget {
appBar: isFullScreen
? null
: AppBar(actions: [infoAction, CommonElements.getFullScreenAction(context, ref)]),
body: ManyConsumer<Bout, TeamMatch>(
body: ManyConsumer<TeamMatchBout, TeamMatch>(
filterObject: match,
builder: (context, bouts) {
builder: (context, teamMatchBouts) {
final matchInfos = [
match.league?.name,
'${AppLocalizations.of(context)!.boutNo}: ${match.id ?? ''}',
Expand All @@ -71,7 +71,8 @@ class MatchDisplay extends StatelessWidget {
fontSize: 12,
minFontSize: 10,
))),
...CommonElements.getTeamHeader(match, bouts, context),
...CommonElements.getTeamHeader(
match.home.team, match.guest.team, teamMatchBouts.map((e) => e.bout).toList(), context),
];
final column = Column(
children: [
Expand All @@ -86,18 +87,18 @@ class MatchDisplay extends StatelessWidget {
),
Expanded(
child: ListView.builder(
itemCount: bouts.length,
itemCount: teamMatchBouts.length,
itemBuilder: (context, index) {
return Column(
children: [
InkWell(
onTap: () => navigateToBoutScreen(context, match, bouts[index]),
onTap: () => navigateToTeamMatchBoutScreen(context, match, teamMatchBouts[index]),
child: IntrinsicHeight(
child: ManyConsumer<BoutAction, Bout>(
filterObject: bouts[index],
filterObject: teamMatchBouts[index].bout,
builder: (context, actions) => BoutListItem(
match: match,
bout: bouts[index],
bout: teamMatchBouts[index].bout,
actions: actions,
),
),
Expand Down Expand Up @@ -236,8 +237,7 @@ class BoutListItem extends StatelessWidget {
Expanded(
flex: 70,
child: ThemedContainer(
color:
data.winnerRole != null ? getColorFromBoutRole(data.winnerRole!).shade800 : null,
color: data.winnerRole != null ? getColorFromBoutRole(data.winnerRole!).shade800 : null,
child: Center(
child: ScaledText(getAbbreviationFromBoutResult(data.result, context), fontSize: 12),
),
Expand Down
1 change: 0 additions & 1 deletion wrestling_scoreboard_client/lib/ui/edit/bout_edit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ class ParticipantSelectTile extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final localizations = AppLocalizations.of(context)!;
return ListTile(
title: Row(
children: [
Expand Down
6 changes: 3 additions & 3 deletions wrestling_scoreboard_client/lib/ui/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ final router = GoRouter(
builder: (context, state) => MatchDisplay(id: int.parse(state.pathParameters['match_id']!)),
),
GoRoute(
path: '${BoutDisplay.route}/:bout_id',
path: '${TeamMatchBoutDisplay.route}/:team_match_bout_id',
builder: (context, state) {
final matchId = int.parse(state.pathParameters['match_id']!);
final boutId = int.parse(state.pathParameters['bout_id']!);
return BoutDisplay(matchId: matchId, boutId: boutId);
final teamMatchBoutId = int.parse(state.pathParameters['team_match_bout_id']!);
return TeamMatchBoutDisplay(matchId: matchId, teamMatchBoutId: teamMatchBoutId);
},
),
],
Expand Down
Loading

0 comments on commit 320c34d

Please sign in to comment.