Merge pull request 'Neuer Regelsatz: Platzierung' (#213) from feature/206-Neuer-Regelsatz-Platzierung into development
All checks were successful
All checks were successful
Reviewed-on: #213 Reviewed-by: Felix Kirchner <flixcoo@noreply.git.yannick-weigert.de>
This commit was merged in pull request #213.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttericon/rpg_awesome_icons.dart';
|
||||||
import 'package:tallee/core/enums.dart';
|
import 'package:tallee/core/enums.dart';
|
||||||
import 'package:tallee/data/models/match.dart';
|
import 'package:tallee/data/models/match.dart';
|
||||||
import 'package:tallee/data/models/player.dart';
|
import 'package:tallee/data/models/player.dart';
|
||||||
@@ -18,6 +19,8 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) {
|
|||||||
return loc.single_loser;
|
return loc.single_loser;
|
||||||
case Ruleset.multipleWinners:
|
case Ruleset.multipleWinners:
|
||||||
return loc.multiple_winners;
|
return loc.multiple_winners;
|
||||||
|
case Ruleset.placement:
|
||||||
|
return loc.placement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +82,8 @@ IconData getRulesetIcon(Ruleset ruleset) {
|
|||||||
return Icons.sentiment_dissatisfied;
|
return Icons.sentiment_dissatisfied;
|
||||||
case Ruleset.multipleWinners:
|
case Ruleset.multipleWinners:
|
||||||
return Icons.group;
|
return Icons.group;
|
||||||
|
case Ruleset.placement:
|
||||||
|
return RpgAwesome.podium;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,14 @@ enum ExportResult { success, canceled, unknownException }
|
|||||||
/// - [Ruleset.singleWinner]: The match is won by a single player.
|
/// - [Ruleset.singleWinner]: The match is won by a single player.
|
||||||
/// - [Ruleset.singleLoser]: The match has a single loser.
|
/// - [Ruleset.singleLoser]: The match has a single loser.
|
||||||
/// - [Ruleset.multipleWinners]: Multiple players can be winners.
|
/// - [Ruleset.multipleWinners]: Multiple players can be winners.
|
||||||
|
/// - [Ruleset.placement]: The player with the highest placement wins.
|
||||||
enum Ruleset {
|
enum Ruleset {
|
||||||
highestScore,
|
highestScore,
|
||||||
lowestScore,
|
lowestScore,
|
||||||
singleWinner,
|
singleWinner,
|
||||||
singleLoser,
|
singleLoser,
|
||||||
multipleWinners,
|
multipleWinners,
|
||||||
|
placement,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Different colors available for games
|
/// Different colors available for games
|
||||||
|
|||||||
@@ -353,4 +353,19 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
|
|||||||
return await deleteAllScoresForMatch(matchId: matchId);
|
return await deleteAllScoresForMatch(matchId: matchId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the placement for each player in a match.
|
||||||
|
/// The highest score is assigned to the first player, the second highest to the second player, and so on.
|
||||||
|
Future<void> setPlacements({
|
||||||
|
required String matchId,
|
||||||
|
required List<Player> players,
|
||||||
|
}) async {
|
||||||
|
for (int i = 0; i < players.length; i++) {
|
||||||
|
await db.scoreEntryDao.addScore(
|
||||||
|
matchId: matchId,
|
||||||
|
playerId: players[i].id,
|
||||||
|
entry: ScoreEntry(roundNumber: 0, score: players.length - i, change: 0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ class Match {
|
|||||||
|
|
||||||
case Ruleset.multipleWinners:
|
case Ruleset.multipleWinners:
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
|
case Ruleset.placement:
|
||||||
|
return _getPlayersWithHighestScore().take(1).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
},
|
},
|
||||||
"delete_group": "Gruppe löschen",
|
"delete_group": "Gruppe löschen",
|
||||||
"delete_match": "Spiel löschen",
|
"delete_match": "Spiel löschen",
|
||||||
|
"drag_to_set_placement": "Ziehen um Platzierung zu setzen",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"edit_game": "Spielvorlage bearbeiten",
|
"edit_game": "Spielvorlage bearbeiten",
|
||||||
"edit_group": "Gruppe bearbeiten",
|
"edit_group": "Gruppe bearbeiten",
|
||||||
@@ -97,6 +98,8 @@
|
|||||||
"none": "Kein",
|
"none": "Kein",
|
||||||
"none_group": "Keine",
|
"none_group": "Keine",
|
||||||
"not_available": "Nicht verfügbar",
|
"not_available": "Nicht verfügbar",
|
||||||
|
"placement": "Platzierung",
|
||||||
|
"place": "Platz",
|
||||||
"played_matches": "Gespielte Spiele",
|
"played_matches": "Gespielte Spiele",
|
||||||
"player_name": "Spieler:innenname",
|
"player_name": "Spieler:innenname",
|
||||||
"players": "Spieler:innen",
|
"players": "Spieler:innen",
|
||||||
@@ -110,6 +113,7 @@
|
|||||||
"ruleset": "Regelwerk",
|
"ruleset": "Regelwerk",
|
||||||
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
|
"ruleset_least_points": "Umgekehrte Wertung: Der/die Spieler:in mit den wenigsten Punkten gewinnt.",
|
||||||
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
|
"ruleset_most_points": "Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.",
|
||||||
|
"ruleset_placement": "Spieler:innen können in einer Reihenfolge angeordnet werden, die ihre Platzierung reflektiert.",
|
||||||
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
|
"ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.",
|
||||||
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
|
"ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.",
|
||||||
"save_changes": "Änderungen speichern",
|
"save_changes": "Änderungen speichern",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
},
|
},
|
||||||
"delete_group": "Delete Group",
|
"delete_group": "Delete Group",
|
||||||
"delete_match": "Delete Match",
|
"delete_match": "Delete Match",
|
||||||
|
"drag_to_set_placement": "Drag to set placement",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"edit_game": "Edit Game",
|
"edit_game": "Edit Game",
|
||||||
"edit_group": "Edit Group",
|
"edit_group": "Edit Group",
|
||||||
@@ -98,6 +99,8 @@
|
|||||||
"none": "None",
|
"none": "None",
|
||||||
"none_group": "None",
|
"none_group": "None",
|
||||||
"not_available": "Not available",
|
"not_available": "Not available",
|
||||||
|
"placement": "Placement",
|
||||||
|
"place": "place",
|
||||||
"played_matches": "Played Matches",
|
"played_matches": "Played Matches",
|
||||||
"player_name": "Player name",
|
"player_name": "Player name",
|
||||||
"players": "Players",
|
"players": "Players",
|
||||||
@@ -110,6 +113,7 @@
|
|||||||
"ruleset": "Ruleset",
|
"ruleset": "Ruleset",
|
||||||
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
|
"ruleset_least_points": "Inverse scoring: the player with the fewest points wins.",
|
||||||
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
|
"ruleset_most_points": "Traditional ruleset: the player with the most points wins.",
|
||||||
|
"ruleset_placement": "Players can be arranged in an order, which reflects their placement.",
|
||||||
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
|
"ruleset_single_loser": "Exactly one loser is determined; last place receives the penalty or consequence.",
|
||||||
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
|
"ruleset_single_winner": "Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.",
|
||||||
"save_changes": "Save Changes",
|
"save_changes": "Save Changes",
|
||||||
|
|||||||
@@ -320,6 +320,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Delete Match'**
|
/// **'Delete Match'**
|
||||||
String get delete_match;
|
String get delete_match;
|
||||||
|
|
||||||
|
/// No description provided for @drag_to_set_placement.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Drag to set placement'**
|
||||||
|
String get drag_to_set_placement;
|
||||||
|
|
||||||
/// No description provided for @description.
|
/// No description provided for @description.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -638,6 +644,18 @@ abstract class AppLocalizations {
|
|||||||
/// **'Not available'**
|
/// **'Not available'**
|
||||||
String get not_available;
|
String get not_available;
|
||||||
|
|
||||||
|
/// No description provided for @placement.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Placement'**
|
||||||
|
String get placement;
|
||||||
|
|
||||||
|
/// No description provided for @place.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'place'**
|
||||||
|
String get place;
|
||||||
|
|
||||||
/// No description provided for @played_matches.
|
/// No description provided for @played_matches.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
@@ -710,6 +728,12 @@ abstract class AppLocalizations {
|
|||||||
/// **'Traditional ruleset: the player with the most points wins.'**
|
/// **'Traditional ruleset: the player with the most points wins.'**
|
||||||
String get ruleset_most_points;
|
String get ruleset_most_points;
|
||||||
|
|
||||||
|
/// No description provided for @ruleset_placement.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Players can be arranged in an order, which reflects their placement.'**
|
||||||
|
String get ruleset_placement;
|
||||||
|
|
||||||
/// No description provided for @ruleset_single_loser.
|
/// No description provided for @ruleset_single_loser.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get delete_match => 'Spiel löschen';
|
String get delete_match => 'Spiel löschen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get drag_to_set_placement => 'Ziehen um Platzierung zu setzen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Beschreibung';
|
String get description => 'Beschreibung';
|
||||||
|
|
||||||
@@ -295,6 +298,12 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get not_available => 'Nicht verfügbar';
|
String get not_available => 'Nicht verfügbar';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get placement => 'Platzierung';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get place => 'Platz';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get played_matches => 'Gespielte Spiele';
|
String get played_matches => 'Gespielte Spiele';
|
||||||
|
|
||||||
@@ -333,6 +342,10 @@ class AppLocalizationsDe extends AppLocalizations {
|
|||||||
String get ruleset_most_points =>
|
String get ruleset_most_points =>
|
||||||
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.';
|
'Traditionelles Regelwerk: Der/die Spieler:in mit den meisten Punkten gewinnt.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get ruleset_placement =>
|
||||||
|
'Spieler:innen können in einer Reihenfolge angeordnet werden, die ihre Platzierung reflektiert.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ruleset_single_loser =>
|
String get ruleset_single_loser =>
|
||||||
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';
|
'Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.';
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get delete_match => 'Delete Match';
|
String get delete_match => 'Delete Match';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get drag_to_set_placement => 'Drag to set placement';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get description => 'Description';
|
String get description => 'Description';
|
||||||
|
|
||||||
@@ -295,6 +298,12 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get not_available => 'Not available';
|
String get not_available => 'Not available';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get placement => 'Placement';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get place => 'place';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get played_matches => 'Played Matches';
|
String get played_matches => 'Played Matches';
|
||||||
|
|
||||||
@@ -333,6 +342,10 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String get ruleset_most_points =>
|
String get ruleset_most_points =>
|
||||||
'Traditional ruleset: the player with the most points wins.';
|
'Traditional ruleset: the player with the most points wins.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get ruleset_placement =>
|
||||||
|
'Players can be arranged in an order, which reflects their placement.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get ruleset_single_loser =>
|
String get ruleset_single_loser =>
|
||||||
'Exactly one loser is determined; last place receives the penalty or consequence.';
|
'Exactly one loser is determined; last place receives the penalty or consequence.';
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
|
|||||||
children: getSingleResultRow(loc),
|
children: getSingleResultRow(loc),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return getScoreResultWidget(loc);
|
return getMultiResultRows(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,45 +325,78 @@ class _MatchDetailViewState extends State<MatchDetailView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the result widget for scores
|
/// Returns the result widget for scores or placement
|
||||||
Widget getScoreResultWidget(AppLocalizations loc) {
|
Widget getMultiResultRows(AppLocalizations loc) {
|
||||||
List<(String, int)> playerScores = [];
|
List<(String, int)> playerScores = [];
|
||||||
for (var player in match.players) {
|
for (var player in match.players) {
|
||||||
int score = match.scores[player.id]?.score ?? 0;
|
int score = match.scores[player.id]?.score ?? 0;
|
||||||
playerScores.add((player.name, score));
|
playerScores.add((player.name, score));
|
||||||
}
|
}
|
||||||
if (widget.match.game.ruleset == Ruleset.highestScore) {
|
|
||||||
|
final ruleset = match.game.ruleset;
|
||||||
|
|
||||||
|
if (ruleset == Ruleset.highestScore || ruleset == Ruleset.placement) {
|
||||||
playerScores.sort((a, b) => b.$2.compareTo(a.$2));
|
playerScores.sort((a, b) => b.$2.compareTo(a.$2));
|
||||||
} else if (widget.match.game.ruleset == Ruleset.lowestScore) {
|
} else if (ruleset == Ruleset.lowestScore) {
|
||||||
playerScores.sort((a, b) => a.$2.compareTo(b.$2));
|
playerScores.sort((a, b) => a.$2.compareTo(b.$2));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
for (var score in playerScores)
|
for (var i = 0; i < playerScores.length; i++)
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
score.$1,
|
playerScores[i].$1,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: CustomTheme.textColor,
|
color: CustomTheme.textColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
getResultValueText(loc, i, playerScores[i].$2),
|
||||||
getPointLabel(loc, score.$2),
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget getResultValueText(AppLocalizations loc, int index, int score) {
|
||||||
|
final ruleset = match.game.ruleset;
|
||||||
|
|
||||||
|
if (ruleset == Ruleset.placement) {
|
||||||
|
return Text(
|
||||||
|
getPlacementText(context, index + 1),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: getPlacementTextcolor(index),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Text(
|
||||||
|
getPointLabel(loc, score),
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: CustomTheme.primaryColor,
|
color: CustomTheme.primaryColor,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getPlacementTextcolor(int placement) {
|
||||||
|
switch (placement) {
|
||||||
|
case 0:
|
||||||
|
return const Color(0xFFFFBF00);
|
||||||
|
case 1:
|
||||||
|
return const Color(0xBBFFFFFF);
|
||||||
|
case 2:
|
||||||
|
return const Color(0xFFCD7F32);
|
||||||
|
default:
|
||||||
|
return CustomTheme.textColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns if the result can be displayed in a single row
|
// Returns if the result can be displayed in a single row
|
||||||
bool isSingleRowResult() {
|
bool isSingleRowResult() {
|
||||||
@@ -371,6 +404,34 @@ class _MatchDetailViewState extends State<MatchDetailView> {
|
|||||||
match.game.ruleset == Ruleset.singleLoser;
|
match.game.ruleset == Ruleset.singleLoser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getPlacementText(BuildContext context, int rank) {
|
||||||
|
final loc = AppLocalizations.of(context);
|
||||||
|
final locale = Localizations.localeOf(context).languageCode;
|
||||||
|
|
||||||
|
if (locale == 'de') {
|
||||||
|
return '$rank. ${loc.place}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '${_ordinalEn(rank)} ${loc.place}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _ordinalEn(int number) {
|
||||||
|
if (number % 100 >= 11 && number % 100 <= 13) {
|
||||||
|
return '${number}th';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (number % 10) {
|
||||||
|
case 1:
|
||||||
|
return '${number}st';
|
||||||
|
case 2:
|
||||||
|
return '${number}nd';
|
||||||
|
case 3:
|
||||||
|
return '${number}rd';
|
||||||
|
default:
|
||||||
|
return '${number}th';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateScoresForCurrentMatch() {
|
void updateScoresForCurrentMatch() {
|
||||||
db.scoreEntryDao
|
db.scoreEntryDao
|
||||||
.getAllMatchScores(matchId: match.id)
|
.getAllMatchScores(matchId: match.id)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
|
|||||||
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_radio_list_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/live_edit_list_tile.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/match_result_view/score_list_tile.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||||
|
|
||||||
class MatchResultView extends StatefulWidget {
|
class MatchResultView extends StatefulWidget {
|
||||||
/// A view that allows selecting and saving the winner of a match
|
/// A view that allows selecting and saving the winner of a match
|
||||||
@@ -50,7 +51,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
ruleset = Ruleset.highestScore; //widget.match.game.ruleset;
|
ruleset = widget.match.game.ruleset;
|
||||||
canSave = !rulesetSupportsScoreEntry();
|
canSave = !rulesetSupportsScoreEntry();
|
||||||
|
|
||||||
allPlayers = widget.match.players;
|
allPlayers = widget.match.players;
|
||||||
@@ -73,6 +74,12 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
final score = scoreList?.score ?? 0;
|
final score = scoreList?.score ?? 0;
|
||||||
controller[i].text = score.toString();
|
controller[i].text = score.toString();
|
||||||
}
|
}
|
||||||
|
} else if (rulesetSupportsPlacement()) {
|
||||||
|
allPlayers.sort((a, b) {
|
||||||
|
final scoreA = widget.match.scores[a.id]?.score ?? 0;
|
||||||
|
final scoreB = widget.match.scores[b.id]?.score ?? 0;
|
||||||
|
return scoreB.compareTo(scoreA);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
@@ -106,7 +113,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isLiveEditMode && rulesetSupportsScoreEntry()
|
child: isLiveEditMode
|
||||||
// Live Edit Mode
|
// Live Edit Mode
|
||||||
? ListView.builder(
|
? ListView.builder(
|
||||||
itemCount: allPlayers.length,
|
itemCount: allPlayers.length,
|
||||||
@@ -122,7 +129,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
// Normal Mode
|
// Normal Container
|
||||||
: Container(
|
: Container(
|
||||||
margin: const EdgeInsets.symmetric(
|
margin: const EdgeInsets.symmetric(
|
||||||
horizontal: 12,
|
horizontal: 12,
|
||||||
@@ -161,6 +168,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: allPlayers.length,
|
itemCount: allPlayers.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return CustomRadioListTile(
|
return CustomRadioListTile(
|
||||||
@@ -183,6 +191,7 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Show score entry
|
// Show score entry
|
||||||
if (rulesetSupportsScoreEntry())
|
if (rulesetSupportsScoreEntry())
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -205,6 +214,111 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Show draggable placement list
|
||||||
|
if (rulesetSupportsPlacement())
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// Placement indicators
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 8.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
for (
|
||||||
|
int i = 0;
|
||||||
|
i < allPlayers.length;
|
||||||
|
i++
|
||||||
|
)
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 60,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
CustomTheme.boxBorderColor,
|
||||||
|
borderRadius: CustomTheme
|
||||||
|
.standardBorderRadiusAll,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
child: Text(
|
||||||
|
' #${i + 1} ',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Drag list
|
||||||
|
Expanded(
|
||||||
|
child: ReorderableListView.builder(
|
||||||
|
physics:
|
||||||
|
const NeverScrollableScrollPhysics(),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
proxyDecorator: (child, index, animation) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: animation,
|
||||||
|
child: child,
|
||||||
|
builder: (context, child) {
|
||||||
|
final alpha =
|
||||||
|
(Curves.easeInOut.transform(
|
||||||
|
animation.value,
|
||||||
|
) *
|
||||||
|
40)
|
||||||
|
.toInt();
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
child!,
|
||||||
|
Positioned.fill(
|
||||||
|
left: 4,
|
||||||
|
top: 4,
|
||||||
|
right: 4,
|
||||||
|
bottom: 4,
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white
|
||||||
|
.withAlpha(alpha),
|
||||||
|
borderRadius: CustomTheme
|
||||||
|
.standardBorderRadiusAll,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
|
setState(() {
|
||||||
|
if (newIndex > oldIndex) {
|
||||||
|
newIndex -= 1;
|
||||||
|
}
|
||||||
|
final Player item = allPlayers
|
||||||
|
.removeAt(oldIndex);
|
||||||
|
allPlayers.insert(newIndex, item);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
itemCount: allPlayers.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return TextIconListTile(
|
||||||
|
key: ValueKey(allPlayers[index].id),
|
||||||
|
text: allPlayers[index].name,
|
||||||
|
icon: Icons.drag_handle,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -266,6 +380,8 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
} else if (ruleset == Ruleset.lowestScore ||
|
} else if (ruleset == Ruleset.lowestScore ||
|
||||||
ruleset == Ruleset.highestScore) {
|
ruleset == Ruleset.highestScore) {
|
||||||
await _handleScores();
|
await _handleScores();
|
||||||
|
} else if (ruleset == Ruleset.placement) {
|
||||||
|
await _handlePlacement();
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.onWinnerChanged?.call();
|
widget.onWinnerChanged?.call();
|
||||||
@@ -311,12 +427,22 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles saving the placement for each player in the database.
|
||||||
|
Future<void> _handlePlacement() async {
|
||||||
|
await db.scoreEntryDao.setPlacements(
|
||||||
|
matchId: widget.match.id,
|
||||||
|
players: allPlayers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
String getTitleForRuleset(AppLocalizations loc) {
|
String getTitleForRuleset(AppLocalizations loc) {
|
||||||
switch (ruleset) {
|
switch (ruleset) {
|
||||||
case Ruleset.singleWinner:
|
case Ruleset.singleWinner:
|
||||||
return loc.select_winner;
|
return loc.select_winner;
|
||||||
case Ruleset.singleLoser:
|
case Ruleset.singleLoser:
|
||||||
return loc.select_loser;
|
return loc.select_loser;
|
||||||
|
case Ruleset.placement:
|
||||||
|
return loc.drag_to_set_placement;
|
||||||
default:
|
default:
|
||||||
return loc.enter_points;
|
return loc.enter_points;
|
||||||
}
|
}
|
||||||
@@ -329,4 +455,8 @@ class _MatchResultViewState extends State<MatchResultView> {
|
|||||||
bool rulesetSupportsScoreEntry() {
|
bool rulesetSupportsScoreEntry() {
|
||||||
return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore;
|
return ruleset == Ruleset.lowestScore || ruleset == Ruleset.highestScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rulesetSupportsPlacement() {
|
||||||
|
return ruleset == Ruleset.placement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
return TextIconListTile(
|
return TextIconListTile(
|
||||||
text: suggestedPlayers[index].name,
|
text: suggestedPlayers[index].name,
|
||||||
suffixText: getNameCountText(suggestedPlayers[index]),
|
suffixText: getNameCountText(suggestedPlayers[index]),
|
||||||
|
icon: Icons.add,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
// If the player is not already selected
|
// If the player is not already selected
|
||||||
|
|||||||
@@ -261,30 +261,29 @@ class _MatchTileState extends State<MatchTile> {
|
|||||||
final mvp = widget.match.mvp;
|
final mvp = widget.match.mvp;
|
||||||
final mvpScore = widget.match.scores[mvp.first.id]?.score ?? 0;
|
final mvpScore = widget.match.scores[mvp.first.id]?.score ?? 0;
|
||||||
final mvpNames = mvp.map((player) => player.name).join(', ');
|
final mvpNames = mvp.map((player) => player.name).join(', ');
|
||||||
|
|
||||||
return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})';
|
return '${loc.winner}: $mvpNames (${getPointLabel(loc, mvpScore)})';
|
||||||
|
} else if (ruleset == Ruleset.placement) {
|
||||||
|
return '${loc.winner}: ${widget.match.mvp.first.name}';
|
||||||
}
|
}
|
||||||
return '${loc.winner}: n.A.';
|
return '${loc.winner}: n.A.';
|
||||||
}
|
}
|
||||||
|
|
||||||
Icon getMvpIcon() {
|
Icon getMvpIcon() {
|
||||||
const Icon(Icons.emoji_events, size: 20, color: Colors.amber);
|
final icon = getRulesetIcon(widget.match.game.ruleset);
|
||||||
|
|
||||||
switch (widget.match.game.ruleset) {
|
switch (widget.match.game.ruleset) {
|
||||||
case Ruleset.singleWinner:
|
case Ruleset.singleWinner:
|
||||||
return const Icon(Icons.emoji_events, size: 20, color: Colors.amber);
|
return Icon(icon, size: 20, color: Colors.amber);
|
||||||
case Ruleset.singleLoser:
|
case Ruleset.singleLoser:
|
||||||
return const Icon(
|
return Icon(icon, size: 20, color: Colors.blue);
|
||||||
Icons.sentiment_dissatisfied_outlined,
|
|
||||||
size: 20,
|
|
||||||
color: Colors.blue,
|
|
||||||
);
|
|
||||||
case Ruleset.lowestScore:
|
case Ruleset.lowestScore:
|
||||||
return const Icon(Icons.arrow_downward, size: 20, color: Colors.orange);
|
return Icon(icon, size: 20, color: Colors.orange);
|
||||||
case Ruleset.highestScore:
|
case Ruleset.highestScore:
|
||||||
return const Icon(Icons.arrow_upward, size: 20, color: Colors.green);
|
return Icon(icon, size: 20, color: Colors.green);
|
||||||
default:
|
case Ruleset.multipleWinners:
|
||||||
return const Icon(Icons.emoji_events, size: 20, color: Colors.amber);
|
return Icon(icon, size: 20, color: Colors.amber);
|
||||||
|
case Ruleset.placement:
|
||||||
|
return Icon(icon, size: 20, color: Colors.deepOrangeAccent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class TextIconListTile extends StatelessWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.text,
|
required this.text,
|
||||||
this.suffixText = '',
|
this.suffixText = '',
|
||||||
this.iconEnabled = true,
|
this.icon,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ class TextIconListTile extends StatelessWidget {
|
|||||||
/// An optional suffix text to display after the main text.
|
/// An optional suffix text to display after the main text.
|
||||||
final String suffixText;
|
final String suffixText;
|
||||||
|
|
||||||
/// A boolean to determine if the icon should be displayed.
|
/// The icon to display in the tile.
|
||||||
final bool iconEnabled;
|
final IconData? icon;
|
||||||
|
|
||||||
/// The callback to be invoked when the icon is pressed.
|
/// The callback to be invoked when the icon is pressed.
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
@@ -64,11 +64,8 @@ class TextIconListTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (iconEnabled)
|
if (icon != null)
|
||||||
GestureDetector(
|
GestureDetector(onTap: onPressed, child: Icon(icon, size: 20)),
|
||||||
onTap: onPressed,
|
|
||||||
child: const Icon(Icons.add, size: 20),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user