Add support for selecting multiple winners and update localization
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 48s
Pull Request Pipeline / lint (pull_request) Failing after 51s

This commit is contained in:
2026-05-10 14:54:00 +02:00
parent 881382b399
commit 03ab2045b2
8 changed files with 119 additions and 1 deletions

View File

@@ -120,6 +120,7 @@
"search_for_groups": "Nach Gruppen suchen",
"search_for_players": "Nach Spieler:innen suchen",
"select_winner": "Gewinner:in wählen",
"select_winners": "Gewinner:innen wählen",
"select_loser": "Verlierer:in wählen",
"selected_players": "Ausgewählte Spieler:innen",
"settings": "Einstellungen",

View File

@@ -120,6 +120,7 @@
"search_for_groups": "Search for groups",
"search_for_players": "Search for players",
"select_winner": "Select Winner",
"select_winners": "Select Winners",
"select_loser": "Select Loser",
"selected_players": "Selected players",
"settings": "Settings",

View File

@@ -770,6 +770,12 @@ abstract class AppLocalizations {
/// **'Select Winner'**
String get select_winner;
/// No description provided for @select_winners.
///
/// In en, this message translates to:
/// **'Select Winners'**
String get select_winners;
/// No description provided for @select_loser.
///
/// In en, this message translates to:

View File

@@ -366,6 +366,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get select_winner => 'Gewinner:in wählen';
@override
String get select_winners => 'Gewinner:innen wählen';
@override
String get select_loser => 'Verlierer:in wählen';

View File

@@ -366,6 +366,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get select_winner => 'Select Winner';
@override
String get select_winners => 'Select Winners';
@override
String get select_loser => 'Select Loser';

View File

@@ -269,6 +269,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
/// Returns the widget to be displayed in the result [InfoTile]
Widget getResultWidget(AppLocalizations loc) {
///TODO: add support for multiple winners
if (isSingleRowResult()) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@@ -8,6 +8,7 @@ import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/tiles/match_result_view/custom_checkbox_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/score_list_tile.dart';
@@ -45,9 +46,12 @@ class _MatchResultViewState extends State<MatchResultView> {
/// Flag to indicate if the save button should be enabled
late bool canSave;
/// Currently selected winner player
/// Currently selected winner player (single winner)
Player? _selectedPlayer;
/// Currently selected winners (multiple winners)
Set<String> _selectedWinners = {};
@override
void initState() {
db = Provider.of<AppDatabase>(context, listen: false);
@@ -80,7 +84,10 @@ class _MatchResultViewState extends State<MatchResultView> {
final scoreB = widget.match.scores[b.id]?.score ?? 0;
return scoreB.compareTo(scoreA);
});
} else if (rulesetSupportsMultipleWinners()) {
//TODO: Implement winners pre filling
}
;
super.initState();
}
}
@@ -319,6 +326,36 @@ class _MatchResultViewState extends State<MatchResultView> {
],
),
),
// Show multiple winner selection
if (rulesetSupportsMultipleWinners())
Expanded(
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return CustomCheckboxListTile(
text: allPlayers[index].name,
value: _selectedWinners.contains(
allPlayers[index].id,
),
onChanged: (bool value) {
setState(() {
if (value) {
_selectedWinners.add(
allPlayers[index].id,
);
} else {
_selectedWinners.remove(
allPlayers[index].id,
);
}
});
},
);
},
),
),
],
),
),
@@ -382,6 +419,8 @@ class _MatchResultViewState extends State<MatchResultView> {
await _handleScores();
} else if (ruleset == Ruleset.placement) {
await _handlePlacement();
} else if (ruleset == Ruleset.multipleWinners) {
await _handleWinners();
}
widget.onWinnerChanged?.call();
@@ -399,6 +438,12 @@ class _MatchResultViewState extends State<MatchResultView> {
}
}
/// Handles saving the winners to the database.
Future<bool> _handleWinners() async {
//TODO: Implement winner handling
return true;
}
/// Handles saving or removing the loser in the database.
Future<bool> _handleLoser() async {
if (_selectedPlayer == null) {
@@ -443,6 +488,8 @@ class _MatchResultViewState extends State<MatchResultView> {
return loc.select_loser;
case Ruleset.placement:
return loc.drag_to_set_placement;
case Ruleset.multipleWinners:
return loc.select_winners;
default:
return loc.enter_points;
}
@@ -459,4 +506,8 @@ class _MatchResultViewState extends State<MatchResultView> {
bool rulesetSupportsPlacement() {
return ruleset == Ruleset.placement;
}
bool rulesetSupportsMultipleWinners() {
return ruleset == Ruleset.multipleWinners;
}
}

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:tallee/core/custom_theme.dart';
class CustomCheckboxListTile extends StatelessWidget {
const CustomCheckboxListTile({
super.key,
required this.text,
required this.value,
required this.onChanged,
});
final String text;
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onChanged(!value),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: CustomTheme.boxColor,
border: Border.all(color: CustomTheme.boxBorderColor),
borderRadius: CustomTheme.standardBorderRadiusAll,
),
child: Row(
children: [
Checkbox(
value: value,
onChanged: (bool? v) {
if (v == null) return;
onChanged(v);
},
),
Expanded(
child: Text(
text,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
],
),
),
);
}
}