From 03ab2045b2f8924ea8cbe14e6f39679708acee94 Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Sun, 10 May 2026 14:54:00 +0200 Subject: [PATCH] Add support for selecting multiple winners and update localization --- lib/l10n/arb/app_de.arb | 1 + lib/l10n/arb/app_en.arb | 1 + lib/l10n/generated/app_localizations.dart | 6 +++ lib/l10n/generated/app_localizations_de.dart | 3 ++ lib/l10n/generated/app_localizations_en.dart | 3 ++ .../match_view/match_detail_view.dart | 1 + .../match_view/match_result_view.dart | 53 ++++++++++++++++++- .../custom_checkbox_list_tile.dart | 52 ++++++++++++++++++ 8 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 5492bab..622d9cb 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -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", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 7fb944b..b5d617e 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -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", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index bfdb659..dd7617e 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -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: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 8567ba0..e9e6451 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -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'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 04e68b4..3b90592 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -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'; diff --git a/lib/presentation/views/main_menu/match_view/match_detail_view.dart b/lib/presentation/views/main_menu/match_view/match_detail_view.dart index 86c26c6..d8cd627 100644 --- a/lib/presentation/views/main_menu/match_view/match_detail_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_detail_view.dart @@ -269,6 +269,7 @@ class _MatchDetailViewState extends State { /// 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, diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 61b2a55..fd2c35a 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -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 { /// 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 _selectedWinners = {}; + @override void initState() { db = Provider.of(context, listen: false); @@ -80,7 +84,10 @@ class _MatchResultViewState extends State { 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 { ], ), ), + + // 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 { 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 { } } + /// Handles saving the winners to the database. + Future _handleWinners() async { + //TODO: Implement winner handling + return true; + } + /// Handles saving or removing the loser in the database. Future _handleLoser() async { if (_selectedPlayer == null) { @@ -443,6 +488,8 @@ class _MatchResultViewState extends State { 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 { bool rulesetSupportsPlacement() { return ruleset == Ruleset.placement; } + + bool rulesetSupportsMultipleWinners() { + return ruleset == Ruleset.multipleWinners; + } } diff --git a/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart b/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart new file mode 100644 index 0000000..77c9242 --- /dev/null +++ b/lib/presentation/widgets/tiles/match_result_view/custom_checkbox_list_tile.dart @@ -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 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, + ), + ), + ), + ], + ), + ), + ); + } +}