From 9ad50c9f9c28fd4ca0e4f000675a854b2e56c7b6 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 23 May 2026 21:52:32 +0200 Subject: [PATCH 01/39] Removed bg color --- .../main_menu/match_view/create_match/create_game_view.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 998f4e1..a5729be 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -117,7 +117,6 @@ class _CreateGameViewState extends State { return ScaffoldMessenger( child: Scaffold( - backgroundColor: CustomTheme.backgroundColor, appBar: AppBar( title: Text(isEditing ? loc.edit_game : loc.create_game), actions: [ -- 2.49.1 From 134f77c5a3160eb5197c9906d9de8e638c9570d5 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 01:26:08 +0200 Subject: [PATCH 02/39] feat: create statistics view --- lib/core/enums.dart | 28 + lib/data/models/statistic.dart | 19 + lib/l10n/arb/app_de.arb | 35 + lib/l10n/arb/app_en.arb | 35 + lib/l10n/generated/app_localizations.dart | 210 ++++++ lib/l10n/generated/app_localizations_de.dart | 110 +++ lib/l10n/generated/app_localizations_en.dart | 108 +++ .../main_menu/custom_navigation_bar.dart | 2 +- .../create_statistic_view.dart | 635 ++++++++++++++++++ .../statistics_view.dart | 169 +++-- pubspec.lock | 16 + pubspec.yaml | 2 + 12 files changed, 1296 insertions(+), 73 deletions(-) create mode 100644 lib/data/models/statistic.dart create mode 100644 lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart rename lib/presentation/views/main_menu/{ => statistics_view}/statistics_view.dart (62%) diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 99141e4..5d46a3f 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -44,3 +44,31 @@ enum Ruleset { /// Different colors for highlighting games enum GameColor { red, orange, yellow, green, teal, blue, purple, pink } + +enum StatisticType { + totalMatches, + totalWins, + totalScore, + totalLosses, + averageScore, + bestScore, + worstScore, + winrate, +} + +enum StatisticScope { + allPlayers, + //selectedPlayer, + selectedGroups, + selectedGames, + timeframe, +} + +enum Timeframe { + last7Days, + last30Days, + last90Days, + last180Days, + lastYear, + allTime, +} diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart new file mode 100644 index 0000000..4b5df07 --- /dev/null +++ b/lib/data/models/statistic.dart @@ -0,0 +1,19 @@ +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; + +class Statistic { + final StatisticType type; + final List scopes; + final Timeframe? timeframe; + final List? selectedGroups; + final List? selectedGames; + + Statistic({ + required this.type, + required this.scopes, + this.timeframe, + this.selectedGroups, + this.selectedGames, + }); +} diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 610d2c9..8f92cb0 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -26,6 +26,17 @@ "create_match": "Spiel erstellen", "create_new_group": "Neue Gruppe erstellen", "create_new_match": "Neues Spiel erstellen", + "create_statistic": "Statistik erstellen", + "create_statistic_classifier_subtitle": "Wähle die anzuzeigende Hauptmetrik aus", + "create_statistic_classifier_title": "Klassifikator", + "create_statistic_games_subtitle": "Wähle die gefilterten Spielvorlagen", + "create_statistic_games_title": "Spielvorlagen", + "create_statistic_groups_subtitle": "Wähle die gefilterten Gruppen", + "create_statistic_groups_title": "Gruppen", + "create_statistic_scope_subtitle": "Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.", + "create_statistic_scope_title": "Bereich", + "create_statistic_timeframe_subtitle": "Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.", + "create_statistic_timeframe_title": "Zeitraum", "created_on": "Erstellt am", "data": "Daten", "data_successfully_deleted": "Daten erfolgreich gelöscht", @@ -82,6 +93,7 @@ "legal_notice": "Impressum", "licenses": "Lizenzen", "live_edit_mode": "Live-Bearbeitungsmodus", + "loading": "Lädt...", "loser": "Verlierer:in", "lowest_score": "Niedrigste Punkte", "match_in_progress": "Spiel läuft...", @@ -134,6 +146,11 @@ "save_changes": "Änderungen speichern", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", + "select_a_classifier": "Klassifikator auswählen", + "select_a_game": "Spielvorlage auswählen", + "select_a_group": "Gruppe auswählen", + "select_a_scope": "Bereich auswählen", + "select_a_timeframe": "Zeitraum auswählen", "select_loser": "Verlierer:in wählen", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", @@ -142,6 +159,18 @@ "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", + "statistic_scope_all_players": "Alle Spieler:innen", + "statistic_scope_selected_games": "Ausgewählte Spielvorlagen", + "statistic_scope_selected_groups": "Ausgewählte Gruppen", + "statistic_scope_timeframe": "Zeitraum", + "statistic_type_average_score": "Durchschnittliche Punktzahl", + "statistic_type_best_score": "Beste Punktzahl", + "statistic_type_total_losses": "Niederlagen insgesamt", + "statistic_type_total_matches": "Spiele insgesamt", + "statistic_type_total_score": "Punktzahl insgesamt", + "statistic_type_total_wins": "Siege insgesamt", + "statistic_type_winrate": "Siegquote", + "statistic_type_worst_score": "Schlechteste Punktzahl", "statistics": "Statistiken", "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", @@ -149,6 +178,12 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", + "timeframe_all_time": "Gesamter Zeitraum", + "timeframe_last_180_days": "Letzte 180 Tage", + "timeframe_last_30_days": "Letzte 30 Tage", + "timeframe_last_7_days": "Letzte 7 Tage", + "timeframe_last_90_days": "Letzte 90 Tage", + "timeframe_last_year": "Letztes Jahr", "today_at": "Heute um", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index a8b0634..b7548bd 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -26,6 +26,17 @@ "create_match": "Create match", "create_new_group": "Create new group", "create_new_match": "Create new match", + "create_statistic": "Create statistic", + "create_statistic_classifier_subtitle": "Select which key metric you want to display", + "create_statistic_classifier_title": "Classifier", + "create_statistic_games_subtitle": "Select the filtered games", + "create_statistic_games_title": "Games", + "create_statistic_groups_subtitle": "Select the filtered groups", + "create_statistic_groups_title": "Groups", + "create_statistic_scope_subtitle": "Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.", + "create_statistic_scope_title": "Scope", + "create_statistic_timeframe_subtitle": "Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.", + "create_statistic_timeframe_title": "Timeframe", "created_on": "Created on", "data": "Data", "data_successfully_deleted": "Data successfully deleted", @@ -82,6 +93,7 @@ "legal_notice": "Legal Notice", "licenses": "Licenses", "live_edit_mode": "Live Edit Mode", + "loading": "Loading...", "loser": "Loser", "lowest_score": "Lowest Score", "match_in_progress": "Match in progress...", @@ -139,10 +151,27 @@ "selected_players": "Selected players", "set_name": "Set name", "settings": "Settings", + "select_a_classifier": "Select a classifier", + "select_a_game": "Select a game", + "select_a_group": "Select a group", + "select_a_scope": "Select a scope", + "select_a_timeframe": "Select a timeframe", "single_loser": "Single Loser", "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", + "statistic_scope_all_players": "All players", + "statistic_scope_selected_games": "Selected games", + "statistic_scope_selected_groups": "Selected groups", + "statistic_scope_timeframe": "Timeframe", + "statistic_type_average_score": "Average score", + "statistic_type_best_score": "Best score", + "statistic_type_total_losses": "Total losses", + "statistic_type_total_matches": "Total matches", + "statistic_type_total_score": "Total score", + "statistic_type_total_wins": "Total wins", + "statistic_type_winrate": "Winrate", + "statistic_type_worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -157,6 +186,12 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", + "timeframe_all_time": "All time", + "timeframe_last_180_days": "Last 180 days", + "timeframe_last_30_days": "Last 30 days", + "timeframe_last_7_days": "Last 7 days", + "timeframe_last_90_days": "Last 90 days", + "timeframe_last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 4cee263..e8af2e8 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -254,6 +254,72 @@ abstract class AppLocalizations { /// **'Create new match'** String get create_new_match; + /// No description provided for @create_statistic. + /// + /// In en, this message translates to: + /// **'Create statistic'** + String get create_statistic; + + /// No description provided for @create_statistic_classifier_subtitle. + /// + /// In en, this message translates to: + /// **'Select which key metric you want to display'** + String get create_statistic_classifier_subtitle; + + /// No description provided for @create_statistic_classifier_title. + /// + /// In en, this message translates to: + /// **'Classifier'** + String get create_statistic_classifier_title; + + /// No description provided for @create_statistic_games_subtitle. + /// + /// In en, this message translates to: + /// **'Select the filtered games'** + String get create_statistic_games_subtitle; + + /// No description provided for @create_statistic_games_title. + /// + /// In en, this message translates to: + /// **'Games'** + String get create_statistic_games_title; + + /// No description provided for @create_statistic_groups_subtitle. + /// + /// In en, this message translates to: + /// **'Select the filtered groups'** + String get create_statistic_groups_subtitle; + + /// No description provided for @create_statistic_groups_title. + /// + /// In en, this message translates to: + /// **'Groups'** + String get create_statistic_groups_title; + + /// No description provided for @create_statistic_scope_subtitle. + /// + /// In en, this message translates to: + /// **'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'** + String get create_statistic_scope_subtitle; + + /// No description provided for @create_statistic_scope_title. + /// + /// In en, this message translates to: + /// **'Scope'** + String get create_statistic_scope_title; + + /// No description provided for @create_statistic_timeframe_subtitle. + /// + /// In en, this message translates to: + /// **'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'** + String get create_statistic_timeframe_subtitle; + + /// No description provided for @create_statistic_timeframe_title. + /// + /// In en, this message translates to: + /// **'Timeframe'** + String get create_statistic_timeframe_title; + /// No description provided for @created_on. /// /// In en, this message translates to: @@ -548,6 +614,12 @@ abstract class AppLocalizations { /// **'Live Edit Mode'** String get live_edit_mode; + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loading; + /// No description provided for @loser. /// /// In en, this message translates to: @@ -890,6 +962,36 @@ abstract class AppLocalizations { /// **'Settings'** String get settings; + /// No description provided for @select_a_classifier. + /// + /// In en, this message translates to: + /// **'Select a classifier'** + String get select_a_classifier; + + /// No description provided for @select_a_game. + /// + /// In en, this message translates to: + /// **'Select a game'** + String get select_a_game; + + /// No description provided for @select_a_group. + /// + /// In en, this message translates to: + /// **'Select a group'** + String get select_a_group; + + /// No description provided for @select_a_scope. + /// + /// In en, this message translates to: + /// **'Select a scope'** + String get select_a_scope; + + /// No description provided for @select_a_timeframe. + /// + /// In en, this message translates to: + /// **'Select a timeframe'** + String get select_a_timeframe; + /// No description provided for @single_loser. /// /// In en, this message translates to: @@ -914,6 +1016,78 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; + /// No description provided for @statistic_scope_all_players. + /// + /// In en, this message translates to: + /// **'All players'** + String get statistic_scope_all_players; + + /// No description provided for @statistic_scope_selected_games. + /// + /// In en, this message translates to: + /// **'Selected games'** + String get statistic_scope_selected_games; + + /// No description provided for @statistic_scope_selected_groups. + /// + /// In en, this message translates to: + /// **'Selected groups'** + String get statistic_scope_selected_groups; + + /// No description provided for @statistic_scope_timeframe. + /// + /// In en, this message translates to: + /// **'Timeframe'** + String get statistic_scope_timeframe; + + /// No description provided for @statistic_type_average_score. + /// + /// In en, this message translates to: + /// **'Average score'** + String get statistic_type_average_score; + + /// No description provided for @statistic_type_best_score. + /// + /// In en, this message translates to: + /// **'Best score'** + String get statistic_type_best_score; + + /// No description provided for @statistic_type_total_losses. + /// + /// In en, this message translates to: + /// **'Total losses'** + String get statistic_type_total_losses; + + /// No description provided for @statistic_type_total_matches. + /// + /// In en, this message translates to: + /// **'Total matches'** + String get statistic_type_total_matches; + + /// No description provided for @statistic_type_total_score. + /// + /// In en, this message translates to: + /// **'Total score'** + String get statistic_type_total_score; + + /// No description provided for @statistic_type_total_wins. + /// + /// In en, this message translates to: + /// **'Total wins'** + String get statistic_type_total_wins; + + /// No description provided for @statistic_type_winrate. + /// + /// In en, this message translates to: + /// **'Winrate'** + String get statistic_type_winrate; + + /// No description provided for @statistic_type_worst_score. + /// + /// In en, this message translates to: + /// **'Worst score'** + String get statistic_type_worst_score; + /// Success message when adding a player /// /// In en, this message translates to: @@ -944,6 +1118,42 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; + /// No description provided for @timeframe_all_time. + /// + /// In en, this message translates to: + /// **'All time'** + String get timeframe_all_time; + + /// No description provided for @timeframe_last_180_days. + /// + /// In en, this message translates to: + /// **'Last 180 days'** + String get timeframe_last_180_days; + + /// No description provided for @timeframe_last_30_days. + /// + /// In en, this message translates to: + /// **'Last 30 days'** + String get timeframe_last_30_days; + + /// No description provided for @timeframe_last_7_days. + /// + /// In en, this message translates to: + /// **'Last 7 days'** + String get timeframe_last_7_days; + + /// No description provided for @timeframe_last_90_days. + /// + /// In en, this message translates to: + /// **'Last 90 days'** + String get timeframe_last_90_days; + + /// No description provided for @timeframe_last_year. + /// + /// In en, this message translates to: + /// **'Last year'** + String get timeframe_last_year; + /// No description provided for @today_at. /// /// 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 66dca88..4ff81bb 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -88,6 +88,44 @@ class AppLocalizationsDe extends AppLocalizations { @override String get create_new_match => 'Neues Spiel erstellen'; + @override + String get create_statistic => 'Statistik erstellen'; + + @override + String get create_statistic_classifier_subtitle => + 'Wähle die anzuzeigende Hauptmetrik aus'; + + @override + String get create_statistic_classifier_title => 'Klassifikator'; + + @override + String get create_statistic_games_subtitle => + 'Wähle die gefilterten Spielvorlagen'; + + @override + String get create_statistic_games_title => 'Spielvorlagen'; + + @override + String get create_statistic_groups_subtitle => + 'Wähle die gefilterten Gruppen'; + + @override + String get create_statistic_groups_title => 'Gruppen'; + + @override + String get create_statistic_scope_subtitle => + 'Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.'; + + @override + String get create_statistic_scope_title => 'Bereich'; + + @override + String get create_statistic_timeframe_subtitle => + 'Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.'; + + @override + String get create_statistic_timeframe_title => 'Zeitraum'; + @override String get created_on => 'Erstellt am'; @@ -249,6 +287,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get live_edit_mode => 'Live-Bearbeitungsmodus'; + @override + String get loading => 'Lädt...'; + @override String get loser => 'Verlierer:in'; @@ -426,6 +467,21 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settings => 'Einstellungen'; + @override + String get select_a_classifier => 'Klassifikator auswählen'; + + @override + String get select_a_game => 'Spielvorlage auswählen'; + + @override + String get select_a_group => 'Gruppe auswählen'; + + @override + String get select_a_scope => 'Bereich auswählen'; + + @override + String get select_a_timeframe => 'Zeitraum auswählen'; + @override String get single_loser => 'Ein:e Verlierer:in'; @@ -438,6 +494,42 @@ class AppLocalizationsDe extends AppLocalizations { @override String get stats => 'Statistiken'; + @override + String get statistic_scope_all_players => 'Alle Spieler:innen'; + + @override + String get statistic_scope_selected_games => 'Ausgewählte Spielvorlagen'; + + @override + String get statistic_scope_selected_groups => 'Ausgewählte Gruppen'; + + @override + String get statistic_scope_timeframe => 'Zeitraum'; + + @override + String get statistic_type_average_score => 'Durchschnittliche Punktzahl'; + + @override + String get statistic_type_best_score => 'Beste Punktzahl'; + + @override + String get statistic_type_total_losses => 'Niederlagen insgesamt'; + + @override + String get statistic_type_total_matches => 'Spiele insgesamt'; + + @override + String get statistic_type_total_score => 'Punktzahl insgesamt'; + + @override + String get statistic_type_total_wins => 'Siege insgesamt'; + + @override + String get statistic_type_winrate => 'Siegquote'; + + @override + String get statistic_type_worst_score => 'Schlechteste Punktzahl'; + @override String successfully_added_player(String playerName) { return 'Spieler:in $playerName erfolgreich hinzugefügt'; @@ -458,6 +550,24 @@ class AppLocalizationsDe extends AppLocalizations { @override String get tie => 'Unentschieden'; + @override + String get timeframe_all_time => 'Gesamter Zeitraum'; + + @override + String get timeframe_last_180_days => 'Letzte 180 Tage'; + + @override + String get timeframe_last_30_days => 'Letzte 30 Tage'; + + @override + String get timeframe_last_7_days => 'Letzte 7 Tage'; + + @override + String get timeframe_last_90_days => 'Letzte 90 Tage'; + + @override + String get timeframe_last_year => 'Letztes Jahr'; + @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index b9e467a..4bc1dd7 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -88,6 +88,42 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_new_match => 'Create new match'; + @override + String get create_statistic => 'Create statistic'; + + @override + String get create_statistic_classifier_subtitle => + 'Select which key metric you want to display'; + + @override + String get create_statistic_classifier_title => 'Classifier'; + + @override + String get create_statistic_games_subtitle => 'Select the filtered games'; + + @override + String get create_statistic_games_title => 'Games'; + + @override + String get create_statistic_groups_subtitle => 'Select the filtered groups'; + + @override + String get create_statistic_groups_title => 'Groups'; + + @override + String get create_statistic_scope_subtitle => + 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; + + @override + String get create_statistic_scope_title => 'Scope'; + + @override + String get create_statistic_timeframe_subtitle => + 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; + + @override + String get create_statistic_timeframe_title => 'Timeframe'; + @override String get created_on => 'Created on'; @@ -249,6 +285,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get live_edit_mode => 'Live Edit Mode'; + @override + String get loading => 'Loading...'; + @override String get loser => 'Loser'; @@ -426,6 +465,21 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings => 'Settings'; + @override + String get select_a_classifier => 'Select a classifier'; + + @override + String get select_a_game => 'Select a game'; + + @override + String get select_a_group => 'Select a group'; + + @override + String get select_a_scope => 'Select a scope'; + + @override + String get select_a_timeframe => 'Select a timeframe'; + @override String get single_loser => 'Single Loser'; @@ -438,6 +492,42 @@ class AppLocalizationsEn extends AppLocalizations { @override String get stats => 'Stats'; + @override + String get statistic_scope_all_players => 'All players'; + + @override + String get statistic_scope_selected_games => 'Selected games'; + + @override + String get statistic_scope_selected_groups => 'Selected groups'; + + @override + String get statistic_scope_timeframe => 'Timeframe'; + + @override + String get statistic_type_average_score => 'Average score'; + + @override + String get statistic_type_best_score => 'Best score'; + + @override + String get statistic_type_total_losses => 'Total losses'; + + @override + String get statistic_type_total_matches => 'Total matches'; + + @override + String get statistic_type_total_score => 'Total score'; + + @override + String get statistic_type_total_wins => 'Total wins'; + + @override + String get statistic_type_winrate => 'Winrate'; + + @override + String get statistic_type_worst_score => 'Worst score'; + @override String successfully_added_player(String playerName) { return 'Successfully added player $playerName'; @@ -457,6 +547,24 @@ class AppLocalizationsEn extends AppLocalizations { @override String get tie => 'Tie'; + @override + String get timeframe_all_time => 'All time'; + + @override + String get timeframe_last_180_days => 'Last 180 days'; + + @override + String get timeframe_last_30_days => 'Last 30 days'; + + @override + String get timeframe_last_7_days => 'Last 7 days'; + + @override + String get timeframe_last_90_days => 'Last 90 days'; + + @override + String get timeframe_last_year => 'Last year'; + @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 7e5434b..07d66b4 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -6,7 +6,7 @@ import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistics_view.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart'; diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart new file mode 100644 index 0000000..17706a1 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -0,0 +1,635 @@ +import 'package:animated_custom_dropdown/custom_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/constants.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; + +class CreateStatisticView extends StatefulWidget { + const CreateStatisticView({super.key, required this.onStatisticCreated}); + + final void Function() onStatisticCreated; + + @override + State createState() => _CreateStatisticViewState(); +} + +class _CreateStatisticViewState extends State { + bool isLoading = false; + + /* Data loaded from the database */ + List players = []; + List games = []; + List groups = []; + + /* User selections */ + StatisticType? selectedType; + List selectedScope = []; + List selectedGames = []; + List selectedPlayers = []; + List selectedGroups = []; + Timeframe? selectedTimeframe; + + @override + void initState() { + loadAllData(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var loc = AppLocalizations.of(context); + + return ScaffoldMessenger( + child: Scaffold( + appBar: AppBar(title: Text(loc.create_statistic)), + body: Stack( + alignment: AlignmentDirectional.center, + children: [ + SingleChildScrollView( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Classifier title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.create_statistic_classifier_title, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.create_statistic_classifier_subtitle, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + softWrap: true, + ), + ], + ), + ), + + // Classifier selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateStatisticTypeToString(item, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedType, enabled) => Text( + translateStatisticTypeToString(selectedType, context), + style: headerStyle, + ), + hintText: loc.select_a_classifier, + items: StatisticType.values, + decoration: decoration, + onChanged: (value) { + setState(() { + selectedType = value; + }); + }, + ), + ), + + const SizedBox(height: 10), + + // Scope title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.create_statistic_scope_title, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.create_statistic_scope_subtitle, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // Scope selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: loc.select_a_scope, + items: StatisticScope.values, + decoration: decoration, + listItemBuilder: + (context, scope, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateScopeToString(scope, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems + .map((s) => translateScopeToString(s, context)) + .join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedScope = values; + }); + }, + ), + ), + + if (selectedScope.contains(StatisticScope.selectedGames)) ...[ + const SizedBox(height: 10), + + // games title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.create_statistic_games_title, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.create_statistic_games_subtitle, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // game selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_game, + items: games, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + translateRulesetToString( + item.ruleset, + context, + ), + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + + // Check icon + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: const TextStyle( + color: CustomTheme.textColor, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedGames = values; + }); + }, + ), + ), + ], + + if (selectedScope.contains( + StatisticScope.selectedGroups, + )) ...[ + const SizedBox(height: 10), + + // groups title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.create_statistic_groups_title, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.create_statistic_groups_subtitle, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_group, + items: groups, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + ' ${item.members.length.toString()} ${loc.members}', + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List groups) { + setState(() { + selectedGroups = groups; + }); + }, + ), + ), + ], + + if (selectedScope.contains(StatisticScope.timeframe)) ...[ + const SizedBox(height: 10), + + // timeframe title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.create_statistic_timeframe_title, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.create_statistic_timeframe_subtitle, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + enabled: !isLoading, + excludeSelected: false, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading + ? loc.loading + : loc.select_a_timeframe, + items: Timeframe.values, + decoration: decoration, + listItemBuilder: + (context, timeframe, isSelected, onItemSelect) => + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + translateTimeframeToString( + timeframe, + context, + ), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedTimeframe, enabled) => + Text( + translateTimeframeToString( + selectedTimeframe, + context, + ), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onChanged: (Timeframe? timeframe) { + setState(() { + selectedTimeframe = timeframe; + }); + }, + ), + ), + ], + ], + ), + ), + + // Create statistic button + Positioned( + bottom: MediaQuery.of(context).padding.bottom, + child: AnimatedDialogButton( + buttonConstraints: const BoxConstraints(minWidth: 350), + buttonText: loc.create_statistic, + onPressed: selectedType != null && selectedScope.isNotEmpty + ? () => submitStatistic() + : null, + ), + ), + ], + ), + ), + ); + } + + CustomDropdownDecoration get decoration => CustomDropdownDecoration( + listItemDecoration: const ListItemDecoration( + selectedIconBorder: BorderSide(color: CustomTheme.primaryColor, width: 1), + selectedIconColor: CustomTheme.primaryColor, + highlightColor: CustomTheme.secondaryColor, + splashColor: Colors.transparent, + selectedColor: CustomTheme.onBoxColor, + ), + listItemStyle: itemStyle, + headerStyle: headerStyle, + hintStyle: hintStyle, + closedFillColor: CustomTheme.boxColor, + closedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + expandedFillColor: CustomTheme.boxColor, + expandedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + ); + + CustomDropdownDisabledDecoration get disabledDecoration => + CustomDropdownDisabledDecoration( + fillColor: CustomTheme.boxColor.withAlpha(125), + border: Border.all( + color: CustomTheme.boxBorderColor.withAlpha(125), + width: 1, + ), + headerStyle: disabledHeaderStyle, + hintStyle: disabledHintStyle, + ); + + TextStyle get headerStyle => const TextStyle( + color: CustomTheme.textColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get itemStyle => + const TextStyle(color: CustomTheme.textColor, fontSize: 14); + + TextStyle get hintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + TextStyle get disabledHeaderStyle => const TextStyle( + color: CustomTheme.hintColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get disabledHintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + Future loadAllData() async { + isLoading = true; + final db = Provider.of(context, listen: false); + + Future.wait([ + db.playerDao.getAllPlayers(), + db.groupDao.getAllGroups(), + db.gameDao.getAllGames(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]) + .then((results) async { + players = results[0]; + groups = results[1]; + games = results[2]; + isLoading = false; + }) + .catchError((error) { + print('Error loading data: $error'); + }); + } + + void submitStatistic() { + final newStatistic = Statistic( + type: selectedType!, + scopes: selectedScope, + timeframe: selectedTimeframe, + selectedGroups: selectedGroups, + selectedGames: selectedGames, + ); + // final db = Provider.of(context, listen: false); + // db.statisticDao.addStatistic(newStatistic); + Navigator.of(context).pop(newStatistic); + } +} + +String translateTimeframeToString(Timeframe timeframe, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (timeframe) { + case Timeframe.last7Days: + return loc.timeframe_last_7_days; + case Timeframe.last30Days: + return loc.timeframe_last_30_days; + case Timeframe.last90Days: + return loc.timeframe_last_90_days; + case Timeframe.last180Days: + return loc.timeframe_last_180_days; + case Timeframe.lastYear: + return loc.timeframe_last_year; + case Timeframe.allTime: + return loc.timeframe_all_time; + } +} + +String translateScopeToString(StatisticScope scope, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (scope) { + case StatisticScope.allPlayers: + return loc.statistic_scope_all_players; + case StatisticScope.selectedGroups: + return loc.statistic_scope_selected_groups; + case StatisticScope.selectedGames: + return loc.statistic_scope_selected_games; + case StatisticScope.timeframe: + return loc.statistic_scope_timeframe; + } +} + +String translateStatisticTypeToString( + StatisticType type, + BuildContext context, +) { + final loc = AppLocalizations.of(context); + switch (type) { + case StatisticType.totalMatches: + return loc.statistic_type_total_matches; + case StatisticType.totalWins: + return loc.statistic_type_total_wins; + case StatisticType.totalScore: + return loc.statistic_type_total_score; + case StatisticType.totalLosses: + return loc.statistic_type_total_losses; + case StatisticType.averageScore: + return loc.statistic_type_average_score; + case StatisticType.bestScore: + return loc.statistic_type_best_score; + case StatisticType.worstScore: + return loc.statistic_type_worst_score; + case StatisticType.winrate: + return loc.statistic_type_winrate; + } +} diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart similarity index 62% rename from lib/presentation/views/main_menu/statistics_view.dart rename to lib/presentation/views/main_menu/statistics_view/statistics_view.dart index 8659a2e..3470a12 100644 --- a/lib/presentation/views/main_menu/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/constants.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart'; +import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart'; import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; import 'package:tallee/presentation/widgets/top_centered_message.dart'; @@ -47,85 +50,107 @@ class _StatisticsViewState extends State { final loc = AppLocalizations.of(context); return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { - return SingleChildScrollView( - child: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, + return Stack( + alignment: AlignmentDirectional.center, + children: [ + SingleChildScrollView( + child: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: constraints.maxWidth), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.matches, - icon: Icons.groups_rounded, - value: matchCount, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + QuickInfoTile( + width: constraints.maxWidth * 0.45, + height: constraints.maxHeight * 0.13, + title: loc.matches, + icon: Icons.groups_rounded, + value: matchCount, + ), + SizedBox(width: constraints.maxWidth * 0.05), + QuickInfoTile( + width: constraints.maxWidth * 0.45, + height: constraints.maxHeight * 0.13, + title: loc.groups, + icon: Icons.groups_rounded, + value: groupCount, + ), + ], ), - SizedBox(width: constraints.maxWidth * 0.05), - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.groups, - icon: Icons.groups_rounded, - value: groupCount, + SizedBox(height: constraints.maxHeight * 0.02), + Visibility( + visible: + winCounts.isEmpty && + matchCounts.isEmpty && + winRates.isEmpty, + replacement: Column( + children: [ + StatisticsTile( + icon: Icons.sports_score, + title: loc.wins, + width: constraints.maxWidth * 0.95, + values: winCounts, + itemCount: 3, + barColor: Colors.green, + ), + SizedBox(height: constraints.maxHeight * 0.02), + StatisticsTile( + icon: Icons.percent, + title: loc.winrate, + width: constraints.maxWidth * 0.95, + values: winRates, + itemCount: 5, + barColor: Colors.orange[700]!, + ), + SizedBox(height: constraints.maxHeight * 0.02), + StatisticsTile( + icon: Icons.casino, + title: loc.amount_of_matches, + width: constraints.maxWidth * 0.95, + values: matchCounts, + itemCount: 10, + barColor: Colors.blue, + ), + ], + ), + child: TopCenteredMessage( + icon: Icons.info, + title: loc.info, + message: AppLocalizations.of( + context, + ).no_statistics_available, + ), ), + SizedBox(height: MediaQuery.paddingOf(context).bottom), ], ), - SizedBox(height: constraints.maxHeight * 0.02), - Visibility( - visible: - winCounts.isEmpty && - matchCounts.isEmpty && - winRates.isEmpty, - replacement: Column( - children: [ - StatisticsTile( - icon: Icons.sports_score, - title: loc.wins, - width: constraints.maxWidth * 0.95, - values: winCounts, - itemCount: 3, - barColor: Colors.green, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.percent, - title: loc.winrate, - width: constraints.maxWidth * 0.95, - values: winRates, - itemCount: 5, - barColor: Colors.orange[700]!, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.casino, - title: loc.amount_of_matches, - width: constraints.maxWidth * 0.95, - values: matchCounts, - itemCount: 10, - barColor: Colors.blue, - ), - ], - ), - child: TopCenteredMessage( - icon: Icons.info, - title: loc.info, - message: AppLocalizations.of( - context, - ).no_statistics_available, - ), - ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), - ], + ), ), ), - ), + Positioned( + bottom: MediaQuery.paddingOf(context).bottom + 20, + child: MainMenuButton( + text: loc.create_statistic, + icon: Icons.bar_chart, + onPressed: () { + Navigator.push( + context, + adaptivePageRoute( + builder: (context) => CreateStatisticView( + onStatisticCreated: loadStatisticData, + ), + ), + ); + }, + ), + ), + ], ); }, ); diff --git a/pubspec.lock b/pubspec.lock index 96b2cc5..fc30e57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" + animated_custom_dropdown: + dependency: "direct main" + description: + name: animated_custom_dropdown + sha256: "5a72dc209041bb53f6c7164bc2e366552d5197cdb032b1c9b2c36e3013024486" + url: "https://pub.dev" + source: hosted + version: "3.1.1" arb_utils: dependency: "direct dev" description: @@ -353,6 +361,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.8" + dropdown_flutter: + dependency: "direct main" + description: + name: dropdown_flutter + sha256: "5ae3d05d768d0bb6030ff735e6b4b93f7b29be3cf3bec7c86cd4f444c8f067ff" + url: "https://pub.dev" + source: hosted + version: "1.0.3" equatable: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3d8d99f..8ca8550 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,11 +7,13 @@ environment: sdk: ^3.8.1 dependencies: + animated_custom_dropdown: ^3.1.1 clock: ^1.1.2 collection: ^1.19.1 cupertino_icons: ^1.0.6 drift: ^2.27.0 drift_flutter: ^0.2.4 + dropdown_flutter: ^1.0.3 file_picker: ^11.0.2 file_saver: ^0.3.1 flutter: -- 2.49.1 From d389b93cc5e9801bf83632cd9a197eb57a700399 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 12:16:36 +0200 Subject: [PATCH 03/39] Updated method with join --- lib/data/dao/player_group_dao.dart | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index 48c5653..5139eea 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -39,18 +39,25 @@ class PlayerGroupDao extends DatabaseAccessor /// Retrieves all players belonging to a specific group by [groupId]. Future> getPlayersOfGroup({required String groupId}) async { - final query = select(playerGroupTable) - ..where((pG) => pG.groupId.equals(groupId)); - final result = await query.get(); + final query = select(playerGroupTable).join([ + innerJoin( + playerTable, + playerTable.id.equalsExp(playerGroupTable.playerId), + ), + ])..where(playerGroupTable.groupId.equals(groupId)); - List groupMembers = List.empty(growable: true); - - for (var entry in result) { - final player = await db.playerDao.getPlayerById(playerId: entry.playerId); - groupMembers.add(player); - } - - return groupMembers; + final results = await query.map((row) => row.readTable(playerTable)).get(); + return results + .map( + (result) => Player( + id: result.id, + createdAt: result.createdAt, + name: result.name, + nameCount: result.nameCount, + description: result.description, + ), + ) + .toList(); } /// Checks if a player with [playerId] is in the group with [groupId]. -- 2.49.1 From 37031d66c98aedc5ef297c1be20119b11d85bcf5 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 12:16:44 +0200 Subject: [PATCH 04/39] Updated attribute order --- lib/data/models/group.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/data/models/group.dart b/lib/data/models/group.dart index 5c1515c..8b1fe92 100644 --- a/lib/data/models/group.dart +++ b/lib/data/models/group.dart @@ -5,17 +5,17 @@ import 'package:uuid/uuid.dart'; class Group { final String id; - final String name; - final String description; final DateTime createdAt; + final String name; final List members; + final String description; Group({ + required this.name, + required this.members, String? id, DateTime? createdAt, - required this.name, String? description, - required this.members, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? clock.now(), description = description ?? ''; -- 2.49.1 From 807ae61df7e6c4d7bf91fd854d42fbd903bd7cf1 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 13:52:27 +0200 Subject: [PATCH 05/39] feat: basic database functionality --- lib/data/dao/statistic_dao.dart | 112 + lib/data/dao/statistic_dao.g.dart | 19 + lib/data/dao/statistic_game_dao.dart | 60 + lib/data/dao/statistic_game_dao.g.dart | 29 + lib/data/dao/statistic_group_dao.dart | 66 + lib/data/dao/statistic_group_dao.g.dart | 29 + lib/data/dao/statistic_scope_dao.dart | 55 + lib/data/dao/statistic_scope_dao.g.dart | 26 + lib/data/db/database.dart | 16 + lib/data/db/database.g.dart | 2842 ++++++++++++++++- lib/data/db/tables/statistic_game_table.dart | 13 + lib/data/db/tables/statistic_group_table.dart | 13 + lib/data/db/tables/statistic_scope_table.dart | 11 + lib/data/db/tables/statistic_table.dart | 10 + lib/data/models/statistic.dart | 10 +- .../create_statistic_view.dart | 4 +- .../statistics_view/statistics_view.dart | 127 +- pubspec.yaml | 2 +- test/db_tests/statistics/statistic_test.dart | 124 + 19 files changed, 3457 insertions(+), 111 deletions(-) create mode 100644 lib/data/dao/statistic_dao.dart create mode 100644 lib/data/dao/statistic_dao.g.dart create mode 100644 lib/data/dao/statistic_game_dao.dart create mode 100644 lib/data/dao/statistic_game_dao.g.dart create mode 100644 lib/data/dao/statistic_group_dao.dart create mode 100644 lib/data/dao/statistic_group_dao.g.dart create mode 100644 lib/data/dao/statistic_scope_dao.dart create mode 100644 lib/data/dao/statistic_scope_dao.g.dart create mode 100644 lib/data/db/tables/statistic_game_table.dart create mode 100644 lib/data/db/tables/statistic_group_table.dart create mode 100644 lib/data/db/tables/statistic_scope_table.dart create mode 100644 lib/data/db/tables/statistic_table.dart create mode 100644 test/db_tests/statistics/statistic_test.dart diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart new file mode 100644 index 0000000..39904fc --- /dev/null +++ b/lib/data/dao/statistic_dao.dart @@ -0,0 +1,112 @@ +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; +import 'package:tallee/data/models/statistic.dart'; + +part 'statistic_dao.g.dart'; + +@DriftAccessor(tables: [StatisticTable]) +class StatisticDao extends DatabaseAccessor + with _$StatisticDaoMixin { + StatisticDao(super.db); + + /* Create */ + + Future addStatistic({required Statistic statistic}) async { + await into(statisticTable).insert( + StatisticTableCompanion.insert( + id: statistic.id, + type: statistic.type.name, + timeframe: Value(statistic.timeframe?.name), + ), + mode: InsertMode.insertOrReplace, + ); + + await db.statisticScopeDao.addStatisticScopes( + statisticId: statistic.id, + scopes: statistic.scopes, + ); + + if (statistic.selectedGroups != null) { + await db.statisticGroupDao.addStatisticGroups( + statisticId: statistic.id, + groups: statistic.selectedGroups!, + ); + } + + if (statistic.selectedGames != null) { + await db.statisticGameDao.addStatisticGames( + statisticId: statistic.id, + games: statistic.selectedGames!, + ); + } + + return true; + } + + /* Read */ + + Future getStatisticById(String statisticId) async { + final query = select(statisticTable); + final row = await query.getSingleOrNull(); + if (row != null) { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere((type) => type.name == row.type), + scopes: scopes, + id: row.id, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + ); + } + return null; + } + + /// Retrieves all statistics from the database, including their associated groups and games. + Future> getAllStatistics() async { + final query = select(statisticTable); + final rows = await query.get(); + return Future.wait( + rows.map((row) async { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere( + (type) => type.name == row.type, + ), + scopes: [], + id: row.id, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + ); + }), + ); + } + + /* Delete */ + + Future deleteStatistic(String statisticId) async { + final rowsDeleted = await (delete( + statisticTable, + )..where((tbl) => tbl.id.equals(statisticId))).go(); + + return rowsDeleted > 0; + } + + Future deleteAllStatistics() async { + final rowsDeleted = await delete(statisticTable).go(); + return rowsDeleted > 0; + } +} diff --git a/lib/data/dao/statistic_dao.g.dart b/lib/data/dao/statistic_dao.g.dart new file mode 100644 index 0000000..0ce36e1 --- /dev/null +++ b/lib/data/dao/statistic_dao.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + StatisticDaoManager get managers => StatisticDaoManager(this); +} + +class StatisticDaoManager { + final _$StatisticDaoMixin _db; + StatisticDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); +} diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart new file mode 100644 index 0000000..ea7260f --- /dev/null +++ b/lib/data/dao/statistic_game_dao.dart @@ -0,0 +1,60 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/models/game.dart'; + +part 'statistic_game_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGameTable]) +class StatisticGameDao extends DatabaseAccessor + with _$StatisticGameDaoMixin { + StatisticGameDao(super.db); + + /// Retrieves a list of games associated with a specific statistic. + Future> getGamesForStatistic(String statisticId) async { + final query = select(statisticGameTable).join([ + innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)), + ])..where(statisticGameTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(gameTable)).get(); + return results + .map( + (result) => Game( + id: result.id, + name: result.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), + description: result.description, + color: GameColor.values.firstWhere((e) => e.name == result.color), + icon: result.icon, + createdAt: result.createdAt, + ), + ) + .toList(); + } + + Future addStatisticGames({ + required String statisticId, + required List games, + }) { + final entries = games + .map( + (game) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: game.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGameTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic games: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_game_dao.g.dart b/lib/data/dao/statistic_game_dao.g.dart new file mode 100644 index 0000000..d6ee984 --- /dev/null +++ b/lib/data/dao/statistic_game_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_game_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGameDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GameTableTable get gameTable => attachedDatabase.gameTable; + $StatisticGameTableTable get statisticGameTable => + attachedDatabase.statisticGameTable; + StatisticGameDaoManager get managers => StatisticGameDaoManager(this); +} + +class StatisticGameDaoManager { + final _$StatisticGameDaoMixin _db; + StatisticGameDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GameTableTableTableManager get gameTable => + $$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager( + _db.attachedDatabase, + _db.statisticGameTable, + ); +} diff --git a/lib/data/dao/statistic_group_dao.dart b/lib/data/dao/statistic_group_dao.dart new file mode 100644 index 0000000..9eb9397 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.dart @@ -0,0 +1,66 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/models/group.dart'; + +part 'statistic_group_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGroupTable, GroupTable]) +class StatisticGroupDao extends DatabaseAccessor + with _$StatisticGroupDaoMixin { + StatisticGroupDao(super.db); + + /// Retrieves a list of groups associated with a specific statistic. + Future> getGroupsForStatistic(String statisticId) async { + final query = select(statisticGroupTable).join([ + innerJoin( + groupTable, + groupTable.id.equalsExp(statisticGroupTable.groupId), + ), + ])..where(statisticGroupTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(groupTable)).get(); + final groups = await Future.wait( + results.map((result) async { + final groupMembers = await db.playerGroupDao.getPlayersOfGroup( + groupId: result.id, + ); + return Group( + id: result.id, + createdAt: result.createdAt, + name: result.name, + description: result.description, + members: groupMembers, + ); + }), + ); + + return groups; + } + + Future addStatisticGroups({ + required String statisticId, + required List groups, + }) async { + final entries = groups + .map( + (group) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: group.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGroupTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic groups: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_group_dao.g.dart b/lib/data/dao/statistic_group_dao.g.dart new file mode 100644 index 0000000..57a83c5 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_group_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGroupDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; + $StatisticGroupTableTable get statisticGroupTable => + attachedDatabase.statisticGroupTable; + StatisticGroupDaoManager get managers => StatisticGroupDaoManager(this); +} + +class StatisticGroupDaoManager { + final _$StatisticGroupDaoMixin _db; + StatisticGroupDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GroupTableTableTableManager get groupTable => + $$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager( + _db.attachedDatabase, + _db.statisticGroupTable, + ); +} diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart new file mode 100644 index 0000000..2027acd --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.dart @@ -0,0 +1,55 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; + +part 'statistic_scope_dao.g.dart'; + +@DriftAccessor(tables: [StatisticScopeTable]) +class StatisticScopeDao extends DatabaseAccessor + with _$StatisticScopeDaoMixin { + StatisticScopeDao(super.db); + + /// Retrieves a list of statistic scopes associated with a specific statistic ID. + Future> getScopeForStatistic(String statisticId) async { + final query = select(statisticScopeTable) + ..where((tbl) => tbl.statisticId.equals(statisticId)); + + final results = await query.get(); + return results + .map( + (result) => StatisticScope.values.firstWhere( + (e) => e.name == result.scope, + orElse: () => throw Exception( + 'Invalid scope value: ${result.scope} for statistic ID: $statisticId', + ), + ), + ) + .toList(); + } + + Future addStatisticScopes({ + required String statisticId, + required List scopes, + }) async { + final entries = scopes + .map( + (scope) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope.name, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticScopeTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic scopes: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_scope_dao.g.dart b/lib/data/dao/statistic_scope_dao.g.dart new file mode 100644 index 0000000..adaa171 --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_scope_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticScopeDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $StatisticScopeTableTable get statisticScopeTable => + attachedDatabase.statisticScopeTable; + StatisticScopeDaoManager get managers => StatisticScopeDaoManager(this); +} + +class StatisticScopeDaoManager { + final _$StatisticScopeDaoMixin _db; + StatisticScopeDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager( + _db.attachedDatabase, + _db.statisticScopeTable, + ); +} diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index a7e9c1d..792da0e 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -8,6 +8,10 @@ import 'package:tallee/data/dao/player_dao.dart'; import 'package:tallee/data/dao/player_group_dao.dart'; import 'package:tallee/data/dao/player_match_dao.dart'; import 'package:tallee/data/dao/score_entry_dao.dart'; +import 'package:tallee/data/dao/statistic_dao.dart'; +import 'package:tallee/data/dao/statistic_game_dao.dart'; +import 'package:tallee/data/dao/statistic_group_dao.dart'; +import 'package:tallee/data/dao/statistic_scope_dao.dart'; import 'package:tallee/data/dao/team_dao.dart'; import 'package:tallee/data/db/tables/game_table.dart'; import 'package:tallee/data/db/tables/group_table.dart'; @@ -16,6 +20,10 @@ import 'package:tallee/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_match_table.dart'; import 'package:tallee/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/score_entry_table.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; import 'package:tallee/data/db/tables/team_table.dart'; part 'database.g.dart'; @@ -30,6 +38,10 @@ part 'database.g.dart'; GameTable, TeamTable, ScoreEntryTable, + StatisticTable, + StatisticScopeTable, + StatisticGameTable, + StatisticGroupTable, ], daos: [ PlayerDao, @@ -40,6 +52,10 @@ part 'database.g.dart'; GameDao, ScoreEntryDao, TeamDao, + StatisticDao, + StatisticScopeDao, + StatisticGameDao, + StatisticGroupDao, ], ) class AppDatabase extends _$AppDatabase { diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index c8d0faa..489b890 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -2732,6 +2732,971 @@ class ScoreEntryTableCompanion extends UpdateCompanion { } } +class $StatisticTableTable extends StatisticTable + with TableInfo<$StatisticTableTable, StatisticTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _typeMeta = const VerificationMeta('type'); + @override + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _timeframeMeta = const VerificationMeta( + 'timeframe', + ); + @override + late final GeneratedColumn timeframe = GeneratedColumn( + 'timeframe', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + @override + List get $columns => [id, type, timeframe]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, + type.isAcceptableOrUnknown(data['type']!, _typeMeta), + ); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('timeframe')) { + context.handle( + _timeframeMeta, + timeframe.isAcceptableOrUnknown(data['timeframe']!, _timeframeMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + StatisticTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticTableData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + timeframe: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}timeframe'], + ), + ); + } + + @override + $StatisticTableTable createAlias(String alias) { + return $StatisticTableTable(attachedDatabase, alias); + } +} + +class StatisticTableData extends DataClass + implements Insertable { + final String id; + final String type; + final String? timeframe; + const StatisticTableData({ + required this.id, + required this.type, + this.timeframe, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['type'] = Variable(type); + if (!nullToAbsent || timeframe != null) { + map['timeframe'] = Variable(timeframe); + } + return map; + } + + StatisticTableCompanion toCompanion(bool nullToAbsent) { + return StatisticTableCompanion( + id: Value(id), + type: Value(type), + timeframe: timeframe == null && nullToAbsent + ? const Value.absent() + : Value(timeframe), + ); + } + + factory StatisticTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticTableData( + id: serializer.fromJson(json['id']), + type: serializer.fromJson(json['type']), + timeframe: serializer.fromJson(json['timeframe']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'type': serializer.toJson(type), + 'timeframe': serializer.toJson(timeframe), + }; + } + + StatisticTableData copyWith({ + String? id, + String? type, + Value timeframe = const Value.absent(), + }) => StatisticTableData( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe.present ? timeframe.value : this.timeframe, + ); + StatisticTableData copyWithCompanion(StatisticTableCompanion data) { + return StatisticTableData( + id: data.id.present ? data.id.value : this.id, + type: data.type.present ? data.type.value : this.type, + timeframe: data.timeframe.present ? data.timeframe.value : this.timeframe, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticTableData(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, type, timeframe); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticTableData && + other.id == this.id && + other.type == this.type && + other.timeframe == this.timeframe); +} + +class StatisticTableCompanion extends UpdateCompanion { + final Value id; + final Value type; + final Value timeframe; + final Value rowid; + const StatisticTableCompanion({ + this.id = const Value.absent(), + this.type = const Value.absent(), + this.timeframe = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticTableCompanion.insert({ + required String id, + required String type, + this.timeframe = const Value.absent(), + this.rowid = const Value.absent(), + }) : id = Value(id), + type = Value(type); + static Insertable custom({ + Expression? id, + Expression? type, + Expression? timeframe, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (type != null) 'type': type, + if (timeframe != null) 'timeframe': timeframe, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticTableCompanion copyWith({ + Value? id, + Value? type, + Value? timeframe, + Value? rowid, + }) { + return StatisticTableCompanion( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe ?? this.timeframe, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (timeframe.present) { + map['timeframe'] = Variable(timeframe.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticTableCompanion(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticScopeTableTable extends StatisticScopeTable + with TableInfo<$StatisticScopeTableTable, StatisticScopeTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticScopeTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _scopeMeta = const VerificationMeta('scope'); + @override + late final GeneratedColumn scope = GeneratedColumn( + 'scope', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [statisticId, scope]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_scope_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('scope')) { + context.handle( + _scopeMeta, + scope.isAcceptableOrUnknown(data['scope']!, _scopeMeta), + ); + } else if (isInserting) { + context.missing(_scopeMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, scope}; + @override + StatisticScopeTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticScopeTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + scope: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}scope'], + )!, + ); + } + + @override + $StatisticScopeTableTable createAlias(String alias) { + return $StatisticScopeTableTable(attachedDatabase, alias); + } +} + +class StatisticScopeTableData extends DataClass + implements Insertable { + final String statisticId; + final String scope; + const StatisticScopeTableData({ + required this.statisticId, + required this.scope, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['scope'] = Variable(scope); + return map; + } + + StatisticScopeTableCompanion toCompanion(bool nullToAbsent) { + return StatisticScopeTableCompanion( + statisticId: Value(statisticId), + scope: Value(scope), + ); + } + + factory StatisticScopeTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticScopeTableData( + statisticId: serializer.fromJson(json['statisticId']), + scope: serializer.fromJson(json['scope']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'scope': serializer.toJson(scope), + }; + } + + StatisticScopeTableData copyWith({String? statisticId, String? scope}) => + StatisticScopeTableData( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + ); + StatisticScopeTableData copyWithCompanion(StatisticScopeTableCompanion data) { + return StatisticScopeTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + scope: data.scope.present ? data.scope.value : this.scope, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableData(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, scope); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticScopeTableData && + other.statisticId == this.statisticId && + other.scope == this.scope); +} + +class StatisticScopeTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value scope; + final Value rowid; + const StatisticScopeTableCompanion({ + this.statisticId = const Value.absent(), + this.scope = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticScopeTableCompanion.insert({ + required String statisticId, + required String scope, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + scope = Value(scope); + static Insertable custom({ + Expression? statisticId, + Expression? scope, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (scope != null) 'scope': scope, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticScopeTableCompanion copyWith({ + Value? statisticId, + Value? scope, + Value? rowid, + }) { + return StatisticScopeTableCompanion( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (scope.present) { + map['scope'] = Variable(scope.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGameTableTable extends StatisticGameTable + with TableInfo<$StatisticGameTableTable, StatisticGameTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGameTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _gameIdMeta = const VerificationMeta('gameId'); + @override + late final GeneratedColumn gameId = GeneratedColumn( + 'game_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES game_table (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [statisticId, gameId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_game_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('game_id')) { + context.handle( + _gameIdMeta, + gameId.isAcceptableOrUnknown(data['game_id']!, _gameIdMeta), + ); + } else if (isInserting) { + context.missing(_gameIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, gameId}; + @override + StatisticGameTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGameTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + gameId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}game_id'], + )!, + ); + } + + @override + $StatisticGameTableTable createAlias(String alias) { + return $StatisticGameTableTable(attachedDatabase, alias); + } +} + +class StatisticGameTableData extends DataClass + implements Insertable { + final String statisticId; + final String gameId; + const StatisticGameTableData({ + required this.statisticId, + required this.gameId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['game_id'] = Variable(gameId); + return map; + } + + StatisticGameTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGameTableCompanion( + statisticId: Value(statisticId), + gameId: Value(gameId), + ); + } + + factory StatisticGameTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGameTableData( + statisticId: serializer.fromJson(json['statisticId']), + gameId: serializer.fromJson(json['gameId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'gameId': serializer.toJson(gameId), + }; + } + + StatisticGameTableData copyWith({String? statisticId, String? gameId}) => + StatisticGameTableData( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + ); + StatisticGameTableData copyWithCompanion(StatisticGameTableCompanion data) { + return StatisticGameTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + gameId: data.gameId.present ? data.gameId.value : this.gameId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableData(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, gameId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGameTableData && + other.statisticId == this.statisticId && + other.gameId == this.gameId); +} + +class StatisticGameTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value gameId; + final Value rowid; + const StatisticGameTableCompanion({ + this.statisticId = const Value.absent(), + this.gameId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGameTableCompanion.insert({ + required String statisticId, + required String gameId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + gameId = Value(gameId); + static Insertable custom({ + Expression? statisticId, + Expression? gameId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (gameId != null) 'game_id': gameId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGameTableCompanion copyWith({ + Value? statisticId, + Value? gameId, + Value? rowid, + }) { + return StatisticGameTableCompanion( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (gameId.present) { + map['game_id'] = Variable(gameId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGroupTableTable extends StatisticGroupTable + with TableInfo<$StatisticGroupTableTable, StatisticGroupTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGroupTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _groupIdMeta = const VerificationMeta( + 'groupId', + ); + @override + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES group_table (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [statisticId, groupId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_group_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('group_id')) { + context.handle( + _groupIdMeta, + groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), + ); + } else if (isInserting) { + context.missing(_groupIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, groupId}; + @override + StatisticGroupTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGroupTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + ); + } + + @override + $StatisticGroupTableTable createAlias(String alias) { + return $StatisticGroupTableTable(attachedDatabase, alias); + } +} + +class StatisticGroupTableData extends DataClass + implements Insertable { + final String statisticId; + final String groupId; + const StatisticGroupTableData({ + required this.statisticId, + required this.groupId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['group_id'] = Variable(groupId); + return map; + } + + StatisticGroupTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGroupTableCompanion( + statisticId: Value(statisticId), + groupId: Value(groupId), + ); + } + + factory StatisticGroupTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGroupTableData( + statisticId: serializer.fromJson(json['statisticId']), + groupId: serializer.fromJson(json['groupId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'groupId': serializer.toJson(groupId), + }; + } + + StatisticGroupTableData copyWith({String? statisticId, String? groupId}) => + StatisticGroupTableData( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + ); + StatisticGroupTableData copyWithCompanion(StatisticGroupTableCompanion data) { + return StatisticGroupTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableData(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, groupId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGroupTableData && + other.statisticId == this.statisticId && + other.groupId == this.groupId); +} + +class StatisticGroupTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value groupId; + final Value rowid; + const StatisticGroupTableCompanion({ + this.statisticId = const Value.absent(), + this.groupId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGroupTableCompanion.insert({ + required String statisticId, + required String groupId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + groupId = Value(groupId); + static Insertable custom({ + Expression? statisticId, + Expression? groupId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (groupId != null) 'group_id': groupId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGroupTableCompanion copyWith({ + Value? statisticId, + Value? groupId, + Value? rowid, + }) { + return StatisticGroupTableCompanion( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); @@ -2749,6 +3714,13 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final $ScoreEntryTableTable scoreEntryTable = $ScoreEntryTableTable( this, ); + late final $StatisticTableTable statisticTable = $StatisticTableTable(this); + late final $StatisticScopeTableTable statisticScopeTable = + $StatisticScopeTableTable(this); + late final $StatisticGameTableTable statisticGameTable = + $StatisticGameTableTable(this); + late final $StatisticGroupTableTable statisticGroupTable = + $StatisticGroupTableTable(this); late final PlayerDao playerDao = PlayerDao(this as AppDatabase); late final GroupDao groupDao = GroupDao(this as AppDatabase); late final MatchDao matchDao = MatchDao(this as AppDatabase); @@ -2761,6 +3733,16 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final GameDao gameDao = GameDao(this as AppDatabase); late final ScoreEntryDao scoreEntryDao = ScoreEntryDao(this as AppDatabase); late final TeamDao teamDao = TeamDao(this as AppDatabase); + late final StatisticDao statisticDao = StatisticDao(this as AppDatabase); + late final StatisticScopeDao statisticScopeDao = StatisticScopeDao( + this as AppDatabase, + ); + late final StatisticGameDao statisticGameDao = StatisticGameDao( + this as AppDatabase, + ); + late final StatisticGroupDao statisticGroupDao = StatisticGroupDao( + this as AppDatabase, + ); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -2774,6 +3756,10 @@ abstract class _$AppDatabase extends GeneratedDatabase { teamTable, playerMatchTable, scoreEntryTable, + statisticTable, + statisticScopeTable, + statisticGameTable, + statisticGroupTable, ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ @@ -2840,6 +3826,41 @@ abstract class _$AppDatabase extends GeneratedDatabase { ), result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)], ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_scope_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'game_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'group_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), ]); } @@ -3419,6 +4440,33 @@ final class $$GroupTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.groupTable.id, + db.statisticGroupTable.groupId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GroupTableTableFilterComposer @@ -3499,6 +4547,31 @@ class $$GroupTableTableFilterComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableOrderingComposer @@ -3603,6 +4676,32 @@ class $$GroupTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableTableManager @@ -3621,6 +4720,7 @@ class $$GroupTableTableTableManager PrefetchHooks Function({ bool matchTableRefs, bool playerGroupTableRefs, + bool statisticGroupTableRefs, }) > { $$GroupTableTableTableManager(_$AppDatabase db, $GroupTableTable table) @@ -3671,12 +4771,17 @@ class $$GroupTableTableTableManager ) .toList(), prefetchHooksCallback: - ({matchTableRefs = false, playerGroupTableRefs = false}) { + ({ + matchTableRefs = false, + playerGroupTableRefs = false, + statisticGroupTableRefs = false, + }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (matchTableRefs) db.matchTable, if (playerGroupTableRefs) db.playerGroupTable, + if (statisticGroupTableRefs) db.statisticGroupTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -3723,6 +4828,27 @@ class $$GroupTableTableTableManager ), typedResults: items, ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + GroupTableData, + $GroupTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$GroupTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.groupId == item.id, + ), + typedResults: items, + ), ]; }, ); @@ -3743,7 +4869,11 @@ typedef $$GroupTableTableProcessedTableManager = $$GroupTableTableUpdateCompanionBuilder, (GroupTableData, $$GroupTableTableReferences), GroupTableData, - PrefetchHooks Function({bool matchTableRefs, bool playerGroupTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool playerGroupTableRefs, + bool statisticGroupTableRefs, + }) >; typedef $$GameTableTableCreateCompanionBuilder = GameTableCompanion Function({ @@ -3789,6 +4919,33 @@ final class $$GameTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.gameTable.id, + db.statisticGameTable.gameId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.gameId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GameTableTableFilterComposer @@ -3859,6 +5016,31 @@ class $$GameTableTableFilterComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableOrderingComposer @@ -3962,6 +5144,32 @@ class $$GameTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableTableManager @@ -3977,7 +5185,10 @@ class $$GameTableTableTableManager $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool statisticGameTableRefs, + }) > { $$GameTableTableTableManager(_$AppDatabase db, $GameTableTable table) : super( @@ -4038,36 +5249,63 @@ class $$GameTableTableTableManager ), ) .toList(), - prefetchHooksCallback: ({matchTableRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [if (matchTableRefs) db.matchTable], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (matchTableRefs) - await $_getPrefetchedData< - GameTableData, - $GameTableTable, - MatchTableData - >( - currentTable: table, - referencedTable: $$GameTableTableReferences - ._matchTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GameTableTableReferences( - db, - table, - p0, - ).matchTableRefs, - referencedItemsForCurrentItem: (item, referencedItems) => - referencedItems.where((e) => e.gameId == item.id), - typedResults: items, - ), - ]; + prefetchHooksCallback: + ({matchTableRefs = false, statisticGameTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (matchTableRefs) db.matchTable, + if (statisticGameTableRefs) db.statisticGameTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (matchTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + MatchTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._matchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).matchTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.gameId == item.id, + ), + typedResults: items, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.gameId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); }, - ); - }, ), ); } @@ -4084,7 +5322,7 @@ typedef $$GameTableTableProcessedTableManager = $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({bool matchTableRefs, bool statisticGameTableRefs}) >; typedef $$MatchTableTableCreateCompanionBuilder = MatchTableCompanion Function({ @@ -6273,6 +7511,1536 @@ typedef $$ScoreEntryTableTableProcessedTableManager = ScoreEntryTableData, PrefetchHooks Function({bool playerId, bool matchId}) >; +typedef $$StatisticTableTableCreateCompanionBuilder = + StatisticTableCompanion Function({ + required String id, + required String type, + Value timeframe, + Value rowid, + }); +typedef $$StatisticTableTableUpdateCompanionBuilder = + StatisticTableCompanion Function({ + Value id, + Value type, + Value timeframe, + Value rowid, + }); + +final class $$StatisticTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData + > { + $$StatisticTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static MultiTypedResultKey< + $StatisticScopeTableTable, + List + > + _statisticScopeTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticScopeTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticScopeTable.statisticId, + ), + ); + + $$StatisticScopeTableTableProcessedTableManager get statisticScopeTableRefs { + final manager = $$StatisticScopeTableTableTableManager( + $_db, + $_db.statisticScopeTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticScopeTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGameTable.statisticId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGroupTable.statisticId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$StatisticTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnFilters(column), + ); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableFilterComposer f) f, + ) { + final $$StatisticScopeTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableFilterComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$StatisticTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + GeneratedColumn get timeframe => + $composableBuilder(column: $table.timeframe, builder: (column) => column); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableAnnotationComposer a) f, + ) { + final $$StatisticScopeTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + > { + $$StatisticTableTableTableManager( + _$AppDatabase db, + $StatisticTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value type = const Value.absent(), + Value timeframe = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion( + id: id, + type: type, + timeframe: timeframe, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required String type, + Value timeframe = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion.insert( + id: id, + type: type, + timeframe: timeframe, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: + ({ + statisticScopeTableRefs = false, + statisticGameTableRefs = false, + statisticGroupTableRefs = false, + }) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (statisticScopeTableRefs) db.statisticScopeTable, + if (statisticGameTableRefs) db.statisticGameTable, + if (statisticGroupTableRefs) db.statisticGroupTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (statisticScopeTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticScopeTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticScopeTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticScopeTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$StatisticTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + >; +typedef $$StatisticScopeTableTableCreateCompanionBuilder = + StatisticScopeTableCompanion Function({ + required String statisticId, + required String scope, + Value rowid, + }); +typedef $$StatisticScopeTableTableUpdateCompanionBuilder = + StatisticScopeTableCompanion Function({ + Value statisticId, + Value scope, + Value rowid, + }); + +final class $$StatisticScopeTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData + > { + $$StatisticScopeTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticScopeTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticScopeTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnFilters(column), + ); + + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnOrderings(column), + ); + + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get scope => + $composableBuilder(column: $table.scope, builder: (column) => column); + + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + > { + $$StatisticScopeTableTableTableManager( + _$AppDatabase db, + $StatisticScopeTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticScopeTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticScopeTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticScopeTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value scope = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String scope, + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticScopeTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticScopeTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + >; +typedef $$StatisticGameTableTableCreateCompanionBuilder = + StatisticGameTableCompanion Function({ + required String statisticId, + required String gameId, + Value rowid, + }); +typedef $$StatisticGameTableTableUpdateCompanionBuilder = + StatisticGameTableCompanion Function({ + Value statisticId, + Value gameId, + Value rowid, + }); + +final class $$StatisticGameTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData + > { + $$StatisticGameTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGameTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GameTableTable _gameIdTable(_$AppDatabase db) => + db.gameTable.createAlias( + $_aliasNameGenerator(db.statisticGameTable.gameId, db.gameTable.id), + ); + + $$GameTableTableProcessedTableManager get gameId { + final $_column = $_itemColumn('game_id')!; + + final manager = $$GameTableTableTableManager( + $_db, + $_db.gameTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_gameIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticGameTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableFilterComposer get gameId { + final $$GameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableFilterComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableOrderingComposer get gameId { + final $$GameTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableOrderingComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableAnnotationComposer get gameId { + final $$GameTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableAnnotationComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + > { + $$StatisticGameTableTableTableManager( + _$AppDatabase db, + $StatisticGameTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGameTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGameTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticGameTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value gameId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String gameId, + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGameTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, gameId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGameTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (gameId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.gameId, + referencedTable: + $$StatisticGameTableTableReferences + ._gameIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._gameIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGameTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + >; +typedef $$StatisticGroupTableTableCreateCompanionBuilder = + StatisticGroupTableCompanion Function({ + required String statisticId, + required String groupId, + Value rowid, + }); +typedef $$StatisticGroupTableTableUpdateCompanionBuilder = + StatisticGroupTableCompanion Function({ + Value statisticId, + Value groupId, + Value rowid, + }); + +final class $$StatisticGroupTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData + > { + $$StatisticGroupTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGroupTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GroupTableTable _groupIdTable(_$AppDatabase db) => + db.groupTable.createAlias( + $_aliasNameGenerator(db.statisticGroupTable.groupId, db.groupTable.id), + ); + + $$GroupTableTableProcessedTableManager get groupId { + final $_column = $_itemColumn('group_id')!; + + final manager = $$GroupTableTableTableManager( + $_db, + $_db.groupTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticGroupTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableFilterComposer get groupId { + final $$GroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableFilterComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableOrderingComposer get groupId { + final $$GroupTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableOrderingComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableAnnotationComposer get groupId { + final $$GroupTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableAnnotationComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + > { + $$StatisticGroupTableTableTableManager( + _$AppDatabase db, + $StatisticGroupTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGroupTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGroupTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticGroupTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value groupId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String groupId, + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGroupTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, groupId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (groupId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: + $$StatisticGroupTableTableReferences + ._groupIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._groupIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGroupTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + >; class $AppDatabaseManager { final _$AppDatabase _db; @@ -6293,4 +9061,12 @@ class $AppDatabaseManager { $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); $$ScoreEntryTableTableTableManager get scoreEntryTable => $$ScoreEntryTableTableTableManager(_db, _db.scoreEntryTable); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager(_db, _db.statisticTable); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager(_db, _db.statisticScopeTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager(_db, _db.statisticGameTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager(_db, _db.statisticGroupTable); } diff --git a/lib/data/db/tables/statistic_game_table.dart b/lib/data/db/tables/statistic_game_table.dart new file mode 100644 index 0000000..e1cc7d4 --- /dev/null +++ b/lib/data/db/tables/statistic_game_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGameTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get gameId => + text().references(GameTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, gameId}; +} diff --git a/lib/data/db/tables/statistic_group_table.dart b/lib/data/db/tables/statistic_group_table.dart new file mode 100644 index 0000000..cd642ad --- /dev/null +++ b/lib/data/db/tables/statistic_group_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGroupTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get groupId => + text().references(GroupTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, groupId}; +} diff --git a/lib/data/db/tables/statistic_scope_table.dart b/lib/data/db/tables/statistic_scope_table.dart new file mode 100644 index 0000000..3a9bcdc --- /dev/null +++ b/lib/data/db/tables/statistic_scope_table.dart @@ -0,0 +1,11 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticScopeTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get scope => text()(); + + @override + Set> get primaryKey => {statisticId, scope}; +} diff --git a/lib/data/db/tables/statistic_table.dart b/lib/data/db/tables/statistic_table.dart new file mode 100644 index 0000000..d627894 --- /dev/null +++ b/lib/data/db/tables/statistic_table.dart @@ -0,0 +1,10 @@ +import 'package:drift/drift.dart'; + +class StatisticTable extends Table { + TextColumn get id => text()(); + TextColumn get type => text()(); + TextColumn get timeframe => text().nullable()(); + + @override + Set> get primaryKey => {id}; +} diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart index 4b5df07..3d118f7 100644 --- a/lib/data/models/statistic.dart +++ b/lib/data/models/statistic.dart @@ -1,8 +1,10 @@ import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.dart'; +import 'package:uuid/uuid.dart'; class Statistic { + final String id; final StatisticType type; final List scopes; final Timeframe? timeframe; @@ -12,8 +14,14 @@ class Statistic { Statistic({ required this.type, required this.scopes, + String? id, this.timeframe, this.selectedGroups, this.selectedGames, - }); + }) : id = id ?? const Uuid().v4(); + + @override + String toString() { + return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)'; + } } diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart index 17706a1..9ac03aa 100644 --- a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -571,8 +571,8 @@ class _CreateStatisticViewState extends State { selectedGroups: selectedGroups, selectedGames: selectedGames, ); - // final db = Provider.of(context, listen: false); - // db.statisticDao.addStatistic(newStatistic); + final db = Provider.of(context, listen: false); + db.statisticDao.addStatistic(statistic: newStatistic); Navigator.of(context).pop(newStatistic); } } diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart index 3470a12..f551a8b 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -5,13 +5,12 @@ import 'package:tallee/core/constants.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; -import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart'; -import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; -import 'package:tallee/presentation/widgets/top_centered_message.dart'; +import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; class StatisticsView extends StatefulWidget { /// A view that displays player statistics @@ -38,11 +37,20 @@ class _StatisticsViewState extends State { 1, )); bool isLoading = true; + List statisticTiles = List.generate( + 4, + (_) => const InfoTile( + icon: Icons.sports_score, + title: 'Skeleton Statistic', + width: double.infinity, + content: Text('Skeleton content'), + ), + ); @override void initState() { super.initState(); - loadStatisticData(); + getStatisticTiles(context); } @override @@ -51,7 +59,8 @@ class _StatisticsViewState extends State { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Stack( - alignment: AlignmentDirectional.center, + alignment: AlignmentDirectional.bottomCenter, + fit: StackFit.expand, children: [ SingleChildScrollView( child: AppSkeleton( @@ -62,73 +71,7 @@ class _StatisticsViewState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.matches, - icon: Icons.groups_rounded, - value: matchCount, - ), - SizedBox(width: constraints.maxWidth * 0.05), - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.groups, - icon: Icons.groups_rounded, - value: groupCount, - ), - ], - ), - SizedBox(height: constraints.maxHeight * 0.02), - Visibility( - visible: - winCounts.isEmpty && - matchCounts.isEmpty && - winRates.isEmpty, - replacement: Column( - children: [ - StatisticsTile( - icon: Icons.sports_score, - title: loc.wins, - width: constraints.maxWidth * 0.95, - values: winCounts, - itemCount: 3, - barColor: Colors.green, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.percent, - title: loc.winrate, - width: constraints.maxWidth * 0.95, - values: winRates, - itemCount: 5, - barColor: Colors.orange[700]!, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.casino, - title: loc.amount_of_matches, - width: constraints.maxWidth * 0.95, - values: matchCounts, - itemCount: 10, - barColor: Colors.blue, - ), - ], - ), - child: TopCenteredMessage( - icon: Icons.info, - title: loc.info, - message: AppLocalizations.of( - context, - ).no_statistics_available, - ), - ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), - ], + children: [...statisticTiles], ), ), ), @@ -138,8 +81,8 @@ class _StatisticsViewState extends State { child: MainMenuButton( text: loc.create_statistic, icon: Icons.bar_chart, - onPressed: () { - Navigator.push( + onPressed: () async { + Statistic newStatistic = await Navigator.push( context, adaptivePageRoute( builder: (context) => CreateStatisticView( @@ -147,6 +90,18 @@ class _StatisticsViewState extends State { ), ), ); + final newTile = InfoTile( + icon: Icons.sports_score, + title: newStatistic.type.name, + width: MediaQuery.sizeOf(context).width * 0.95, + content: Text( + '${newStatistic.id}\n${newStatistic.scopes}\n${newStatistic.type}\n${newStatistic.timeframe}\n${newStatistic.selectedGroups}\n${newStatistic.selectedGames}\n', + ), + ); + + setState(() { + statisticTiles.add(newTile); + }); }, ), ), @@ -196,6 +151,30 @@ class _StatisticsViewState extends State { }); } + Future getStatisticTiles(BuildContext context) async { + isLoading = true; + final db = Provider.of(context, listen: false); + final statistics = await db.statisticDao.getAllStatistics(); + + setState(() { + statisticTiles = []; + for (var statistic in statistics) { + statisticTiles.add( + InfoTile( + icon: Icons.sports_score, + title: statistic.type.name, + width: MediaQuery.sizeOf(context).width * 0.95, + content: Text(statistic.toString()), + ), + ); + statisticTiles.add( + SizedBox(height: MediaQuery.sizeOf(context).height * 0.02), + ); + } + }); + isLoading = false; + } + /// Calculates the number of wins for each player /// and returns a sorted list of tuples (playerName, winCount) List<(Player, int)> _calculateWinsForAllPlayers({ diff --git a/pubspec.yaml b/pubspec.yaml index 8ca8550..b6563cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+273 +version: 0.0.33+274 environment: sdk: ^3.8.1 diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart new file mode 100644 index 0000000..b969b4c --- /dev/null +++ b/test/db_tests/statistics/statistic_test.dart @@ -0,0 +1,124 @@ +import 'dart:core'; + +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNotNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/score_entry.dart'; +import 'package:tallee/data/models/statistic.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Group testGroup1; + late Group testGroup2; + late Game testGame; + late Match testMatch1; + late Match testMatch2; + late Match testMatchOnlyPlayers; + late Match testMatchOnlyGroup; + final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() async { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testPlayer5 = Player(name: 'Eve'); + testGroup1 = Group( + name: 'Test Group 1', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testGroup2 = Group( + name: 'Test Group 2', + description: '', + members: [testPlayer4, testPlayer5], + ); + testGame = Game( + name: 'Test Game', + ruleset: Ruleset.singleWinner, + description: 'A test game', + color: GameColor.blue, + icon: '', + ); + testMatch1 = Match( + name: 'First Test Match', + game: testGame, + group: testGroup1, + players: [testPlayer4, testPlayer5], + scores: {testPlayer4.id: ScoreEntry(score: 1)}, + ); + testMatch2 = Match( + name: 'Second Test Match', + game: testGame, + group: testGroup2, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyPlayers = Match( + name: 'Test Match with Players', + game: testGame, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyGroup = Match( + name: 'Test Match with Group', + game: testGame, + group: testGroup2, + players: testGroup2.members, + ); + }); + await database.playerDao.addPlayersAsList( + players: [ + testPlayer1, + testPlayer2, + testPlayer3, + testPlayer4, + testPlayer5, + ], + ); + await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); + await database.gameDao.addGame(game: testGame); + }); + tearDown(() async { + await database.close(); + }); + + test('Adding/fetching statistic works correclty', () async { + final statistic = Statistic( + type: StatisticType.averageScore, + scopes: [StatisticScope.allPlayers, StatisticScope.selectedGames], + timeframe: Timeframe.allTime, + selectedGames: [testGame], + selectedGroups: [testGroup1], + ); + + final added = await database.statisticDao.addStatistic( + statistic: statistic, + ); + expect(added, isTrue); + + final fetched = await database.statisticDao.getStatisticById(statistic.id); + expect(fetched, isNotNull); + expect(fetched!.type, statistic.type); + }); +} -- 2.49.1 From 2e3b46253388f10bdd63e84d2c859971d19bce45 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 15:11:56 +0200 Subject: [PATCH 06/39] feat: added statistic tile factory --- .../statistic_tile_factory.dart | 318 ++++++++++++++++++ .../statistics_view/statistics_view.dart | 285 +++------------- .../widgets/tiles/statistics_tile.dart | 223 ++++++++---- 3 files changed, 529 insertions(+), 297 deletions(-) create mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart new file mode 100644 index 0000000..c482a7d --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -0,0 +1,318 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' + show translateStatisticTypeToString; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +/// Build the [StatisticsTile] for a given [Statistic]. + +Widget buildStatisticTile({ + required Statistic statistic, + required List matches, + required List players, + required BuildContext context, + double? width, + int itemCount = 5, +}) { + final filteredMatches = _getFilterMatches(statistic, matches); + final filteredPlayers = _getFilteredPlayers( + statistic, + players, + filteredMatches, + ); + + print('Building tile for statistic: $statistic'); + print('Filtered matches count: ${filteredMatches.length}'); + print('Filtered players count: ${filteredPlayers.length}'); + + final values = _computeValuesForType( + type: statistic.type, + matches: filteredMatches, + players: filteredPlayers, + ); + print(values); + + return StatisticsTile( + icon: _getStatisticIcon(type: statistic.type), + title: translateStatisticTypeToString(statistic.type, context), + width: width ?? MediaQuery.sizeOf(context).width * 0.95, + values: values, + itemCount: itemCount, + barColor: _getStatisticColor(statistic), + statistic: statistic, + ); +} + +List _getFilterMatches(Statistic statistic, List matches) { + List filteredMatches = matches; + + // Filter timeframe + if (statistic.scopes.contains(StatisticScope.timeframe) && + statistic.timeframe != null) { + final minDate = _getMinimumDate(timeframe: statistic.timeframe!); + print( + 'Filtering matches by timeframe: ${statistic.timeframe}, minDate: $minDate', + ); + if (minDate != null) { + filteredMatches = matches + .where((m) => m.endedAt != null && m.endedAt!.isAfter(minDate)) + .toList(); + } + } + + // Filter games + if (statistic.scopes.contains(StatisticScope.selectedGames) && + (statistic.selectedGames?.isNotEmpty ?? false)) { + final gameIds = statistic.selectedGames!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((match) => gameIds.contains(match.game.id)) + .toList(); + } + + // Filter groups + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final groupIds = statistic.selectedGroups!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((m) => m.group != null && groupIds.contains(m.group!.id)) + .toList(); + } + + return filteredMatches; +} + +/// Returns a [Player] List with the selected players depending on +List _getFilteredPlayers( + Statistic statistic, + List allPlayers, + List filteredMatches, +) { + // allPlayers + if (statistic.scopes.contains(StatisticScope.allPlayers)) { + return allPlayers; + } + + // selectedGroups -> only members + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final Set ids = { + for (final g in statistic.selectedGroups!) + for (final p in g.members) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); + } + + // Else -> all players from filtered matches + final Set ids = { + for (final m in filteredMatches) + for (final p in m.players) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); +} + +/// Returns a [DateTime] with the minimum time and date the [timeframe] allows +DateTime? _getMinimumDate({required Timeframe timeframe}) { + final now = DateTime.now(); + switch (timeframe) { + case Timeframe.last7Days: + return now.subtract(const Duration(days: 7)); + case Timeframe.last30Days: + return now.subtract(const Duration(days: 30)); + case Timeframe.last90Days: + return now.subtract(const Duration(days: 90)); + case Timeframe.last180Days: + return now.subtract(const Duration(days: 180)); + case Timeframe.lastYear: + return now.subtract(const Duration(days: 365)); + case Timeframe.allTime: + return null; + } +} + +/// Computes the statistic values for each player based on the statistic type +/// and returns a list of (Player, value) tuples sorted descending by value. +List<(Player, num)> _computeValuesForType({ + required StatisticType type, + required List matches, + required List players, +}) { + switch (type) { + case StatisticType.totalMatches: + return _sortDesc( + players.map((p) => (p, _matchesPlayed(p, matches) as num)).toList(), + ); + + case StatisticType.totalWins: + return _sortDesc( + players.map((p) => (p, _wins(p, matches) as num)).toList(), + ); + + case StatisticType.totalLosses: + return _sortDesc( + players + .map( + (p) => + (p, (_matchesPlayed(p, matches) - _wins(p, matches)) as num), + ) + .toList(), + ); + + case StatisticType.totalScore: + return _sortDesc( + players.map((p) => (p, _totalScore(p, matches) as num)).toList(), + ); + + case StatisticType.averageScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final avg = scores.isEmpty + ? 0.0 + : double.parse( + (scores.reduce((a, b) => a + b) / scores.length) + .toStringAsFixed(2), + ); + return (p, avg as num); + }).toList(), + ); + + case StatisticType.bestScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final best = scores.isEmpty ? 0 : scores.reduce(max); + return (p, best as num); + }).toList(), + ); + + case StatisticType.worstScore: + // Ascending here is more meaningful for "worst", but keep the + // existing tile semantics (largest bar = top entry) by sorting + // descending on the inverse — i.e. show smallest score on top. + final entries = players.map((p) { + final scores = _scoresOf(p, matches); + final worst = scores.isEmpty ? 0 : scores.reduce(min); + return (p, worst as num); + }).toList(); + entries.sort((a, b) => a.$2.compareTo(b.$2)); + return entries; + + case StatisticType.winrate: + return _sortDesc( + players.map((p) { + final played = _matchesPlayed(p, matches); + final wins = _wins(p, matches); + final rate = played == 0 + ? 0.0 + : double.parse((wins / played).toStringAsFixed(2)); + return (p, rate as num); + }).toList(), + ); + } +} + +/* Helper functions for different statistic types */ + +/// Detemerines how many matches the player has played in the given list of matches. +int _matchesPlayed(Player p, List matches) => + matches.where((m) => m.players.any((mp) => mp.id == p.id)).length; + +/// Determines how many matches the player has won in the given list of matches. +int _wins(Player p, List matches) => + matches.where((m) => m.mvp.any((mp) => mp.id == p.id)).length; + +/// Determines the total score of the player in the given list of matches. +int _totalScore(Player p, List matches) { + var total = 0; + for (final m in matches) { + final s = m.scores[p.id]; + if (s != null) total += s.score; + } + return total; +} + +/// Returns a list of all scores the player has achieved in the given list of matches. +List _scoresOf(Player p, List matches) => [ + for (final m in matches) + if (m.scores[p.id] != null) m.scores[p.id]!.score, +]; + +/// Returns the list of entries sorted descending by the statistic value. +List<(Player, num)> _sortDesc(List<(Player, num)> entries) { + entries.sort((a, b) => b.$2.compareTo(a.$2)); + return entries; +} + +/* Icon and color */ + +/// Returns the icon for the given statistic type. +IconData _getStatisticIcon({required StatisticType type}) { + switch (type) { + case StatisticType.totalMatches: + return Icons.casino; + case StatisticType.totalWins: + return Icons.emoji_events; + case StatisticType.totalLosses: + return Icons.sentiment_dissatisfied; + case StatisticType.totalScore: + return Icons.scoreboard; + case StatisticType.averageScore: + return Icons.show_chart; + case StatisticType.bestScore: + return Icons.trending_up; + case StatisticType.worstScore: + return Icons.trending_down; + case StatisticType.winrate: + return Icons.percent; + } +} + +/// The bar colors for the statistic tiles +const List _palette = [ + Color(0xFF4CAF50), // green + Color(0xFF2196F3), // blue + Color(0xFFFF9800), // orange + Color(0xFFE91E63), // pink + Color(0xFF9C27B0), // purple + Color(0xFF00BCD4), // cyan + Color(0xFFFFC107), // amber + Color(0xFF3F51B5), // indigo + Color(0xFF8BC34A), // light green + Color(0xFFFF5722), // deep orange +]; + +/// Returns a color from the palette based on the statistic's ID as random seed. +Color _getStatisticColor(Statistic stat) { + final seed = stat.id.hashCode; + return _palette[seed.abs() % _palette.length]; +} + +/* Skeleton data */ + +/// A placeholder tile with mock data for the loading state. +Widget buildSkeletonStatisticTile({required BuildContext context}) { + final count = 4 + Random().nextInt(5); // 4..8 + final values = <(Player, num)>[ + for (var i = 0; i < count; i++) + (Player(name: 'Player ${i + 1}'), count - i), + ]; + + return StatisticsTile( + icon: Icons.bar_chart, + title: 'Skeleton title', + width: MediaQuery.sizeOf(context).width * 0.95, + values: values, + itemCount: count, + barColor: _palette[Random().nextInt(_palette.length)], + statistic: Statistic( + type: StatisticType.totalMatches, + scopes: [StatisticScope.allPlayers], + timeframe: Timeframe.last7Days, + ), + ); +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart index f551a8b..8c3ac16 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -8,9 +8,9 @@ import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; -import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; class StatisticsView extends StatefulWidget { /// A view that displays player statistics @@ -21,36 +21,19 @@ class StatisticsView extends StatefulWidget { } class _StatisticsViewState extends State { - int matchCount = 0; - int groupCount = 0; - - List<(Player, int)> winCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, int)> matchCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, double)> winRates = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); bool isLoading = true; - List statisticTiles = List.generate( - 4, - (_) => const InfoTile( - icon: Icons.sports_score, - title: 'Skeleton Statistic', - width: double.infinity, - content: Text('Skeleton content'), - ), - ); + List _allMatches = const []; + List _allPlayers = const []; + List statisticTiles = []; @override void initState() { super.initState(); - getStatisticTiles(context); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + getStatisticTiles(context); + }); } @override @@ -66,13 +49,14 @@ class _StatisticsViewState extends State { child: AppSkeleton( enabled: isLoading, fixLayoutBuilder: true, - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [...statisticTiles], - ), + child: Column( + spacing: 12, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...statisticTiles, + SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), + ], ), ), ), @@ -86,17 +70,16 @@ class _StatisticsViewState extends State { context, adaptivePageRoute( builder: (context) => CreateStatisticView( - onStatisticCreated: loadStatisticData, + onStatisticCreated: () => getStatisticTiles(context), ), ), ); - final newTile = InfoTile( - icon: Icons.sports_score, - title: newStatistic.type.name, - width: MediaQuery.sizeOf(context).width * 0.95, - content: Text( - '${newStatistic.id}\n${newStatistic.scopes}\n${newStatistic.type}\n${newStatistic.timeframe}\n${newStatistic.selectedGroups}\n${newStatistic.selectedGames}\n', - ), + if (!context.mounted) return; + final newTile = buildStatisticTile( + statistic: newStatistic, + matches: _allMatches, + players: _allPlayers, + context: context, ); setState(() { @@ -111,205 +94,47 @@ class _StatisticsViewState extends State { ); } - /// Loads matches and players from the database - /// and calculates statistics for each player - void loadStatisticData() { + Future getStatisticTiles(BuildContext context) async { + setState(() { + isLoading = true; + statisticTiles = List.generate( + 4, + (index) => Column( + children: [ + buildSkeletonStatisticTile(context: context), + const SizedBox(height: 12), + ], + ), + ); + }); + final db = Provider.of(context, listen: false); - Future.wait([ + final results = await Future.wait([ + db.statisticDao.getAllStatistics(), db.matchDao.getAllMatches(), db.playerDao.getAllPlayers(), - db.matchDao.getMatchCount(), - db.groupDao.getGroupCount(), Future.delayed(Constants.MINIMUM_SKELETON_DURATION), - ]).then((results) async { - if (!mounted) return; + ]); - final matches = results[0] as List; - final players = results[1] as List; - matchCount = results[2] as int; - groupCount = results[3] as int; + if (!mounted) return; - winCounts = _calculateWinsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - matchCounts = _calculateMatchAmountsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - winRates = computeWinRatePercent( - winCounts: winCounts, - matchCounts: matchCounts, - ); - - setState(() { - isLoading = false; - }); - }); - } - - Future getStatisticTiles(BuildContext context) async { - isLoading = true; - final db = Provider.of(context, listen: false); - final statistics = await db.statisticDao.getAllStatistics(); + final statistics = results[0] as List; + _allMatches = results[1] as List; + _allPlayers = results[2] as List; setState(() { - statisticTiles = []; - for (var statistic in statistics) { - statisticTiles.add( - InfoTile( - icon: Icons.sports_score, - title: statistic.type.name, - width: MediaQuery.sizeOf(context).width * 0.95, - content: Text(statistic.toString()), + statisticTiles = [ + for (final statistic in statistics) ...[ + buildStatisticTile( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + context: context, ), - ); - statisticTiles.add( - SizedBox(height: MediaQuery.sizeOf(context).height * 0.02), - ); - } + ], + ]; + isLoading = false; }); - isLoading = false; - } - - /// Calculates the number of wins for each player - /// and returns a sorted list of tuples (playerName, winCount) - List<(Player, int)> _calculateWinsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> winCounts = []; - final loc = AppLocalizations.of(context); - - // Getting the winners - for (var match in matches) { - final mvps = match.mvp; - for (var winner in mvps) { - final index = winCounts.indexWhere((entry) => entry.$1.id == winner.id); - // -1 means winner not found in winCounts - if (index != -1) { - final current = winCounts[index].$2; - winCounts[index] = (winner, current + 1); - } else { - winCounts.add((winner, 1)); - } - } - } - - // Adding all players with zero wins - for (var player in players) { - final index = winCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in winCounts - if (index == -1) { - winCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < winCounts.length; i++) { - final playerId = winCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - winCounts[i] = (player, winCounts[i].$2); - } - - winCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return winCounts; - } - - /// Calculates the number of matches played for each player - /// and returns a sorted list of tuples (playerName, matchCount) - List<(Player, int)> _calculateMatchAmountsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> matchCounts = []; - final loc = AppLocalizations.of(context); - - // Counting matches for each player - for (var match in matches) { - for (Player player in match.players) { - // Check if the player is already in matchCounts - final index = matchCounts.indexWhere( - (entry) => entry.$1.id == player.id, - ); - - // -1 -> not found - if (index == -1) { - // Add new entry - matchCounts.add((player, 1)); - } else { - // Update existing entry - final currentMatchAmount = matchCounts[index].$2; - matchCounts[index] = (player, currentMatchAmount + 1); - } - } - } - - // Adding all players with zero matches - for (var player in players) { - final index = matchCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in matchCounts - if (index == -1) { - matchCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < matchCounts.length; i++) { - final playerId = matchCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - matchCounts[i] = (player, matchCounts[i].$2); - } - - matchCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return matchCounts; - } - - List<(Player, double)> computeWinRatePercent({ - required List<(Player, int)> winCounts, - required List<(Player, int)> matchCounts, - }) { - final Map winsMap = {for (var e in winCounts) e.$1: e.$2}; - final Map matchesMap = {for (var e in matchCounts) e.$1: e.$2}; - - // Get all unique player names - final player = {...matchesMap.keys}; - - // Calculate win rates - final result = player.map((name) { - final int w = winsMap[name] ?? 0; - final int m = matchesMap[name] ?? 0; - // Calculate percentage and round to 2 decimal places - // Avoid division by zero - final double percent = (m > 0) - ? double.parse(((w / m)).toStringAsFixed(2)) - : 0; - return (name, percent); - }).toList(); - - // Sort the result: first by winrate descending, - // then by wins descending in case of a tie - result.sort((a, b) { - final cmp = b.$2.compareTo(a.$2); - if (cmp != 0) return cmp; - final wa = winsMap[a.$1] ?? 0; - final wb = winsMap[b.$1] ?? 0; - return wb.compareTo(wa); - }); - - return result; } } diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index ea9cb49..b740eb5 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -1,9 +1,13 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; @@ -23,6 +27,7 @@ class StatisticsTile extends StatelessWidget { required this.values, required this.itemCount, required this.barColor, + required this.statistic, }); /// The icon displayed next to the title. @@ -43,6 +48,8 @@ class StatisticsTile extends StatelessWidget { /// The color of the bars representing the values. final Color barColor; + final Statistic statistic; + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -52,7 +59,7 @@ class StatisticsTile extends StatelessWidget { title: title, icon: icon, content: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Visibility( visible: values.isNotEmpty, replacement: Center( @@ -63,80 +70,146 @@ class StatisticsTile extends StatelessWidget { builder: (context, constraints) { final maxBarWidth = constraints.maxWidth * 0.65; return Column( - children: List.generate(min(values.length, itemCount), (index) { - /// The maximum wins among all players - final maxMatches = values.isNotEmpty ? values[0].$2 : 0; + children: [ + // Bar chart + ...List.generate(min(values.length, itemCount), (index) { + /// The maximum wins among all players + final maxMatches = values.isNotEmpty ? values[0].$2 : 0; - /// Fraction of wins - final double fraction = (maxMatches > 0) - ? (values[index].$2 / maxMatches) - : 0.0; + /// Fraction of wins + final double fraction = (maxMatches > 0) + ? (values[index].$2 / maxMatches) + : 0.0; - /// Calculated width for current the bar - final double barWidth = maxBarWidth * fraction; + /// Calculated width for current the bar + final double barWidth = maxBarWidth * fraction; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Stack( - children: [ - Container( - height: 24, - width: barWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: barColor, - ), - ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: values[index].$1.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: getNameCountText(values[index].$1), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor.withAlpha( - 150, - ), - ), - ), - ], + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Stack( + children: [ + // Bar + Container( + height: 24, + width: barWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: barColor, ), ), - ), - ], - ), - const Spacer(), - Center( - child: Text( - values[index].$2 <= 1 && values[index].$2 is double - ? values[index].$2.toStringAsFixed(2) - : values[index].$2.toString(), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + + // Player + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: RichText( + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: values[index].$1.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: getNameCountText( + values[index].$1, + ), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor + .withAlpha(150), + ), + ), + ], + ), + ), + ), + ], + ), + const Spacer(), + + // Value + Center( + child: Text( + values[index].$2 <= 1 && + values[index].$2 is double + ? values[index].$2.toStringAsFixed(2) + : values[index].$2.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), ), - ), - ], + ], + ), + ); + }), + + // Group & Game info + if (statistic.selectedGames != null || + statistic.selectedGroups != null) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + // Game + if (statistic.selectedGames != null && + statistic.selectedGames!.isNotEmpty) + Row( + spacing: 8, + children: [ + const Icon( + RpgAwesome.clovers_card, + color: CustomTheme.hintColor, + size: 20, + ), + Text( + getGameText(statistic.selectedGames!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + + // Group + if (statistic.selectedGroups != null && + statistic.selectedGroups!.isNotEmpty) + Row( + spacing: 8, + children: [ + const Icon( + Icons.groups, + color: CustomTheme.hintColor, + ), + Text( + getGroupText(statistic.selectedGroups!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ], + ), ), - ); - }), + ], ); }, ), @@ -144,4 +217,20 @@ class StatisticsTile extends StatelessWidget { ), ); } + + String getGroupText(List groups) { + var text = groups[0].name; + if (groups.length > 1) { + return '$text + ${groups.length - 1}'; + } + return text; + } + + String getGameText(List games) { + var text = games[0].name; + if (games.length > 1) { + return '$text + ${games.length - 1}'; + } + return text; + } } -- 2.49.1 From 18a5dcfdd53d0484e7d6dcdcbb7bc22d936e3df1 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:03:43 +0200 Subject: [PATCH 07/39] Changed colors --- lib/core/common.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index 312e3fa..cf77e35 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -63,9 +63,9 @@ Color getColorFromGameColor(GameColor color) { case GameColor.orange: return const Color(0xFFef681f); case GameColor.pink: - return Colors.pink; + return const Color(0xFFE91E63); case GameColor.teal: - return Colors.teal; + return const Color(0xFF00BCD4); } } -- 2.49.1 From 5a2cc790ddd86c8d5ef4c7f707d549995048478d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:03:58 +0200 Subject: [PATCH 08/39] Renamed GameColor -> AppColor --- lib/core/common.dart | 40 +++++++++---------- lib/core/enums.dart | 2 +- lib/data/dao/game_dao.dart | 6 +-- lib/data/dao/statistic_game_dao.dart | 2 +- lib/data/models/game.dart | 8 ++-- lib/data/models/match.dart | 2 +- .../create_match/create_game_view.dart | 10 ++--- .../main_menu/match_view/match_view.dart | 2 +- lib/presentation/widgets/game_label.dart | 2 +- lib/presentation/widgets/tiles/game_tile.dart | 2 +- lib/services/data_transfer_service.dart | 2 +- test/db_tests/aggregates/match_test.dart | 2 +- test/db_tests/aggregates/team_test.dart | 2 +- test/db_tests/entities/game_test.dart | 16 ++++---- .../relationships/player_match_test.dart | 2 +- test/db_tests/statistics/statistic_test.dart | 2 +- test/db_tests/values/score_entry_test.dart | 2 +- test/services/data_transfer_service_test.dart | 14 +++---- 18 files changed, 59 insertions(+), 59 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index cf77e35..6f1db7f 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -24,47 +24,47 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } } -/// Translates a [GameColor] enum value to its corresponding localized string. -String translateGameColorToString(GameColor color, BuildContext context) { +/// Translates a [AppColor] enum value to its corresponding localized string. +String translateGameColorToString(AppColor color, BuildContext context) { final loc = AppLocalizations.of(context); switch (color) { - case GameColor.red: + case AppColor.red: return loc.color_red; - case GameColor.blue: + case AppColor.blue: return loc.color_blue; - case GameColor.green: + case AppColor.green: return loc.color_green; - case GameColor.yellow: + case AppColor.yellow: return loc.color_yellow; - case GameColor.purple: + case AppColor.purple: return loc.color_purple; - case GameColor.orange: + case AppColor.orange: return loc.color_orange; - case GameColor.pink: + case AppColor.pink: return loc.color_pink; - case GameColor.teal: + case AppColor.teal: return loc.color_teal; } } -/// Returns the [Color] object corresponding to a [GameColor] enum value. -Color getColorFromGameColor(GameColor color) { +/// Returns the [Color] object corresponding to a [AppColor] enum value. +Color getColorFromGameColor(AppColor color) { switch (color) { - case GameColor.red: + case AppColor.red: return Colors.red; - case GameColor.blue: + case AppColor.blue: return Colors.blue; - case GameColor.green: + case AppColor.green: return Colors.green; - case GameColor.yellow: + case AppColor.yellow: return const Color(0xFFF7CA28); - case GameColor.purple: + case AppColor.purple: return Colors.purple; - case GameColor.orange: + case AppColor.orange: return const Color(0xFFef681f); - case GameColor.pink: + case AppColor.pink: return const Color(0xFFE91E63); - case GameColor.teal: + case AppColor.teal: return const Color(0xFF00BCD4); } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 5d46a3f..073fd7a 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -43,7 +43,7 @@ enum Ruleset { } /// Different colors for highlighting games -enum GameColor { red, orange, yellow, green, teal, blue, purple, pink } +enum AppColor { red, orange, yellow, green, teal, blue, purple, pink } enum StatisticType { totalMatches, diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index a4c2300..1adfc9e 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -92,7 +92,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { name: row.name, ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), description: row.description, - color: GameColor.values.firstWhere((e) => e.name == row.color), + color: AppColor.values.firstWhere((e) => e.name == row.color), icon: row.icon, createdAt: row.createdAt, ), @@ -109,7 +109,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { name: result.name, ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), description: result.description, - color: GameColor.values.firstWhere((e) => e.name == result.color), + color: AppColor.values.firstWhere((e) => e.name == result.color), icon: result.icon, createdAt: result.createdAt, ); @@ -156,7 +156,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Updates the color of the game with the given [gameId]. Future updateGameColor({ required String gameId, - required GameColor color, + required AppColor color, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart index ea7260f..4ee5b84 100644 --- a/lib/data/dao/statistic_game_dao.dart +++ b/lib/data/dao/statistic_game_dao.dart @@ -25,7 +25,7 @@ class StatisticGameDao extends DatabaseAccessor name: result.name, ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), description: result.description, - color: GameColor.values.firstWhere((e) => e.name == result.color), + color: AppColor.values.firstWhere((e) => e.name == result.color), icon: result.icon, createdAt: result.createdAt, ), diff --git a/lib/data/models/game.dart b/lib/data/models/game.dart index 89bbd30..ec69204 100644 --- a/lib/data/models/game.dart +++ b/lib/data/models/game.dart @@ -8,13 +8,13 @@ class Game { final String name; final Ruleset ruleset; final String description; - final GameColor color; + final AppColor color; final String icon; Game({ required this.name, required this.ruleset, - this.color = GameColor.orange, + this.color = AppColor.orange, this.description = '', this.icon = '', String? id, @@ -33,7 +33,7 @@ class Game { String? name, Ruleset? ruleset, String? description, - GameColor? color, + AppColor? color, String? icon, }) { return Game( @@ -73,7 +73,7 @@ class Game { orElse: () => Ruleset.singleWinner, ), description = json['description'], - color = GameColor.values.firstWhere((e) => e.name == json['color']), + color = AppColor.values.firstWhere((e) => e.name == json['color']), icon = json['icon']; Map toJson() => { diff --git a/lib/data/models/match.dart b/lib/data/models/match.dart index 2c43fe3..601a01c 100644 --- a/lib/data/models/match.dart +++ b/lib/data/models/match.dart @@ -107,7 +107,7 @@ class Match { name: '', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group = null, diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index a5729be..29dc7d1 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -49,10 +49,10 @@ class _CreateGameViewState extends State { late final AppDatabase db; late List<(Ruleset, String)> _rulesets; - late List<(GameColor, String)> _colors; + late List<(AppColor, String)> _colors; Ruleset? selectedRuleset = Ruleset.singleWinner; - GameColor? selectedColor = GameColor.orange; + AppColor? selectedColor = AppColor.orange; /// Controller for the game name input field. final _gameNameController = TextEditingController(); @@ -87,10 +87,10 @@ class _CreateGameViewState extends State { ), ); _colors = List.generate( - GameColor.values.length, + AppColor.values.length, (index) => ( - GameColor.values[index], - translateGameColorToString(GameColor.values[index], context), + AppColor.values[index], + translateGameColorToString(AppColor.values[index], context), ), ); diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index 83ff069..3c01f45 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -39,7 +39,7 @@ class _MatchViewState extends State { game: Game( name: 'Game name', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group: Group( diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index 553e637..3eae9b1 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -12,7 +12,7 @@ class GameLabel extends StatelessWidget { final String title; final String description; - final GameColor color; + final AppColor color; @override Widget build(BuildContext context) { diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/game_tile.dart index ee5acf0..11d96d8 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/game_tile.dart @@ -51,7 +51,7 @@ class GameTile extends StatelessWidget { ? (badgeColor!.computeLuminance() > 0.5 ? Colors.black : Colors.white) : Colors.white; - final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange); + final gameColor = badgeColor ?? getColorFromGameColor(AppColor.orange); return GestureDetector( onTap: () async { diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 29199f8..0f2f8fa 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -278,7 +278,7 @@ class DataTransferService { name: 'Unknown', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); } diff --git a/test/db_tests/aggregates/match_test.dart b/test/db_tests/aggregates/match_test.dart index 00e6e46..1144a73 100644 --- a/test/db_tests/aggregates/match_test.dart +++ b/test/db_tests/aggregates/match_test.dart @@ -56,7 +56,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/aggregates/team_test.dart b/test/db_tests/aggregates/team_test.dart index fefdcc5..381d22b 100644 --- a/test/db_tests/aggregates/team_test.dart +++ b/test/db_tests/aggregates/team_test.dart @@ -49,7 +49,7 @@ void main() { testGame = Game( name: 'Test Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/entities/game_test.dart b/test/db_tests/entities/game_test.dart index 778d43b..f7e7dcd 100644 --- a/test/db_tests/entities/game_test.dart +++ b/test/db_tests/entities/game_test.dart @@ -28,7 +28,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'A classic strategy game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); testGame2 = Game( @@ -36,7 +36,7 @@ void main() { name: 'Poker', ruleset: Ruleset.multipleWinners, description: 'Card game with multiple winners', - color: GameColor.red, + color: AppColor.red, icon: 'poker_icon', ); testGame3 = Game( @@ -44,7 +44,7 @@ void main() { name: 'Monopoly', ruleset: Ruleset.highestScore, description: 'A board game about real estate', - color: GameColor.orange, + color: AppColor.orange, icon: '', ); }); @@ -124,7 +124,7 @@ void main() { name: 'Game\'s & "Special" ', ruleset: Ruleset.multipleWinners, description: 'Description with émojis 🎮🎲', - color: GameColor.purple, + color: AppColor.purple, icon: '', ); await database.gameDao.addGame(game: specialGame); @@ -280,19 +280,19 @@ void main() { await database.gameDao.updateGameColor( gameId: testGame1.id, - color: GameColor.green, + color: AppColor.green, ); final updatedGame = await database.gameDao.getGameById( gameId: testGame1.id, ); - expect(updatedGame.color, GameColor.green); + expect(updatedGame.color, AppColor.green); }); test('updateGameColor() does nothing for non-existent game', () async { final updated = await database.gameDao.updateGameColor( gameId: 'non-existent-id', - color: GameColor.green, + color: AppColor.green, ); expect(updated, isFalse); @@ -336,7 +336,7 @@ void main() { name: newName, ); - const newGameColor = GameColor.teal; + const newGameColor = AppColor.teal; await database.gameDao.updateGameColor( gameId: testGame1.id, color: newGameColor, diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart index 6d879c3..fa7ec21 100644 --- a/test/db_tests/relationships/player_match_test.dart +++ b/test/db_tests/relationships/player_match_test.dart @@ -42,7 +42,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart index b969b4c..cd08615 100644 --- a/test/db_tests/statistics/statistic_test.dart +++ b/test/db_tests/statistics/statistic_test.dart @@ -59,7 +59,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/values/score_entry_test.dart b/test/db_tests/values/score_entry_test.dart index f6cc292..593d194 100644 --- a/test/db_tests/values/score_entry_test.dart +++ b/test/db_tests/values/score_entry_test.dart @@ -40,7 +40,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/services/data_transfer_service_test.dart b/test/services/data_transfer_service_test.dart index 586138a..94ed977 100644 --- a/test/services/data_transfer_service_test.dart +++ b/test/services/data_transfer_service_test.dart @@ -45,7 +45,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'Strategic board game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); @@ -448,19 +448,19 @@ void main() { Game( name: 'Red Game', ruleset: Ruleset.singleWinner, - color: GameColor.red, + color: AppColor.red, icon: 'icon', ), Game( name: 'Blue Game', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Green Game', ruleset: Ruleset.singleWinner, - color: GameColor.green, + color: AppColor.green, icon: 'icon', ), ]; @@ -484,19 +484,19 @@ void main() { Game( name: 'Highest Score Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Lowest Score Game', ruleset: Ruleset.lowestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Single Winner', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), ]; -- 2.49.1 From d82206319a69e3cd19a44499a16375394ffc37be Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:04:14 +0200 Subject: [PATCH 09/39] Renamed GameColor -> AppColor --- lib/core/common.dart | 2 +- .../main_menu/match_view/create_match/create_game_view.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index 6f1db7f..31d039a 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -25,7 +25,7 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } /// Translates a [AppColor] enum value to its corresponding localized string. -String translateGameColorToString(AppColor color, BuildContext context) { +String translateAppColorToString(AppColor color, BuildContext context) { final loc = AppLocalizations.of(context); switch (color) { case AppColor.red: diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 29dc7d1..06f15db 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -90,7 +90,7 @@ class _CreateGameViewState extends State { AppColor.values.length, (index) => ( AppColor.values[index], - translateGameColorToString(AppColor.values[index], context), + translateAppColorToString(AppColor.values[index], context), ), ); @@ -507,7 +507,7 @@ class _CreateGameViewState extends State { ), Padding( padding: const EdgeInsets.only(right: 5), - child: Text(translateGameColorToString(selectedColor!, context)), + child: Text(translateAppColorToString(selectedColor!, context)), ), Transform.rotate( angle: pi / 2, -- 2.49.1 From 398c7a4168f7d5eba5c2c631e2e6b7d6bda70d29 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:07:09 +0200 Subject: [PATCH 10/39] Refactoring --- lib/core/common.dart | 2 +- .../create_match/choose_game_view.dart | 2 +- .../create_match/create_game_view.dart | 4 ++-- .../statistic_tile_factory.dart | 24 ++++++------------- lib/presentation/widgets/game_label.dart | 2 +- lib/presentation/widgets/tiles/game_tile.dart | 2 +- 6 files changed, 13 insertions(+), 23 deletions(-) diff --git a/lib/core/common.dart b/lib/core/common.dart index 31d039a..df88ea3 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -48,7 +48,7 @@ String translateAppColorToString(AppColor color, BuildContext context) { } /// Returns the [Color] object corresponding to a [AppColor] enum value. -Color getColorFromGameColor(AppColor color) { +Color getColorFromAppColor(AppColor color) { switch (color) { case AppColor.red: return Colors.red; diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 3c51cab..4d085d7 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -164,7 +164,7 @@ class _ChooseGameViewState extends State { game.ruleset, context, ), - badgeColor: getColorFromGameColor(game.color), + badgeColor: getColorFromAppColor(game.color), isHighlighted: selectedGameId == game.id, onTap: () async { setState(() { diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 06f15db..0671055 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -467,7 +467,7 @@ class _CreateGameViewState extends State { height: 16, margin: const EdgeInsets.only(left: 12), decoration: BoxDecoration( - color: getColorFromGameColor( + color: getColorFromAppColor( _colors[index].$1, ), shape: BoxShape.circle, @@ -501,7 +501,7 @@ class _CreateGameViewState extends State { width: 16, height: 16, decoration: BoxDecoration( - color: getColorFromGameColor(selectedColor!), + color: getColorFromAppColor(selectedColor!), shape: BoxShape.circle, ), ), diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart index c482a7d..8461f6d 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:tallee/core/common.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; @@ -9,8 +10,11 @@ import 'package:tallee/presentation/views/main_menu/statistics_view/create_stati show translateStatisticTypeToString; import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; -/// Build the [StatisticsTile] for a given [Statistic]. +List _colorPalette = AppColor.values + .map((c) => getColorFromAppColor(c)) + .toList(); +/// Build the [StatisticsTile] for a given [Statistic]. Widget buildStatisticTile({ required Statistic statistic, required List matches, @@ -272,24 +276,10 @@ IconData _getStatisticIcon({required StatisticType type}) { } } -/// The bar colors for the statistic tiles -const List _palette = [ - Color(0xFF4CAF50), // green - Color(0xFF2196F3), // blue - Color(0xFFFF9800), // orange - Color(0xFFE91E63), // pink - Color(0xFF9C27B0), // purple - Color(0xFF00BCD4), // cyan - Color(0xFFFFC107), // amber - Color(0xFF3F51B5), // indigo - Color(0xFF8BC34A), // light green - Color(0xFFFF5722), // deep orange -]; - /// Returns a color from the palette based on the statistic's ID as random seed. Color _getStatisticColor(Statistic stat) { final seed = stat.id.hashCode; - return _palette[seed.abs() % _palette.length]; + return _colorPalette[seed.abs() % _colorPalette.length]; } /* Skeleton data */ @@ -308,7 +298,7 @@ Widget buildSkeletonStatisticTile({required BuildContext context}) { width: MediaQuery.sizeOf(context).width * 0.95, values: values, itemCount: count, - barColor: _palette[Random().nextInt(_palette.length)], + barColor: _colorPalette[Random().nextInt(_colorPalette.length)], statistic: Statistic( type: StatisticType.totalMatches, scopes: [StatisticScope.allPlayers], diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index 3eae9b1..dd179d6 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -16,7 +16,7 @@ class GameLabel extends StatelessWidget { @override Widget build(BuildContext context) { - final backgroundColor = getColorFromGameColor(color); + final backgroundColor = getColorFromAppColor(color); final fontColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/game_tile.dart index 11d96d8..4fb12d1 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/game_tile.dart @@ -51,7 +51,7 @@ class GameTile extends StatelessWidget { ? (badgeColor!.computeLuminance() > 0.5 ? Colors.black : Colors.white) : Colors.white; - final gameColor = badgeColor ?? getColorFromGameColor(AppColor.orange); + final gameColor = badgeColor ?? getColorFromAppColor(AppColor.orange); return GestureDetector( onTap: () async { -- 2.49.1 From ffd52055fabc8dc1acb52213d4bea2888992e34d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:28:29 +0200 Subject: [PATCH 11/39] fixed bar length --- .../widgets/tiles/statistics_tile.dart | 144 +++++++++++------- 1 file changed, 90 insertions(+), 54 deletions(-) diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index b740eb5..0abe3bb 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; @@ -62,86 +63,121 @@ class StatisticsTile extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Visibility( visible: values.isNotEmpty, + + // No data avaiable message replacement: Center( heightFactor: 4, child: Text(loc.no_data_available), ), + + // Bar chart child: LayoutBuilder( builder: (context, constraints) { - final maxBarWidth = constraints.maxWidth * 0.65; + final maxBarWidth = constraints.maxWidth * 0.8; + final displayCount = min(values.length, itemCount); + final displayValues = values.take(displayCount).toList(); + final maxVal = displayValues.isNotEmpty + ? displayValues.fold( + 0, + (currentMax, entry) => + entry.$2 > currentMax ? entry.$2 : currentMax, + ) + : 0; + return Column( children: [ - // Bar chart - ...List.generate(min(values.length, itemCount), (index) { - /// The maximum wins among all players - final maxMatches = values.isNotEmpty ? values[0].$2 : 0; - + // Bars + ...List.generate(displayCount, (index) { /// Fraction of wins - final double fraction = (maxMatches > 0) - ? (values[index].$2 / maxMatches) + final double fraction = (maxVal > 0) + ? (displayValues[index].$2 / maxVal) : 0.0; /// Calculated width for current the bar - final double barWidth = maxBarWidth * fraction; + final double barWidth = (maxBarWidth * fraction).clamp( + 0.0, + maxBarWidth, + ); return Padding( padding: const EdgeInsets.symmetric(vertical: 2.0), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - Stack( - children: [ - // Bar - Container( - height: 24, - width: barWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: barColor, - ), - ), - - // Player - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: values[index].$1.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: getNameCountText( - values[index].$1, - ), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor - .withAlpha(150), - ), - ), - ], + SizedBox( + width: maxBarWidth, + child: Stack( + clipBehavior: Clip.hardEdge, + children: [ + // Bar + Container( + height: 24, + width: barWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: barColor, ), ), - ), - ], + + // Player + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: RichText( + maxLines: 1, + softWrap: false, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: displayValues[index].$1.name, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: + barColor == + getColorFromAppColor( + AppColor.yellow, + ) + ? const Color(0xFF101010) + : CustomTheme.textColor, + ), + ), + TextSpan( + text: getNameCountText( + displayValues[index].$1, + ), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: + (barColor == + getColorFromAppColor( + AppColor.yellow, + ) + ? const Color( + 0xFF101010, + ) + : CustomTheme.textColor) + .withAlpha(150), + ), + ), + ], + ), + ), + ), + ], + ), ), const Spacer(), // Value Center( child: Text( - values[index].$2 <= 1 && - values[index].$2 is double - ? values[index].$2.toStringAsFixed(2) - : values[index].$2.toString(), + displayValues[index].$2 <= 1 && + displayValues[index].$2 is double + ? displayValues[index].$2.toStringAsFixed(2) + : displayValues[index].$2.toString(), textAlign: TextAlign.center, style: const TextStyle( fontSize: 16, -- 2.49.1 From f65ea09cbe1c0ad6cfd6f6a1148665091f1ebb9c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 17:33:41 +0200 Subject: [PATCH 12/39] Added spacing --- .../widgets/tiles/statistics_tile.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index 0abe3bb..ad850dd 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -196,11 +196,10 @@ class StatisticsTile extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisAlignment: MainAxisAlignment.start, children: [ // Game - if (statistic.selectedGames != null && - statistic.selectedGames!.isNotEmpty) + if (hasGroup) Row( spacing: 8, children: [ @@ -220,10 +219,10 @@ class StatisticsTile extends StatelessWidget { ), ], ), + if (hasGroup && hasGame) const SizedBox(width: 20), // Group - if (statistic.selectedGroups != null && - statistic.selectedGroups!.isNotEmpty) + if (hasGroup) Row( spacing: 8, children: [ @@ -269,4 +268,10 @@ class StatisticsTile extends StatelessWidget { } return text; } + + bool get hasGroup => + statistic.selectedGroups != null && statistic.selectedGroups!.isNotEmpty; + + bool get hasGame => + statistic.selectedGames != null && statistic.selectedGames!.isNotEmpty; } -- 2.49.1 From 428f9670103b47e40c969c7bd40b076d6a1158a3 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 23:09:08 +0200 Subject: [PATCH 13/39] feat: displayCount --- lib/data/dao/statistic_dao.dart | 22 +++++- lib/data/db/database.g.dart | 78 ++++++++++++++++++- lib/data/db/tables/statistic_table.dart | 1 + lib/data/models/statistic.dart | 4 +- .../statistic_tile_factory.dart | 3 - .../widgets/tiles/statistics_tile.dart | 6 +- pubspec.yaml | 2 +- 7 files changed, 98 insertions(+), 18 deletions(-) diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart index 39904fc..7bdebde 100644 --- a/lib/data/dao/statistic_dao.dart +++ b/lib/data/dao/statistic_dao.dart @@ -20,6 +20,7 @@ class StatisticDao extends DatabaseAccessor id: statistic.id, type: statistic.type.name, timeframe: Value(statistic.timeframe?.name), + displayCount: Value(statistic.displayCount), ), mode: InsertMode.insertOrReplace, ); @@ -59,12 +60,13 @@ class StatisticDao extends DatabaseAccessor return Statistic( type: StatisticType.values.firstWhere((type) => type.name == row.type), scopes: scopes, - id: row.id, timeframe: Timeframe.values.firstWhereOrNull( (t) => t.name == row.timeframe, ), selectedGroups: groups, selectedGames: games, + displayCount: row.displayCount, + id: row.id, ); } return null; @@ -73,9 +75,9 @@ class StatisticDao extends DatabaseAccessor /// Retrieves all statistics from the database, including their associated groups and games. Future> getAllStatistics() async { final query = select(statisticTable); - final rows = await query.get(); + final result = await query.get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); final games = await db.statisticGameDao.getGamesForStatistic(row.id); @@ -84,17 +86,29 @@ class StatisticDao extends DatabaseAccessor (type) => type.name == row.type, ), scopes: [], - id: row.id, timeframe: Timeframe.values.firstWhereOrNull( (t) => t.name == row.timeframe, ), selectedGroups: groups, selectedGames: games, + displayCount: row.displayCount, + id: row.id, ); }), ); } + /* Update */ + + Future updateDisplayCount(String statisticId, int displayCount) async { + final rowsUpdated = + await (update(statisticTable) + ..where((tbl) => tbl.id.equals(statisticId))) + .write(StatisticTableCompanion(displayCount: Value(displayCount))); + + return rowsUpdated > 0; + } + /* Delete */ Future deleteStatistic(String statisticId) async { diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index 489b890..3a9d277 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -2767,8 +2767,20 @@ class $StatisticTableTable extends StatisticTable type: DriftSqlType.string, requiredDuringInsert: false, ); + static const VerificationMeta _displayCountMeta = const VerificationMeta( + 'displayCount', + ); @override - List get $columns => [id, type, timeframe]; + late final GeneratedColumn displayCount = GeneratedColumn( + 'display_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(5), + ); + @override + List get $columns => [id, type, timeframe, displayCount]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -2800,6 +2812,15 @@ class $StatisticTableTable extends StatisticTable timeframe.isAcceptableOrUnknown(data['timeframe']!, _timeframeMeta), ); } + if (data.containsKey('display_count')) { + context.handle( + _displayCountMeta, + displayCount.isAcceptableOrUnknown( + data['display_count']!, + _displayCountMeta, + ), + ); + } return context; } @@ -2821,6 +2842,10 @@ class $StatisticTableTable extends StatisticTable DriftSqlType.string, data['${effectivePrefix}timeframe'], ), + displayCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}display_count'], + )!, ); } @@ -2835,10 +2860,12 @@ class StatisticTableData extends DataClass final String id; final String type; final String? timeframe; + final int displayCount; const StatisticTableData({ required this.id, required this.type, this.timeframe, + required this.displayCount, }); @override Map toColumns(bool nullToAbsent) { @@ -2848,6 +2875,7 @@ class StatisticTableData extends DataClass if (!nullToAbsent || timeframe != null) { map['timeframe'] = Variable(timeframe); } + map['display_count'] = Variable(displayCount); return map; } @@ -2858,6 +2886,7 @@ class StatisticTableData extends DataClass timeframe: timeframe == null && nullToAbsent ? const Value.absent() : Value(timeframe), + displayCount: Value(displayCount), ); } @@ -2870,6 +2899,7 @@ class StatisticTableData extends DataClass id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), timeframe: serializer.fromJson(json['timeframe']), + displayCount: serializer.fromJson(json['displayCount']), ); } @override @@ -2879,6 +2909,7 @@ class StatisticTableData extends DataClass 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'timeframe': serializer.toJson(timeframe), + 'displayCount': serializer.toJson(displayCount), }; } @@ -2886,16 +2917,21 @@ class StatisticTableData extends DataClass String? id, String? type, Value timeframe = const Value.absent(), + int? displayCount, }) => StatisticTableData( id: id ?? this.id, type: type ?? this.type, timeframe: timeframe.present ? timeframe.value : this.timeframe, + displayCount: displayCount ?? this.displayCount, ); StatisticTableData copyWithCompanion(StatisticTableCompanion data) { return StatisticTableData( id: data.id.present ? data.id.value : this.id, type: data.type.present ? data.type.value : this.type, timeframe: data.timeframe.present ? data.timeframe.value : this.timeframe, + displayCount: data.displayCount.present + ? data.displayCount.value + : this.displayCount, ); } @@ -2904,37 +2940,42 @@ class StatisticTableData extends DataClass return (StringBuffer('StatisticTableData(') ..write('id: $id, ') ..write('type: $type, ') - ..write('timeframe: $timeframe') + ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, type, timeframe); + int get hashCode => Object.hash(id, type, timeframe, displayCount); @override bool operator ==(Object other) => identical(this, other) || (other is StatisticTableData && other.id == this.id && other.type == this.type && - other.timeframe == this.timeframe); + other.timeframe == this.timeframe && + other.displayCount == this.displayCount); } class StatisticTableCompanion extends UpdateCompanion { final Value id; final Value type; final Value timeframe; + final Value displayCount; final Value rowid; const StatisticTableCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), this.rowid = const Value.absent(), }); StatisticTableCompanion.insert({ required String id, required String type, this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), type = Value(type); @@ -2942,12 +2983,14 @@ class StatisticTableCompanion extends UpdateCompanion { Expression? id, Expression? type, Expression? timeframe, + Expression? displayCount, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (type != null) 'type': type, if (timeframe != null) 'timeframe': timeframe, + if (displayCount != null) 'display_count': displayCount, if (rowid != null) 'rowid': rowid, }); } @@ -2956,12 +2999,14 @@ class StatisticTableCompanion extends UpdateCompanion { Value? id, Value? type, Value? timeframe, + Value? displayCount, Value? rowid, }) { return StatisticTableCompanion( id: id ?? this.id, type: type ?? this.type, timeframe: timeframe ?? this.timeframe, + displayCount: displayCount ?? this.displayCount, rowid: rowid ?? this.rowid, ); } @@ -2978,6 +3023,9 @@ class StatisticTableCompanion extends UpdateCompanion { if (timeframe.present) { map['timeframe'] = Variable(timeframe.value); } + if (displayCount.present) { + map['display_count'] = Variable(displayCount.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -2990,6 +3038,7 @@ class StatisticTableCompanion extends UpdateCompanion { ..write('id: $id, ') ..write('type: $type, ') ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount, ') ..write('rowid: $rowid') ..write(')')) .toString(); @@ -7516,6 +7565,7 @@ typedef $$StatisticTableTableCreateCompanionBuilder = required String id, required String type, Value timeframe, + Value displayCount, Value rowid, }); typedef $$StatisticTableTableUpdateCompanionBuilder = @@ -7523,6 +7573,7 @@ typedef $$StatisticTableTableUpdateCompanionBuilder = Value id, Value type, Value timeframe, + Value displayCount, Value rowid, }); @@ -7645,6 +7696,11 @@ class $$StatisticTableTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnFilters(column), + ); + Expression statisticScopeTableRefs( Expression Function($$StatisticScopeTableTableFilterComposer f) f, ) { @@ -7744,6 +7800,11 @@ class $$StatisticTableTableOrderingComposer column: $table.timeframe, builder: (column) => ColumnOrderings(column), ); + + ColumnOrderings get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnOrderings(column), + ); } class $$StatisticTableTableAnnotationComposer @@ -7764,6 +7825,11 @@ class $$StatisticTableTableAnnotationComposer GeneratedColumn get timeframe => $composableBuilder(column: $table.timeframe, builder: (column) => column); + GeneratedColumn get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => column, + ); + Expression statisticScopeTableRefs( Expression Function($$StatisticScopeTableTableAnnotationComposer a) f, ) { @@ -7880,11 +7946,13 @@ class $$StatisticTableTableTableManager Value id = const Value.absent(), Value type = const Value.absent(), Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), Value rowid = const Value.absent(), }) => StatisticTableCompanion( id: id, type: type, timeframe: timeframe, + displayCount: displayCount, rowid: rowid, ), createCompanionCallback: @@ -7892,11 +7960,13 @@ class $$StatisticTableTableTableManager required String id, required String type, Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), Value rowid = const Value.absent(), }) => StatisticTableCompanion.insert( id: id, type: type, timeframe: timeframe, + displayCount: displayCount, rowid: rowid, ), withReferenceMapper: (p0) => p0 diff --git a/lib/data/db/tables/statistic_table.dart b/lib/data/db/tables/statistic_table.dart index d627894..ef368a5 100644 --- a/lib/data/db/tables/statistic_table.dart +++ b/lib/data/db/tables/statistic_table.dart @@ -4,6 +4,7 @@ class StatisticTable extends Table { TextColumn get id => text()(); TextColumn get type => text()(); TextColumn get timeframe => text().nullable()(); + IntColumn get displayCount => integer().withDefault(const Constant(5))(); @override Set> get primaryKey => {id}; diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart index 3d118f7..4fdbb05 100644 --- a/lib/data/models/statistic.dart +++ b/lib/data/models/statistic.dart @@ -10,14 +10,16 @@ class Statistic { final Timeframe? timeframe; final List? selectedGroups; final List? selectedGames; + final int displayCount; Statistic({ required this.type, required this.scopes, - String? id, this.timeframe, this.selectedGroups, this.selectedGames, + this.displayCount = 5, + String? id, }) : id = id ?? const Uuid().v4(); @override diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart index 8461f6d..fd43390 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -21,7 +21,6 @@ Widget buildStatisticTile({ required List players, required BuildContext context, double? width, - int itemCount = 5, }) { final filteredMatches = _getFilterMatches(statistic, matches); final filteredPlayers = _getFilteredPlayers( @@ -46,7 +45,6 @@ Widget buildStatisticTile({ title: translateStatisticTypeToString(statistic.type, context), width: width ?? MediaQuery.sizeOf(context).width * 0.95, values: values, - itemCount: itemCount, barColor: _getStatisticColor(statistic), statistic: statistic, ); @@ -297,7 +295,6 @@ Widget buildSkeletonStatisticTile({required BuildContext context}) { title: 'Skeleton title', width: MediaQuery.sizeOf(context).width * 0.95, values: values, - itemCount: count, barColor: _colorPalette[Random().nextInt(_colorPalette.length)], statistic: Statistic( type: StatisticType.totalMatches, diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index ad850dd..24d51fc 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -26,7 +26,6 @@ class StatisticsTile extends StatelessWidget { required this.title, required this.width, required this.values, - required this.itemCount, required this.barColor, required this.statistic, }); @@ -43,9 +42,6 @@ class StatisticsTile extends StatelessWidget { /// A list of tuples containing labels and their corresponding numeric values. final List<(Player, num)> values; - /// The maximum number of items to display. - final int itemCount; - /// The color of the bars representing the values. final Color barColor; @@ -74,7 +70,7 @@ class StatisticsTile extends StatelessWidget { child: LayoutBuilder( builder: (context, constraints) { final maxBarWidth = constraints.maxWidth * 0.8; - final displayCount = min(values.length, itemCount); + final displayCount = min(values.length, statistic.displayCount); final displayValues = values.take(displayCount).toList(); final maxVal = displayValues.isNotEmpty ? displayValues.fold( diff --git a/pubspec.yaml b/pubspec.yaml index b6563cc..d6fd994 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+274 +version: 0.0.33+276 environment: sdk: ^3.8.1 -- 2.49.1 From bccd47e20ec00ba4c8d4d2ac01b6a0a637ad2e72 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 23:27:14 +0200 Subject: [PATCH 14/39] Refactoring --- lib/data/dao/game_dao.dart | 32 +++++++------- lib/data/dao/group_dao.dart | 58 ++++++++++++------------- lib/data/dao/match_dao.dart | 61 ++++++++++++++------------- lib/data/dao/player_dao.dart | 48 ++++++++++----------- lib/data/dao/player_group_dao.dart | 26 +++++++----- lib/data/dao/player_match_dao.dart | 23 +++++----- lib/data/dao/score_entry_dao.dart | 42 +++++++++--------- lib/data/dao/statistic_game_dao.dart | 16 +++---- lib/data/dao/statistic_scope_dao.dart | 6 +-- lib/data/dao/team_dao.dart | 22 +++++----- 10 files changed, 171 insertions(+), 163 deletions(-) diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index 1adfc9e..3ee8ebd 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -77,8 +77,8 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Returns `true` if the game exists, `false` otherwise. Future gameExists({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all games from the database. @@ -103,15 +103,15 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Retrieves a [Game] by its [gameId]. Future getGameById({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final result = await query.getSingle(); + final row = await query.getSingle(); return Game( - id: result.id, - name: result.name, - ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), - description: result.description, - color: AppColor.values.firstWhere((e) => e.name == result.color), - icon: result.icon, - createdAt: result.createdAt, + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, ); } @@ -123,7 +123,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String name, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -135,7 +135,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required Ruleset ruleset, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(ruleset: Value(ruleset.name)), ); return rowsAffected > 0; @@ -147,7 +147,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String description, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(description: Value(description)), ); return rowsAffected > 0; @@ -159,7 +159,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required AppColor color, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(color: Value(color.name)), ); return rowsAffected > 0; @@ -171,7 +171,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String icon, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(icon: Value(icon)), ); return rowsAffected > 0; @@ -182,7 +182,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Deletes the game with the given [gameId] from the database. /// Returns `true` if the game was deleted, `false` if the game did not exist. Future deleteGame({required String gameId}) async { - final query = delete(gameTable)..where((g) => g.id.equals(gameId)); + final query = delete(gameTable)..where((tbl) => tbl.id.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index 2de2ab9..8d1c0a2 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -143,16 +143,16 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final query = select(groupTable); final result = await query.get(); return Future.wait( - result.map((groupData) async { + result.map((row) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: groupData.id, + groupId: row.id, ); return Group( - id: groupData.id, - name: groupData.name, - description: groupData.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: groupData.createdAt, + createdAt: row.createdAt, ); }), ); @@ -161,18 +161,18 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Retrieves a [Group] by its [groupId], including its members. Future getGroupById({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingle(); + final row = await query.getSingle(); List members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupId, ); return Group( - id: result.id, - name: result.name, - description: result.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: result.createdAt, + createdAt: row.createdAt, ); } @@ -180,7 +180,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future getGroupCount() async { final count = await (selectOnly(groupTable)..addColumns([groupTable.id.count()])) - .map((row) => row.read(groupTable.id.count())) + .map((tbl) => tbl.read(groupTable.id.count())) .getSingle(); return count ?? 0; } @@ -190,28 +190,28 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future> getGroupsByPlayer({required String playerId}) async { final playerGroups = await (select( playerGroupTable, - )..where((pg) => pg.playerId.equals(playerId))).get(); + )..where((tbl) => tbl.playerId.equals(playerId))).get(); if (playerGroups.isEmpty) return []; final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList(); - final rows = + final result = await (select(groupTable) - ..where((g) => g.id.isIn(groupIds)) - ..orderBy([(g) => OrderingTerm.desc(g.createdAt)])) + ..where((tbl) => tbl.id.isIn(groupIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) .get(); return Future.wait( - rows.map((groupData) async { + result.map((row) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: groupData.id, + groupId: row.id, ); return Group( - id: groupData.id, - name: groupData.name, - description: groupData.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: groupData.createdAt, + createdAt: row.createdAt, ); }), ); @@ -221,8 +221,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Returns `true` if the group exists, `false` otherwise. Future groupExists({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /* Delete */ @@ -252,9 +252,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String name, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(name: Value(name)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(name: Value(name))); return rowsAffected > 0; } @@ -265,9 +264,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String description, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(description: Value(description)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(description: Value(description))); return rowsAffected > 0; } } diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index e8414f4..74611b6 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -258,15 +258,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Returns `true` if the match exists, otherwise `false`. Future matchExists({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves the number of matches in the database. Future getMatchCount() async { final count = await (selectOnly(matchTable)..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -279,10 +279,12 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return Future.wait( result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); + Group? group; if (row.groupId != null) { group = await db.groupDao.getGroupById(groupId: row.groupId!); } + final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); @@ -312,13 +314,13 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Retrieves a [Match] by its [matchId]. Future getMatchById({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingle(); + final row = await query.getSingle(); - final game = await db.gameDao.getGameById(gameId: result.gameId); + final game = await db.gameDao.getGameById(gameId: row.gameId); Group? group; - if (result.groupId != null) { - group = await db.groupDao.getGroupById(groupId: result.groupId!); + if (row.groupId != null) { + group = await db.groupDao.getGroupById(groupId: row.groupId!); } final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); @@ -328,15 +330,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { final teams = await _getMatchTeams(matchId: matchId); return Match( - id: result.id, - name: result.name, + id: row.id, + name: row.name, game: game, group: group, players: players, teams: teams.isEmpty ? null : teams, - notes: result.notes, - createdAt: result.createdAt, - endedAt: result.endedAt, + notes: row.notes, + createdAt: row.createdAt, + endedAt: row.endedAt, scores: scores, ); } @@ -347,7 +349,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { await (selectOnly(matchTable) ..where(matchTable.gameId.equals(gameId)) ..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -355,19 +357,19 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> getMatchesByPlayer({required String playerId}) async { final playerMatches = await (select( playerMatchTable, - )..where((pm) => pm.playerId.equals(playerId))).get(); + )..where((tbl) => tbl.playerId.equals(playerId))).get(); if (playerMatches.isEmpty) return []; - final matchIds = playerMatches.map((pm) => pm.matchId).toSet().toList(); - final rows = + final matchIds = playerMatches.map((tbl) => tbl.matchId).toSet().toList(); + final result = await (select(matchTable) - ..where((m) => m.id.isIn(matchIds)) - ..orderBy([(m) => OrderingTerm.desc(m.createdAt)])) + ..where((tbl) => tbl.id.isIn(matchIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) .get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); Group? group; @@ -403,16 +405,17 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Queries the database directly, filtering by [groupId]. Future> getMatchesByGroup({required String groupId}) async { final query = select(matchTable)..where((m) => m.groupId.equals(groupId)); - final rows = await query.get(); + final result = await query.get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); final group = await db.groupDao.getGroupById(groupId: groupId); final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); final teams = await _getMatchTeams(matchId: row.id); + return Match( id: row.id, name: row.name, @@ -432,7 +435,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> _getMatchTeams({required String matchId}) async { // Get all unique team IDs from PlayerMatchTable for this match final playerMatchQuery = select(db.playerMatchTable) - ..where((pm) => pm.matchId.equals(matchId) & pm.teamId.isNotNull()); + ..where((tbl) => tbl.matchId.equals(matchId) & tbl.teamId.isNotNull()); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; @@ -459,7 +462,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String name, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(name: Value(name)), ); @@ -474,7 +477,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String? groupId, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(groupId: Value(groupId)), ); @@ -487,7 +490,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String notes, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(notes: Value(notes)), ); @@ -498,7 +501,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Sets the groupId to null. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future removeMatchGroup({required String matchId}) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( const MatchTableCompanion(groupId: Value(null)), ); @@ -512,7 +515,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required DateTime endedAt, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(endedAt: Value(endedAt)), ); @@ -524,7 +527,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes the match with the given [matchId] from the database. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future deleteMatch({required String matchId}) async { - final query = delete(matchTable)..where((g) => g.id.equals(matchId)); + final query = delete(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -540,7 +543,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes all matches associated with a specific game. /// Returns the number of matches deleted. Future deleteMatchesByGame({required String gameId}) async { - final query = delete(matchTable)..where((m) => m.gameId.equals(gameId)); + final query = delete(matchTable)..where((tbl) => tbl.gameId.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected; } diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index a6fd1c5..1a60243 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -113,7 +113,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { Future getPlayerCount() async { final count = await (selectOnly(playerTable)..addColumns([playerTable.id.count()])) - .map((row) => row.read(playerTable.id.count())) + .map((tbl) => tbl.read(playerTable.id.count())) .getSingle(); return count ?? 0; } @@ -122,8 +122,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns `true` if the player exists, `false` otherwise. Future playerExists({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all players from the database. @@ -146,13 +146,13 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Retrieves a [Player] by their [id]. Future getPlayerById({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingle(); + final row = await query.getSingle(); return Player( - id: result.id, - name: result.name, - description: result.description, - createdAt: result.createdAt, - nameCount: result.nameCount, + id: row.id, + name: row.name, + description: row.description, + createdAt: row.createdAt, + nameCount: row.nameCount, ); } @@ -174,7 +174,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return transaction(() async { final previousPlayer = await (select( playerTable, - )..where((p) => p.id.equals(playerId))).getSingleOrNull(); + )..where((tbl) => tbl.id.equals(playerId))).getSingleOrNull(); if (previousPlayer == null) return false; final previousName = previousPlayer.name; @@ -186,7 +186,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final rowsAffected = await (update( playerTable, - )..where((p) => p.id.equals(playerId))).write( + )..where((tbl) => tbl.id.equals(playerId))).write( PlayerTableCompanion( name: Value(name), nameCount: Value(newNameCount), @@ -203,9 +203,9 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { } else if (remainingCount > 1 && previousCount > 0) { // Shift every player above the gap down by one to keep numbering in order. await (update(playerTable)..where( - (p) => - p.name.equals(previousName) & - p.nameCount.isBiggerThanValue(previousCount), + (tbl) => + tbl.name.equals(previousName) & + tbl.nameCount.isBiggerThanValue(previousCount), )) .write( PlayerTableCompanion.custom( @@ -226,9 +226,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String description, }) async { final rowsAffected = - await (update(playerTable)..where((g) => g.id.equals(playerId))).write( - PlayerTableCompanion(description: Value(description)), - ); + await (update(playerTable)..where((tbl) => tbl.id.equals(playerId))) + .write(PlayerTableCompanion(description: Value(description))); return rowsAffected > 0; } @@ -237,7 +236,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Deletes the player with the given [id] from the database. /// Returns `true` if the player was deleted, `false` if the player did not exist. Future deletePlayer({required String playerId}) async { - final query = delete(playerTable)..where((p) => p.id.equals(playerId)); + final query = delete(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -248,7 +247,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns the highest name count if players with the same name exist, /// otherwise `null`. Future getNameCount({required String name}) async { - final query = select(playerTable)..where((p) => p.name.equals(name)); + final query = select(playerTable)..where((tbl) => tbl.name.equals(name)); final result = await query.get(); return result.length; } @@ -259,7 +258,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String playerId, required int nameCount, }) async { - final query = update(playerTable)..where((p) => p.id.equals(playerId)); + final query = update(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.write( PlayerTableCompanion(nameCount: Value(nameCount)), ); @@ -269,8 +268,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future getPlayerWithHighestNameCount({required String name}) async { final query = select(playerTable) - ..where((p) => p.name.equals(name)) - ..orderBy([(p) => OrderingTerm.desc(p.nameCount)]) + ..where((tbl) => tbl.name.equals(name)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.nameCount)]) ..limit(1); final result = await query.getSingleOrNull(); if (result != null) { @@ -324,9 +323,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future initializeNameCount({required String name}) async { final rowsAffected = - await (update(playerTable)..where((p) => p.name.equals(name))).write( - const PlayerTableCompanion(nameCount: Value(1)), - ); + await (update(playerTable)..where((tbl) => tbl.name.equals(name))) + .write(const PlayerTableCompanion(nameCount: Value(1))); return rowsAffected > 0; } diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index 5139eea..b48dc23 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -46,15 +46,15 @@ class PlayerGroupDao extends DatabaseAccessor ), ])..where(playerGroupTable.groupId.equals(groupId)); - final results = await query.map((row) => row.readTable(playerTable)).get(); - return results + final result = await query.map((row) => row.readTable(playerTable)).get(); + return result .map( - (result) => Player( - id: result.id, - createdAt: result.createdAt, - name: result.name, - nameCount: result.nameCount, - description: result.description, + (row) => Player( + id: row.id, + createdAt: row.createdAt, + name: row.name, + nameCount: row.nameCount, + description: row.description, ), ) .toList(); @@ -67,7 +67,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = select(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final result = await query.getSingleOrNull(); return result != null; } @@ -88,7 +90,7 @@ class PlayerGroupDao extends DatabaseAccessor await db.transaction(() async { // Remove all existing players from the group final deleteQuery = delete(db.playerGroupTable) - ..where((p) => p.groupId.equals(groupId)); + ..where((tbl) => tbl.groupId.equals(groupId)); await deleteQuery.go(); // Add new players to the player table if they don't exist @@ -128,7 +130,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = delete(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index d119468..912cfcc 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -40,7 +40,7 @@ class PlayerMatchDao extends DatabaseAccessor await (selectOnly(playerMatchTable) ..where(playerMatchTable.matchId.equals(matchId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -56,7 +56,7 @@ class PlayerMatchDao extends DatabaseAccessor ..where(playerMatchTable.matchId.equals(matchId)) ..where(playerMatchTable.playerId.equals(playerId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -66,7 +66,7 @@ class PlayerMatchDao extends DatabaseAccessor Future> getPlayersOfMatch({required String matchId}) async { final result = await (select( playerMatchTable, - )..where((p) => p.matchId.equals(matchId))).get(); + )..where((tbl) => tbl.matchId.equals(matchId))).get(); if (result.isEmpty) return []; @@ -85,8 +85,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final result = await (select(playerMatchTable) - ..where((p) => p.matchId.equals(matchId)) - ..where((p) => p.teamId.equals(teamId))) + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.teamId.equals(teamId))) .get(); if (result.isEmpty) return []; @@ -109,7 +109,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final rowsAffected = await (update(playerMatchTable)..where( - (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + (tbl) => + tbl.matchId.equals(matchId) & tbl.playerId.equals(playerId), )) .write(PlayerMatchTableCompanion(teamId: Value(teamId))); return rowsAffected > 0; @@ -143,9 +144,9 @@ class PlayerMatchDao extends DatabaseAccessor // Remove old players if (playersToRemove.isNotEmpty) { await (delete(playerMatchTable)..where( - (pg) => - pg.matchId.equals(matchId) & - pg.playerId.isIn(playersToRemove.toList()), + (tbl) => + tbl.matchId.equals(matchId) & + tbl.playerId.isIn(playersToRemove.toList()), )) .go(); } @@ -182,8 +183,8 @@ class PlayerMatchDao extends DatabaseAccessor required String playerId, }) async { final query = delete(playerMatchTable) - ..where((pg) => pg.matchId.equals(matchId)) - ..where((pg) => pg.playerId.equals(playerId)); + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.playerId.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/score_entry_dao.dart b/lib/data/dao/score_entry_dao.dart index 830135d..276e8fd 100644 --- a/lib/data/dao/score_entry_dao.dart +++ b/lib/data/dao/score_entry_dao.dart @@ -70,10 +70,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = select(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final result = await query.getSingleOrNull(); @@ -91,7 +91,7 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final result = await query.get(); final Map scoresByPlayer = {}; @@ -113,8 +113,10 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)) - ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ) + ..orderBy([(tbl) => OrderingTerm.asc(tbl.roundNumber)]); final result = await query.get(); return result .map( @@ -136,8 +138,8 @@ class ScoreEntryDao extends DatabaseAccessor final query = selectOnly(scoreEntryTable) ..where(scoreEntryTable.matchId.equals(matchId)) ..addColumns([scoreEntryTable.roundNumber.max()]); - final result = await query.getSingle(); - return result.read(scoreEntryTable.roundNumber.max()); + final row = await query.getSingle(); + return row.read(scoreEntryTable.roundNumber.max()); } /// Aggregates the total score for a player in a match by summing all their @@ -166,10 +168,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final rowsAffected = await (update(scoreEntryTable)..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(entry.roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(entry.roundNumber), )) .write( ScoreEntryTableCompanion( @@ -190,10 +192,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = delete(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final rowsAffected = await query.go(); return rowsAffected > 0; @@ -201,7 +203,7 @@ class ScoreEntryDao extends DatabaseAccessor Future deleteAllScoresForMatch({required String matchId}) async { final query = delete(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -211,7 +213,9 @@ class ScoreEntryDao extends DatabaseAccessor required String playerId, }) async { final query = delete(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart index 4ee5b84..76e3429 100644 --- a/lib/data/dao/statistic_game_dao.dart +++ b/lib/data/dao/statistic_game_dao.dart @@ -20,14 +20,14 @@ class StatisticGameDao extends DatabaseAccessor final results = await query.map((row) => row.readTable(gameTable)).get(); return results .map( - (result) => Game( - id: result.id, - name: result.name, - ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), - description: result.description, - color: AppColor.values.firstWhere((e) => e.name == result.color), - icon: result.icon, - createdAt: result.createdAt, + (row) => Game( + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, ), ) .toList(); diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart index 2027acd..d2fc315 100644 --- a/lib/data/dao/statistic_scope_dao.dart +++ b/lib/data/dao/statistic_scope_dao.dart @@ -18,10 +18,10 @@ class StatisticScopeDao extends DatabaseAccessor final results = await query.get(); return results .map( - (result) => StatisticScope.values.firstWhere( - (e) => e.name == result.scope, + (row) => StatisticScope.values.firstWhere( + (e) => e.name == row.scope, orElse: () => throw Exception( - 'Invalid scope value: ${result.scope} for statistic ID: $statisticId', + 'Invalid scope value: ${row.scope} for statistic ID: $statisticId', ), ), ) diff --git a/lib/data/dao/team_dao.dart b/lib/data/dao/team_dao.dart index cba68fb..333db68 100644 --- a/lib/data/dao/team_dao.dart +++ b/lib/data/dao/team_dao.dart @@ -86,7 +86,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future getTeamCount() async { final count = await (selectOnly(teamTable)..addColumns([teamTable.id.count()])) - .map((row) => row.read(teamTable.id.count())) + .map((tbl) => tbl.read(teamTable.id.count())) .getSingle(); return count ?? 0; } @@ -95,8 +95,8 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Returns `true` if the team exists, `false` otherwise. Future teamExists({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all teams from the database. @@ -119,12 +119,12 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Retrieves a [Team] by its [teamId], including its members. Future getTeamById({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final result = await query.getSingle(); + final row = await query.getSingle(); final members = await _getTeamMembers(teamId: teamId); return Team( - id: result.id, - name: result.name, - createdAt: result.createdAt, + id: row.id, + name: row.name, + createdAt: row.createdAt, members: members, ); } @@ -133,13 +133,13 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future> _getTeamMembers({required String teamId}) async { // Get all player_match entries with this teamId final playerMatchQuery = select(db.playerMatchTable) - ..where((pm) => pm.teamId.equals(teamId)); + ..where((tbl) => tbl.teamId.equals(teamId)); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; // Get unique player IDs - final playerIds = playerMatches.map((pm) => pm.playerId).toSet(); + final playerIds = playerMatches.map((tbl) => tbl.playerId).toSet(); // Fetch all players final players = await Future.wait( @@ -156,7 +156,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { required String name, }) async { final rowsAffected = - await (update(teamTable)..where((t) => t.id.equals(teamId))).write( + await (update(teamTable)..where((tbl) => tbl.id.equals(teamId))).write( TeamTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -175,7 +175,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Deletes the team with the given [teamId] from the database. /// Returns `true` if the team was deleted, `false` otherwise. Future deleteTeam({required String teamId}) async { - final query = delete(teamTable)..where((t) => t.id.equals(teamId)); + final query = delete(teamTable)..where((tbl) => tbl.id.equals(teamId)); final rowsAffected = await query.go(); return rowsAffected > 0; } -- 2.49.1 From 72442b53753ae30bdd05221595f4941c0939d310 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 24 May 2026 23:34:53 +0200 Subject: [PATCH 15/39] fix: added delete function --- lib/services/data_transfer_service.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 0f2f8fa..9dbf955 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -20,6 +20,7 @@ class DataTransferService { static Future deleteAllData(BuildContext context) async { final db = Provider.of(context, listen: false); + await db.statisticDao.deleteAllStatistics(); await db.matchDao.deleteAllMatches(); await db.teamDao.deleteAllTeams(); await db.groupDao.deleteAllGroups(); -- 2.49.1 From bfb40d2eab387ad632d4bae2942bc7e03075d6b2 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 00:39:01 +0200 Subject: [PATCH 16/39] feat: statistic detail view --- lib/data/dao/statistic_dao.dart | 3 +- lib/data/dao/statistic_game_dao.dart | 3 +- lib/data/dao/statistic_group_dao.dart | 3 +- lib/data/dao/statistic_scope_dao.dart | 4 +- lib/l10n/arb/app_de.arb | 7 + lib/l10n/arb/app_en.arb | 30 +-- lib/l10n/generated/app_localizations.dart | 58 +++--- lib/l10n/generated/app_localizations_de.dart | 36 ++-- lib/l10n/generated/app_localizations_en.dart | 32 ++-- .../main_menu/match_view/match_view.dart | 2 +- .../create_statistic_view.dart | 20 +- .../statistic_detail_view.dart | 178 ++++++++++++++++++ .../statistic_tile_factory.dart | 49 +++-- .../statistics_view/statistics_view.dart | 82 +++++--- .../widgets/tiles/statistics_tile.dart | 40 ++-- pubspec.yaml | 2 +- 16 files changed, 406 insertions(+), 143 deletions(-) create mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart index 7bdebde..092ceb0 100644 --- a/lib/data/dao/statistic_dao.dart +++ b/lib/data/dao/statistic_dao.dart @@ -80,12 +80,13 @@ class StatisticDao extends DatabaseAccessor result.map((row) async { final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); return Statistic( type: StatisticType.values.firstWhere( (type) => type.name == row.type, ), - scopes: [], + scopes: scopes, timeframe: Timeframe.values.firstWhereOrNull( (t) => t.name == row.timeframe, ), diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart index 76e3429..d546ce0 100644 --- a/lib/data/dao/statistic_game_dao.dart +++ b/lib/data/dao/statistic_game_dao.dart @@ -12,12 +12,13 @@ class StatisticGameDao extends DatabaseAccessor StatisticGameDao(super.db); /// Retrieves a list of games associated with a specific statistic. - Future> getGamesForStatistic(String statisticId) async { + Future?> getGamesForStatistic(String statisticId) async { final query = select(statisticGameTable).join([ innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)), ])..where(statisticGameTable.statisticId.equals(statisticId)); final results = await query.map((row) => row.readTable(gameTable)).get(); + if (results.isEmpty) return null; return results .map( (row) => Game( diff --git a/lib/data/dao/statistic_group_dao.dart b/lib/data/dao/statistic_group_dao.dart index 9eb9397..449b6a8 100644 --- a/lib/data/dao/statistic_group_dao.dart +++ b/lib/data/dao/statistic_group_dao.dart @@ -12,7 +12,7 @@ class StatisticGroupDao extends DatabaseAccessor StatisticGroupDao(super.db); /// Retrieves a list of groups associated with a specific statistic. - Future> getGroupsForStatistic(String statisticId) async { + Future?> getGroupsForStatistic(String statisticId) async { final query = select(statisticGroupTable).join([ innerJoin( groupTable, @@ -21,6 +21,7 @@ class StatisticGroupDao extends DatabaseAccessor ])..where(statisticGroupTable.statisticId.equals(statisticId)); final results = await query.map((row) => row.readTable(groupTable)).get(); + if (results.isEmpty) return null; final groups = await Future.wait( results.map((result) async { final groupMembers = await db.playerGroupDao.getPlayersOfGroup( diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart index d2fc315..eb286af 100644 --- a/lib/data/dao/statistic_scope_dao.dart +++ b/lib/data/dao/statistic_scope_dao.dart @@ -15,8 +15,8 @@ class StatisticScopeDao extends DatabaseAccessor final query = select(statisticScopeTable) ..where((tbl) => tbl.statisticId.equals(statisticId)); - final results = await query.get(); - return results + final result = await query.get(); + return result .map( (row) => StatisticScope.values.firstWhere( (e) => e.name == row.scope, diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 8f92cb0..b6ae56b 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -21,6 +21,13 @@ "color_yellow": "Gelb", "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Spielvorlage erstellen", "create_group": "Gruppe erstellen", "create_match": "Spiel erstellen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index b7548bd..baf5006 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -20,23 +20,29 @@ "color_teal": "Teal", "color_yellow": "Yellow", "confirm": "Confirm", - "could_not_add_player": "Could not add player", + "could_not_add_player": "Could not add player {playerName}", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Create Game", "create_group": "Create Group", "create_match": "Create match", "create_new_group": "Create new group", "create_new_match": "Create new match", "create_statistic": "Create statistic", - "create_statistic_classifier_subtitle": "Select which key metric you want to display", - "create_statistic_classifier_title": "Classifier", - "create_statistic_games_subtitle": "Select the filtered games", - "create_statistic_games_title": "Games", - "create_statistic_groups_subtitle": "Select the filtered groups", - "create_statistic_groups_title": "Groups", - "create_statistic_scope_subtitle": "Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.", - "create_statistic_scope_title": "Scope", - "create_statistic_timeframe_subtitle": "Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.", - "create_statistic_timeframe_title": "Timeframe", + "which_key_metric": "Select which key metric you want to display", + "classifier": "Classifier", + "select_the_filtered_games": "Select the filtered games", + "games": "Games", + "select_the_filtered_groups": "Select the filtered groups", + "select_main_filter": "Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.", + "scope": "Scope", + "select_a_timeframe_for_which_data_will_be_filtered": "Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.", + "timeframe": "Timeframe", "created_on": "Created on", "data": "Data", "data_successfully_deleted": "Data successfully deleted", @@ -54,6 +60,7 @@ } } }, + "filter": "Filter", "delete_group": "Delete Group", "delete_match": "Delete Match", "delete_player": "Delete player?", @@ -120,6 +127,7 @@ "no_results_entered_yet": "No results entered yet", "no_second_match_available": "No second match available", "no_statistics_available": "No statistics available", + "no_statistics_created_yet": "No statistics created yet", "none": "None", "none_group": "None", "not_available": "Not available", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index e8af2e8..20067d4 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -221,8 +221,8 @@ abstract class AppLocalizations { /// No description provided for @could_not_add_player. /// /// In en, this message translates to: - /// **'Could not add player'** - String could_not_add_player(Object playerName); + /// **'Could not add player {playerName}'** + String could_not_add_player(String playerName); /// No description provided for @create_game. /// @@ -260,65 +260,59 @@ abstract class AppLocalizations { /// **'Create statistic'** String get create_statistic; - /// No description provided for @create_statistic_classifier_subtitle. + /// No description provided for @which_key_metric. /// /// In en, this message translates to: /// **'Select which key metric you want to display'** - String get create_statistic_classifier_subtitle; + String get which_key_metric; - /// No description provided for @create_statistic_classifier_title. + /// No description provided for @classifier. /// /// In en, this message translates to: /// **'Classifier'** - String get create_statistic_classifier_title; + String get classifier; - /// No description provided for @create_statistic_games_subtitle. + /// No description provided for @select_the_filtered_games. /// /// In en, this message translates to: /// **'Select the filtered games'** - String get create_statistic_games_subtitle; + String get select_the_filtered_games; - /// No description provided for @create_statistic_games_title. + /// No description provided for @games. /// /// In en, this message translates to: /// **'Games'** - String get create_statistic_games_title; + String get games; - /// No description provided for @create_statistic_groups_subtitle. + /// No description provided for @select_the_filtered_groups. /// /// In en, this message translates to: /// **'Select the filtered groups'** - String get create_statistic_groups_subtitle; + String get select_the_filtered_groups; - /// No description provided for @create_statistic_groups_title. - /// - /// In en, this message translates to: - /// **'Groups'** - String get create_statistic_groups_title; - - /// No description provided for @create_statistic_scope_subtitle. + /// No description provided for @select_main_filter. /// /// In en, this message translates to: /// **'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'** - String get create_statistic_scope_subtitle; + String get select_main_filter; - /// No description provided for @create_statistic_scope_title. + /// No description provided for @scope. /// /// In en, this message translates to: /// **'Scope'** - String get create_statistic_scope_title; + String get scope; - /// No description provided for @create_statistic_timeframe_subtitle. + /// No description provided for @select_a_timeframe_for_which_data_will_be_filtered. /// /// In en, this message translates to: /// **'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'** - String get create_statistic_timeframe_subtitle; + String get select_a_timeframe_for_which_data_will_be_filtered; - /// No description provided for @create_statistic_timeframe_title. + /// No description provided for @timeframe. /// /// In en, this message translates to: /// **'Timeframe'** - String get create_statistic_timeframe_title; + String get timeframe; /// No description provided for @created_on. /// @@ -380,6 +374,12 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); + /// No description provided for @filter. + /// + /// In en, this message translates to: + /// **'Filter'** + String get filter; + /// No description provided for @delete_group. /// /// In en, this message translates to: @@ -776,6 +776,12 @@ abstract class AppLocalizations { /// **'No statistics available'** String get no_statistics_available; + /// No description provided for @no_statistics_created_yet. + /// + /// In en, this message translates to: + /// **'No statistics created yet'** + String get no_statistics_created_yet; + /// No description provided for @none. /// /// 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 4ff81bb..8152185 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -69,7 +69,7 @@ class AppLocalizationsDe extends AppLocalizations { String get confirm => 'Bestätigen'; @override - String could_not_add_player(Object playerName) { + String could_not_add_player(String playerName) { return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; } @@ -92,39 +92,33 @@ class AppLocalizationsDe extends AppLocalizations { String get create_statistic => 'Statistik erstellen'; @override - String get create_statistic_classifier_subtitle => - 'Wähle die anzuzeigende Hauptmetrik aus'; + String get which_key_metric => 'Select which key metric you want to display'; @override - String get create_statistic_classifier_title => 'Klassifikator'; + String get classifier => 'Classifier'; @override - String get create_statistic_games_subtitle => - 'Wähle die gefilterten Spielvorlagen'; + String get select_the_filtered_games => 'Select the filtered games'; @override - String get create_statistic_games_title => 'Spielvorlagen'; + String get games => 'Games'; @override - String get create_statistic_groups_subtitle => - 'Wähle die gefilterten Gruppen'; + String get select_the_filtered_groups => 'Select the filtered groups'; @override - String get create_statistic_groups_title => 'Gruppen'; + String get select_main_filter => + 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; @override - String get create_statistic_scope_subtitle => - 'Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.'; + String get scope => 'Scope'; @override - String get create_statistic_scope_title => 'Bereich'; + String get select_a_timeframe_for_which_data_will_be_filtered => + 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; @override - String get create_statistic_timeframe_subtitle => - 'Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.'; - - @override - String get create_statistic_timeframe_title => 'Zeitraum'; + String get timeframe => 'Timeframe'; @override String get created_on => 'Erstellt am'; @@ -166,6 +160,9 @@ class AppLocalizationsDe extends AppLocalizations { return 'Wenn du diese Spielvorlage löschst, $_temp0 mit dieser Spielvorlage ebenfalls gelöscht.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Gruppe löschen'; @@ -369,6 +366,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_statistics_available => 'Keine Statistiken verfügbar'; + @override + String get no_statistics_created_yet => 'No statistics created yet'; + @override String get none => 'Kein'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 4bc1dd7..2366ce2 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -69,8 +69,8 @@ class AppLocalizationsEn extends AppLocalizations { String get confirm => 'Confirm'; @override - String could_not_add_player(Object playerName) { - return 'Could not add player'; + String could_not_add_player(String playerName) { + return 'Could not add player $playerName'; } @override @@ -92,37 +92,33 @@ class AppLocalizationsEn extends AppLocalizations { String get create_statistic => 'Create statistic'; @override - String get create_statistic_classifier_subtitle => - 'Select which key metric you want to display'; + String get which_key_metric => 'Select which key metric you want to display'; @override - String get create_statistic_classifier_title => 'Classifier'; + String get classifier => 'Classifier'; @override - String get create_statistic_games_subtitle => 'Select the filtered games'; + String get select_the_filtered_games => 'Select the filtered games'; @override - String get create_statistic_games_title => 'Games'; + String get games => 'Games'; @override - String get create_statistic_groups_subtitle => 'Select the filtered groups'; + String get select_the_filtered_groups => 'Select the filtered groups'; @override - String get create_statistic_groups_title => 'Groups'; - - @override - String get create_statistic_scope_subtitle => + String get select_main_filter => 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; @override - String get create_statistic_scope_title => 'Scope'; + String get scope => 'Scope'; @override - String get create_statistic_timeframe_subtitle => + String get select_a_timeframe_for_which_data_will_be_filtered => 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; @override - String get create_statistic_timeframe_title => 'Timeframe'; + String get timeframe => 'Timeframe'; @override String get created_on => 'Created on'; @@ -164,6 +160,9 @@ class AppLocalizationsEn extends AppLocalizations { return 'If you delete this game template, $_temp0 using this game template will also be deleted.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Delete Group'; @@ -367,6 +366,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_statistics_available => 'No statistics available'; + @override + String get no_statistics_created_yet => 'No statistics created yet'; + @override String get none => 'None'; diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index 3c01f45..1d30afb 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -79,7 +79,7 @@ class _MatchViewState extends State { visible: matches.isNotEmpty, replacement: Center( child: TopCenteredMessage( - icon: Icons.report, + icon: Icons.info, title: loc.info, message: loc.no_matches_created_yet, ), diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart index 9ac03aa..92a01bb 100644 --- a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -68,7 +68,7 @@ class _CreateStatisticViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - loc.create_statistic_classifier_title, + loc.classifier, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -77,7 +77,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.create_statistic_classifier_subtitle, + loc.select_a_classifier, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -139,7 +139,7 @@ class _CreateStatisticViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - loc.create_statistic_scope_title, + loc.scope, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -148,7 +148,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.create_statistic_scope_subtitle, + loc.select_a_scope, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -214,7 +214,7 @@ class _CreateStatisticViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - loc.create_statistic_games_title, + loc.games, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -223,7 +223,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.create_statistic_games_subtitle, + loc.select_the_filtered_games, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -310,7 +310,7 @@ class _CreateStatisticViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - loc.create_statistic_groups_title, + loc.groups, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -319,7 +319,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.create_statistic_groups_subtitle, + loc.select_the_filtered_groups, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -396,7 +396,7 @@ class _CreateStatisticViewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - loc.create_statistic_timeframe_title, + loc.timeframe, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -405,7 +405,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.create_statistic_timeframe_subtitle, + loc.select_a_timeframe_for_which_data_will_be_filtered, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart new file mode 100644 index 0000000..4053cb3 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart @@ -0,0 +1,178 @@ +import 'package:flutter/material.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' + show + translateScopeToString, + translateStatisticTypeToString, + translateTimeframeToString; +import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; +import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +class StatisticDetailView extends StatefulWidget { + const StatisticDetailView({ + super.key, + required this.statistic, + required this.values, + required this.icon, + required this.barColor, + }); + + final Statistic statistic; + final List<(Player, num)> values; + final IconData icon; + final Color barColor; + + @override + State createState() => _StatisticDetailViewState(); +} + +class _StatisticDetailViewState extends State { + int displayCount = 0; + + @override + void initState() { + super.initState(); + displayCount = widget.statistic.displayCount; + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final title = translateStatisticTypeToString( + widget.statistic.type, + context, + ); + const style = TextStyle(fontWeight: FontWeight.bold); + + return Scaffold( + appBar: AppBar(title: Text(title)), + body: SingleChildScrollView( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + StatisticsTile( + icon: widget.icon, + title: title, + width: MediaQuery.sizeOf(context).width * 0.95, + values: widget.values, + barColor: widget.barColor, + selectedGroups: widget.statistic.selectedGroups, + selectedGames: widget.statistic.selectedGames, + ), + const SizedBox(height: 12), + + InfoTile( + icon: Icons.filter_alt, + title: loc.filter, + content: Column( + spacing: 12, + + children: [ + // Scopes + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.scope, style: style), + Text( + widget.statistic.scopes + .map( + (scope) => translateScopeToString(scope, context), + ) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Timeframe + if (widget.statistic.timeframe != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.timeframe, style: style), + Text( + translateTimeframeToString( + widget.statistic.timeframe!, + context, + ), + textAlign: TextAlign.end, + ), + ], + ), + + // Groups + if (widget.statistic.selectedGroups != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.groups, style: style), + Text( + widget.statistic.selectedGroups! + .map((group) => group.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Games + if (widget.statistic.selectedGames != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.games, style: style), + Text( + widget.statistic.selectedGames! + .map((game) => game.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + if (widget.values.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Display count', style: style), + Row( + children: [ + HapticIconButton( + icon: const Icon(Icons.remove), + onPressed: displayCount <= 1 + ? null + : () => setState(() => displayCount -= 1), + ), + SizedBox( + width: 30, + child: Text( + '$displayCount', + textAlign: TextAlign.center, + ), + ), + HapticIconButton( + icon: const Icon(Icons.add), + onPressed: displayCount >= widget.values.length + ? null + : () => setState(() => displayCount += 1), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart index fd43390..807358e 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/statistic.dart'; @@ -14,13 +16,18 @@ List _colorPalette = AppColor.values .map((c) => getColorFromAppColor(c)) .toList(); -/// Build the [StatisticsTile] for a given [Statistic]. -Widget buildStatisticTile({ +/// Returns the icon for the given statistic type. +IconData getStatisticIconForType(StatisticType type) => + _getStatisticIcon(type: type); + +/// Returns a color from the palette based on the statistic's ID. +Color getStatisticColorForStatistic(Statistic stat) => _getStatisticColor(stat); + +/// Computes the statistic values for a given [Statistic]. +List<(Player, num)> computeStatisticValues({ required Statistic statistic, required List matches, required List players, - required BuildContext context, - double? width, }) { final filteredMatches = _getFilterMatches(statistic, matches); final filteredPlayers = _getFilteredPlayers( @@ -29,16 +36,26 @@ Widget buildStatisticTile({ filteredMatches, ); - print('Building tile for statistic: $statistic'); - print('Filtered matches count: ${filteredMatches.length}'); - print('Filtered players count: ${filteredPlayers.length}'); - - final values = _computeValuesForType( + return _computeValuesForType( type: statistic.type, matches: filteredMatches, players: filteredPlayers, ); - print(values); +} + +/// Build the [StatisticsTile] for a given [Statistic]. +Widget buildStatisticTile({ + required Statistic statistic, + required List matches, + required List players, + required BuildContext context, + double? width, +}) { + final values = computeStatisticValues( + statistic: statistic, + matches: matches, + players: players, + ); return StatisticsTile( icon: _getStatisticIcon(type: statistic.type), @@ -46,7 +63,9 @@ Widget buildStatisticTile({ width: width ?? MediaQuery.sizeOf(context).width * 0.95, values: values, barColor: _getStatisticColor(statistic), - statistic: statistic, + displayCount: statistic.displayCount, + selectedGroups: statistic.selectedGroups, + selectedGames: statistic.selectedGames, ); } @@ -296,10 +315,8 @@ Widget buildSkeletonStatisticTile({required BuildContext context}) { width: MediaQuery.sizeOf(context).width * 0.95, values: values, barColor: _colorPalette[Random().nextInt(_colorPalette.length)], - statistic: Statistic( - type: StatisticType.totalMatches, - scopes: [StatisticScope.allPlayers], - timeframe: Timeframe.last7Days, - ), + selectedGames: [Game(name: 'Game 1', ruleset: Ruleset.highestScore)], + selectedGroups: [Group(name: 'Group 1', members: [])], + displayCount: 5, ); } diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart index 8c3ac16..d981b0a 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -8,9 +8,11 @@ import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_detail_view.dart'; import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart'; import 'package:tallee/presentation/widgets/app_skeleton.dart'; import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; +import 'package:tallee/presentation/widgets/top_centered_message.dart'; class StatisticsView extends StatefulWidget { /// A view that displays player statistics @@ -45,18 +47,30 @@ class _StatisticsViewState extends State { alignment: AlignmentDirectional.bottomCenter, fit: StackFit.expand, children: [ - SingleChildScrollView( - child: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: Column( - spacing: 12, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ...statisticTiles, - SizedBox(height: MediaQuery.paddingOf(context).bottom + 80), - ], + Visibility( + visible: statisticTiles.isNotEmpty, + replacement: Center( + child: TopCenteredMessage( + icon: Icons.info, + title: loc.info, + message: loc.no_statistics_created_yet, + ), + ), + child: SingleChildScrollView( + child: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: Column( + spacing: 12, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...statisticTiles, + SizedBox( + height: MediaQuery.paddingOf(context).bottom + 80, + ), + ], + ), ), ), ), @@ -75,12 +89,7 @@ class _StatisticsViewState extends State { ), ); if (!context.mounted) return; - final newTile = buildStatisticTile( - statistic: newStatistic, - matches: _allMatches, - players: _allPlayers, - context: context, - ); + final newTile = _buildStatisticTile(context, newStatistic); setState(() { statisticTiles.add(newTile); @@ -126,15 +135,40 @@ class _StatisticsViewState extends State { setState(() { statisticTiles = [ for (final statistic in statistics) ...[ - buildStatisticTile( - statistic: statistic, - matches: _allMatches, - players: _allPlayers, - context: context, - ), + _buildStatisticTile(context, statistic), ], ]; isLoading = false; }); } + + Widget _buildStatisticTile(BuildContext context, Statistic statistic) { + return GestureDetector( + onTap: () { + final values = computeStatisticValues( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + ); + + Navigator.push( + context, + adaptivePageRoute( + builder: (context) => StatisticDetailView( + statistic: statistic, + values: values, + icon: getStatisticIconForType(statistic.type), + barColor: getStatisticColorForStatistic(statistic), + ), + ), + ); + }, + child: buildStatisticTile( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + context: context, + ), + ); + } } diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index 24d51fc..f6db2a1 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -8,7 +8,6 @@ import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; @@ -27,7 +26,9 @@ class StatisticsTile extends StatelessWidget { required this.width, required this.values, required this.barColor, - required this.statistic, + this.displayCount, + this.selectedGroups, + this.selectedGames, }); /// The icon displayed next to the title. @@ -45,7 +46,10 @@ class StatisticsTile extends StatelessWidget { /// The color of the bars representing the values. final Color barColor; - final Statistic statistic; + final int? displayCount; + + final List? selectedGroups; + final List? selectedGames; @override Widget build(BuildContext context) { @@ -70,8 +74,12 @@ class StatisticsTile extends StatelessWidget { child: LayoutBuilder( builder: (context, constraints) { final maxBarWidth = constraints.maxWidth * 0.8; - final displayCount = min(values.length, statistic.displayCount); - final displayValues = values.take(displayCount).toList(); + + // If displayCount wasnt provided, take all values + final valuesShown = displayCount == null + ? values.length + : min(values.length, displayCount!); + final displayValues = values.take(valuesShown).toList(); final maxVal = displayValues.isNotEmpty ? displayValues.fold( 0, @@ -83,7 +91,7 @@ class StatisticsTile extends StatelessWidget { return Column( children: [ // Bars - ...List.generate(displayCount, (index) { + ...List.generate(valuesShown, (index) { /// Fraction of wins final double fraction = (maxVal > 0) ? (displayValues[index].$2 / maxVal) @@ -187,12 +195,14 @@ class StatisticsTile extends StatelessWidget { }), // Group & Game info - if (statistic.selectedGames != null || - statistic.selectedGroups != null) + if (hasGame || hasGroup) Padding( padding: const EdgeInsets.only(top: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, + child: Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + runSpacing: 4, children: [ // Game if (hasGroup) @@ -205,7 +215,7 @@ class StatisticsTile extends StatelessWidget { size: 20, ), Text( - getGameText(statistic.selectedGames!), + getGameText(selectedGames!), style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -227,7 +237,7 @@ class StatisticsTile extends StatelessWidget { color: CustomTheme.hintColor, ), Text( - getGroupText(statistic.selectedGroups!), + getGroupText(selectedGroups!), style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -265,9 +275,7 @@ class StatisticsTile extends StatelessWidget { return text; } - bool get hasGroup => - statistic.selectedGroups != null && statistic.selectedGroups!.isNotEmpty; + bool get hasGroup => selectedGroups != null && selectedGroups!.isNotEmpty; - bool get hasGame => - statistic.selectedGames != null && statistic.selectedGames!.isNotEmpty; + bool get hasGame => selectedGames != null && selectedGames!.isNotEmpty; } diff --git a/pubspec.yaml b/pubspec.yaml index d6fd994..b8b0aa8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+276 +version: 0.0.33+280 environment: sdk: ^3.8.1 -- 2.49.1 From efd1097d5a78265fe933c5d973dafd231e6ce43c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 12:51:24 +0200 Subject: [PATCH 17/39] feat: changing display count --- lib/data/models/statistic.dart | 19 +++++++ 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 ++ .../statistic_detail_view.dart | 27 +++++++--- .../statistics_view/statistics_view.dart | 54 ++++++++++++------- pubspec.yaml | 2 +- 9 files changed, 88 insertions(+), 28 deletions(-) diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart index 4fdbb05..e995d93 100644 --- a/lib/data/models/statistic.dart +++ b/lib/data/models/statistic.dart @@ -26,4 +26,23 @@ class Statistic { String toString() { return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)'; } + + Statistic copyWith({ + StatisticType? type, + List? scopes, + Timeframe? timeframe, + List? selectedGroups, + List? selectedGames, + int? displayCount, + }) { + return Statistic( + id: id, + type: type ?? this.type, + scopes: scopes ?? this.scopes, + timeframe: timeframe ?? this.timeframe, + selectedGroups: selectedGroups ?? this.selectedGroups, + selectedGames: selectedGames ?? this.selectedGames, + displayCount: displayCount ?? this.displayCount, + ); + } } diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index b6ae56b..aef31dc 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -19,6 +19,7 @@ "color_red": "Rot", "color_teal": "Türkis", "color_yellow": "Gelb", + "displayed_entries": "Angezeigte Einträge", "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", "@could_not_add_player": { diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index baf5006..902caad 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -18,6 +18,7 @@ "color_purple": "Purple", "color_red": "Red", "color_teal": "Teal", + "displayed_entries": "Displayed entries", "color_yellow": "Yellow", "confirm": "Confirm", "could_not_add_player": "Could not add player {playerName}", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 20067d4..37d0763 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -206,6 +206,12 @@ abstract class AppLocalizations { /// **'Teal'** String get color_teal; + /// No description provided for @displayed_entries. + /// + /// In en, this message translates to: + /// **'Displayed entries'** + String get displayed_entries; + /// No description provided for @color_yellow. /// /// 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 8152185..9457988 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -62,6 +62,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get color_teal => 'Türkis'; + @override + String get displayed_entries => 'Angezeigte Einträge'; + @override String get color_yellow => 'Gelb'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 2366ce2..99ecca3 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -62,6 +62,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get color_teal => 'Teal'; + @override + String get displayed_entries => 'Displayed entries'; + @override String get color_yellow => 'Yellow'; diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart index 4053cb3..06d93a4 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart @@ -1,12 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/statistic.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' - show - translateScopeToString, - translateStatisticTypeToString, - translateTimeframeToString; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; @@ -30,7 +28,7 @@ class StatisticDetailView extends StatefulWidget { } class _StatisticDetailViewState extends State { - int displayCount = 0; + late int displayCount; @override void initState() { @@ -48,7 +46,13 @@ class _StatisticDetailViewState extends State { const style = TextStyle(fontWeight: FontWeight.bold); return Scaffold( - appBar: AppBar(title: Text(title)), + appBar: AppBar( + title: Text(title), + leading: HapticIconButton( + icon: const Icon(Icons.arrow_back_ios_new), + onPressed: () => handleBack(context), + ), + ), body: SingleChildScrollView( padding: const EdgeInsets.all(12.0), child: Column( @@ -141,7 +145,7 @@ class _StatisticDetailViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('Display count', style: style), + Text(loc.displayed_entries, style: style), Row( children: [ HapticIconButton( @@ -175,4 +179,11 @@ class _StatisticDetailViewState extends State { ), ); } + + // Handles saving the display count and giving it to statistics view + Future handleBack(BuildContext context) async { + final db = Provider.of(context, listen: false); + await db.statisticDao.updateDisplayCount(widget.statistic.id, displayCount); + if (context.mounted) Navigator.of(context).pop(displayCount); + } } diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart index d981b0a..3381267 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -26,6 +26,7 @@ class _StatisticsViewState extends State { bool isLoading = true; List _allMatches = const []; List _allPlayers = const []; + List _statistics = const []; List statisticTiles = []; @override @@ -34,7 +35,7 @@ class _StatisticsViewState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; - getStatisticTiles(context); + loadStatistics(context); }); } @@ -84,15 +85,16 @@ class _StatisticsViewState extends State { context, adaptivePageRoute( builder: (context) => CreateStatisticView( - onStatisticCreated: () => getStatisticTiles(context), + onStatisticCreated: () => loadStatistics(context), ), ), ); if (!context.mounted) return; - final newTile = _buildStatisticTile(context, newStatistic); - setState(() { - statisticTiles.add(newTile); + _statistics = [..._statistics, newStatistic]; + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); }); }, ), @@ -103,7 +105,7 @@ class _StatisticsViewState extends State { ); } - Future getStatisticTiles(BuildContext context) async { + Future loadStatistics(BuildContext context) async { setState(() { isLoading = true; statisticTiles = List.generate( @@ -131,27 +133,26 @@ class _StatisticsViewState extends State { final statistics = results[0] as List; _allMatches = results[1] as List; _allPlayers = results[2] as List; + _statistics = statistics; setState(() { - statisticTiles = [ - for (final statistic in statistics) ...[ - _buildStatisticTile(context, statistic), - ], - ]; + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); isLoading = false; }); } Widget _buildStatisticTile(BuildContext context, Statistic statistic) { - return GestureDetector( - onTap: () { - final values = computeStatisticValues( - statistic: statistic, - matches: _allMatches, - players: _allPlayers, - ); + final values = computeStatisticValues( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + ); - Navigator.push( + return GestureDetector( + onTap: () async { + final newDisplayCount = await Navigator.push( context, adaptivePageRoute( builder: (context) => StatisticDetailView( @@ -162,6 +163,21 @@ class _StatisticsViewState extends State { ), ), ); + if (newDisplayCount != null && + newDisplayCount != statistic.displayCount) { + setState(() { + _statistics = _statistics + .map( + (stat) => stat.id == statistic.id + ? stat.copyWith(displayCount: newDisplayCount) + : stat, + ) + .toList(); + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + }); + } }, child: buildStatisticTile( statistic: statistic, diff --git a/pubspec.yaml b/pubspec.yaml index b8b0aa8..6c5f4d6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+280 +version: 0.0.33+281 environment: sdk: ^3.8.1 -- 2.49.1 From b9710ed851747004cd33a7925f63cb58cb0eebd2 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 12:55:36 +0200 Subject: [PATCH 18/39] fix: pixel overflow --- lib/presentation/widgets/game_label.dart | 56 +++++++++++++----------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index dd179d6..2e6bf74 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -21,32 +21,35 @@ class GameLabel extends StatelessWidget { ? Colors.black : Colors.white; - return IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Title - Container( - decoration: BoxDecoration( - color: backgroundColor.withAlpha(230), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - bottomLeft: Radius.circular(8), - ), - ), - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - child: Text( - title, - style: TextStyle( - fontSize: 12, - color: fontColor, - fontWeight: FontWeight.bold, - ), + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Title + Container( + decoration: BoxDecoration( + color: backgroundColor.withAlpha(230), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + bottomLeft: Radius.circular(8), ), ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: TextStyle( + fontSize: 12, + color: fontColor, + fontWeight: FontWeight.bold, + ), + ), + ), - // Description - Container( + // Description + Flexible( + child: Container( decoration: BoxDecoration( color: backgroundColor.withAlpha(140), borderRadius: const BorderRadius.only( @@ -57,6 +60,9 @@ class GameLabel extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Text( description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, style: TextStyle( fontSize: 12, color: fontColor, @@ -64,8 +70,8 @@ class GameLabel extends StatelessWidget { ), ), ), - ], - ), + ), + ], ); } } -- 2.49.1 From fb2f6d3adc8a3c306b171fffff9fbb38bd8dd479 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 13:06:11 +0200 Subject: [PATCH 19/39] feat: dynamic display count shown in tile --- .../statistic_detail_view.dart | 2 ++ .../widgets/tiles/statistics_tile.dart | 34 ++++++++++++------- test/db_tests/statistics/statistic_test.dart | 10 +++--- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart index 06d93a4..f1bc209 100644 --- a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart @@ -65,6 +65,8 @@ class _StatisticDetailViewState extends State { barColor: widget.barColor, selectedGroups: widget.statistic.selectedGroups, selectedGames: widget.statistic.selectedGames, + displayCount: displayCount, + showAllValues: true, ), const SizedBox(height: 12), diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index f6db2a1..02cdb75 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -26,9 +26,10 @@ class StatisticsTile extends StatelessWidget { required this.width, required this.values, required this.barColor, - this.displayCount, + required this.displayCount, this.selectedGroups, this.selectedGames, + this.showAllValues = false, }); /// The icon displayed next to the title. @@ -46,11 +47,13 @@ class StatisticsTile extends StatelessWidget { /// The color of the bars representing the values. final Color barColor; - final int? displayCount; - + // statistic data + final int displayCount; final List? selectedGroups; final List? selectedGames; + final bool showAllValues; + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -76,9 +79,9 @@ class StatisticsTile extends StatelessWidget { final maxBarWidth = constraints.maxWidth * 0.8; // If displayCount wasnt provided, take all values - final valuesShown = displayCount == null + final valuesShown = showAllValues ? values.length - : min(values.length, displayCount!); + : min(values.length, displayCount); final displayValues = values.take(valuesShown).toList(); final maxVal = displayValues.isNotEmpty ? displayValues.fold( @@ -103,6 +106,17 @@ class StatisticsTile extends StatelessWidget { maxBarWidth, ); + final barClr = index >= displayCount + ? barColor.withAlpha(150) + : barColor; + + var textClr = barColor.computeLuminance() > 0.5 + ? const Color(0xFF101010) + : CustomTheme.textColor; + textClr = textClr.withAlpha( + index >= displayCount ? 220 : 255, + ); + return Padding( padding: const EdgeInsets.symmetric(vertical: 2.0), child: Row( @@ -119,7 +133,7 @@ class StatisticsTile extends StatelessWidget { width: barWidth, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), - color: barColor, + color: barClr, ), ), @@ -138,13 +152,7 @@ class StatisticsTile extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: - barColor == - getColorFromAppColor( - AppColor.yellow, - ) - ? const Color(0xFF101010) - : CustomTheme.textColor, + color: textClr, ), ), TextSpan( diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart index cd08615..bd590c7 100644 --- a/test/db_tests/statistics/statistic_test.dart +++ b/test/db_tests/statistics/statistic_test.dart @@ -8,9 +8,7 @@ import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/group.dart'; -import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/score_entry.dart'; import 'package:tallee/data/models/statistic.dart'; void main() { @@ -23,10 +21,10 @@ void main() { late Group testGroup1; late Group testGroup2; late Game testGame; - late Match testMatch1; + /*late Match testMatch1; late Match testMatch2; late Match testMatchOnlyPlayers; - late Match testMatchOnlyGroup; + late Match testMatchOnlyGroup;*/ final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); final fakeClock = Clock(() => fixedDate); @@ -62,7 +60,7 @@ void main() { color: AppColor.blue, icon: '', ); - testMatch1 = Match( + /*testMatch1 = Match( name: 'First Test Match', game: testGame, group: testGroup1, @@ -85,7 +83,7 @@ void main() { game: testGame, group: testGroup2, players: testGroup2.members, - ); + );*/ }); await database.playerDao.addPlayersAsList( players: [ -- 2.49.1 From 730341dc7e5464b11fa14b427c110ec66b1c76ca Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 13:17:02 +0200 Subject: [PATCH 20/39] fix: localizations --- lib/l10n/arb/app_de.arb | 36 +++++--- lib/l10n/arb/app_en.arb | 35 ++++---- lib/l10n/generated/app_localizations.dart | 90 +++++++------------ lib/l10n/generated/app_localizations_de.dart | 62 +++++-------- lib/l10n/generated/app_localizations_en.dart | 46 ++++------ .../create_statistic_view.dart | 42 ++++----- 6 files changed, 129 insertions(+), 182 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index aef31dc..94ec9ac 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -2,14 +2,18 @@ "@@locale": "de", "all_players": "Alle Spieler:innen", "all_players_selected": "Alle Spieler:innen ausgewählt", + "all_time": "Gesamter Zeitraum", "amount_of_matches": "Anzahl der Spiele", "app_name": "Tallee", + "average_score": "Durchschnittliche Punktzahl", "best_player": "Beste:r Spieler:in", + "best_score": "Beste Punktzahl", "cancel": "Abbrechen", "choose_color": "Farbe wählen", "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", + "classifier": "Klassifikator", "color": "Farbe", "color_blue": "Blau", "color_green": "Grün", @@ -19,7 +23,6 @@ "color_red": "Rot", "color_teal": "Türkis", "color_yellow": "Gelb", - "displayed_entries": "Angezeigte Einträge", "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", "@could_not_add_player": { @@ -66,6 +69,7 @@ "delete_match": "Spiel löschen", "delete_player": "Spieler:in löschen", "description": "Beschreibung", + "displayed_entries": "Angezeigte Einträge", "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_game": "Spielvorlage bearbeiten", "edit_group": "Gruppe bearbeiten", @@ -82,9 +86,11 @@ "exit_view": "Ansicht verlassen", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", + "filter": "Filter", "format_exception": "Formatfehler (siehe Konsole)", "game": "Spielvorlage", "game_name": "Spielvorlagenname", + "games": "Spielvorlagen", "group": "Gruppe", "group_name": "Gruppenname", "group_profile": "Gruppenprofil", @@ -96,6 +102,11 @@ "import_data": "Daten importieren", "info": "Info", "invalid_schema": "Ungültiges Schema", + "last_180_days": "Letzte 180 Tage", + "last_30_days": "Letzte 30 Tage", + "last_7_days": "Letzte 7 Tage", + "last_90_days": "Letzte 90 Tage", + "last_year": "Letztes Jahr", "least_points": "Niedrigste Punkte", "legal": "Rechtliches", "legal_notice": "Impressum", @@ -128,6 +139,7 @@ "no_results_entered_yet": "Noch keine Ergebnisse eingetragen", "no_second_match_available": "Kein zweites Spiel verfügbar", "no_statistics_available": "Keine Statistiken verfügbar", + "no_statistics_created_yet": "Noch keine Statistiken erstellt", "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", @@ -152,6 +164,7 @@ "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.", "save_changes": "Änderungen speichern", + "scope": "Bereich", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", "select_a_classifier": "Klassifikator auswählen", @@ -159,18 +172,19 @@ "select_a_group": "Gruppe auswählen", "select_a_scope": "Bereich auswählen", "select_a_timeframe": "Zeitraum auswählen", + "select_a_timeframe_for_which_data_will_be_filtered": "Wähle einen Zeitraum, für den die Daten gefiltert werden sollen", "select_loser": "Verlierer:in wählen", + "select_the_filtered_games": "Wähle die gefilterten Spielvorlagen", + "select_the_filtered_groups": "Wähle die gefilterten Gruppen", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", + "selected_games": "Ausgewählte Spielvorlagen", + "selected_groups": "Ausgewählte Gruppen", "selected_players": "Ausgewählte Spieler:innen", "set_name": "Name setzen", "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", - "statistic_scope_all_players": "Alle Spieler:innen", - "statistic_scope_selected_games": "Ausgewählte Spielvorlagen", - "statistic_scope_selected_groups": "Ausgewählte Gruppen", - "statistic_scope_timeframe": "Zeitraum", "statistic_type_average_score": "Durchschnittliche Punktzahl", "statistic_type_best_score": "Beste Punktzahl", "statistic_type_total_losses": "Niederlagen insgesamt", @@ -186,18 +200,18 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", - "timeframe_all_time": "Gesamter Zeitraum", - "timeframe_last_180_days": "Letzte 180 Tage", - "timeframe_last_30_days": "Letzte 30 Tage", - "timeframe_last_7_days": "Letzte 7 Tage", - "timeframe_last_90_days": "Letzte 90 Tage", - "timeframe_last_year": "Letztes Jahr", + "timeframe": "Zeitraum", "today_at": "Heute um", + "total_losses": "Niederlagen insgesamt", + "total_matches": "Spiele insgesamt", + "total_score": "Punktzahl insgesamt", + "total_wins": "Siege insgesamt", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", "winner": "Gewinner:in", "winners": "Gewinner:innen", "winrate": "Siegquote", "wins": "Siege", + "worst_score": "Schlechteste Punktzahl", "yesterday_at": "Gestern um" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 902caad..427e955 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -35,12 +35,10 @@ "create_new_group": "Create new group", "create_new_match": "Create new match", "create_statistic": "Create statistic", - "which_key_metric": "Select which key metric you want to display", "classifier": "Classifier", "select_the_filtered_games": "Select the filtered games", "games": "Games", "select_the_filtered_groups": "Select the filtered groups", - "select_main_filter": "Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.", "scope": "Scope", "select_a_timeframe_for_which_data_will_be_filtered": "Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.", "timeframe": "Timeframe", @@ -169,18 +167,15 @@ "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", - "statistic_scope_all_players": "All players", - "statistic_scope_selected_games": "Selected games", - "statistic_scope_selected_groups": "Selected groups", - "statistic_scope_timeframe": "Timeframe", - "statistic_type_average_score": "Average score", - "statistic_type_best_score": "Best score", - "statistic_type_total_losses": "Total losses", - "statistic_type_total_matches": "Total matches", - "statistic_type_total_score": "Total score", - "statistic_type_total_wins": "Total wins", - "statistic_type_winrate": "Winrate", - "statistic_type_worst_score": "Worst score", + "selected_games": "Selected games", + "selected_groups": "Selected groups", + "average_score": "Average score", + "best_score": "Best score", + "total_losses": "Total losses", + "total_matches": "Total matches", + "total_score": "Total score", + "total_wins": "Total wins", + "worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -195,12 +190,12 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", - "timeframe_all_time": "All time", - "timeframe_last_180_days": "Last 180 days", - "timeframe_last_30_days": "Last 30 days", - "timeframe_last_7_days": "Last 7 days", - "timeframe_last_90_days": "Last 90 days", - "timeframe_last_year": "Last year", + "all_time": "All time", + "last_180_days": "Last 180 days", + "last_30_days": "Last 30 days", + "last_7_days": "Last 7 days", + "last_90_days": "Last 90 days", + "last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 37d0763..41d1603 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -266,12 +266,6 @@ abstract class AppLocalizations { /// **'Create statistic'** String get create_statistic; - /// No description provided for @which_key_metric. - /// - /// In en, this message translates to: - /// **'Select which key metric you want to display'** - String get which_key_metric; - /// No description provided for @classifier. /// /// In en, this message translates to: @@ -296,12 +290,6 @@ abstract class AppLocalizations { /// **'Select the filtered groups'** String get select_the_filtered_groups; - /// No description provided for @select_main_filter. - /// - /// In en, this message translates to: - /// **'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'** - String get select_main_filter; - /// No description provided for @scope. /// /// In en, this message translates to: @@ -1028,77 +1016,59 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; - /// No description provided for @statistic_scope_all_players. - /// - /// In en, this message translates to: - /// **'All players'** - String get statistic_scope_all_players; - - /// No description provided for @statistic_scope_selected_games. + /// No description provided for @selected_games. /// /// In en, this message translates to: /// **'Selected games'** - String get statistic_scope_selected_games; + String get selected_games; - /// No description provided for @statistic_scope_selected_groups. + /// No description provided for @selected_groups. /// /// In en, this message translates to: /// **'Selected groups'** - String get statistic_scope_selected_groups; + String get selected_groups; - /// No description provided for @statistic_scope_timeframe. - /// - /// In en, this message translates to: - /// **'Timeframe'** - String get statistic_scope_timeframe; - - /// No description provided for @statistic_type_average_score. + /// No description provided for @average_score. /// /// In en, this message translates to: /// **'Average score'** - String get statistic_type_average_score; + String get average_score; - /// No description provided for @statistic_type_best_score. + /// No description provided for @best_score. /// /// In en, this message translates to: /// **'Best score'** - String get statistic_type_best_score; + String get best_score; - /// No description provided for @statistic_type_total_losses. + /// No description provided for @total_losses. /// /// In en, this message translates to: /// **'Total losses'** - String get statistic_type_total_losses; + String get total_losses; - /// No description provided for @statistic_type_total_matches. + /// No description provided for @total_matches. /// /// In en, this message translates to: /// **'Total matches'** - String get statistic_type_total_matches; + String get total_matches; - /// No description provided for @statistic_type_total_score. + /// No description provided for @total_score. /// /// In en, this message translates to: /// **'Total score'** - String get statistic_type_total_score; + String get total_score; - /// No description provided for @statistic_type_total_wins. + /// No description provided for @total_wins. /// /// In en, this message translates to: /// **'Total wins'** - String get statistic_type_total_wins; + String get total_wins; - /// No description provided for @statistic_type_winrate. - /// - /// In en, this message translates to: - /// **'Winrate'** - String get statistic_type_winrate; - - /// No description provided for @statistic_type_worst_score. + /// No description provided for @worst_score. /// /// In en, this message translates to: /// **'Worst score'** - String get statistic_type_worst_score; + String get worst_score; /// Success message when adding a player /// @@ -1130,41 +1100,41 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; - /// No description provided for @timeframe_all_time. + /// No description provided for @all_time. /// /// In en, this message translates to: /// **'All time'** - String get timeframe_all_time; + String get all_time; - /// No description provided for @timeframe_last_180_days. + /// No description provided for @last_180_days. /// /// In en, this message translates to: /// **'Last 180 days'** - String get timeframe_last_180_days; + String get last_180_days; - /// No description provided for @timeframe_last_30_days. + /// No description provided for @last_30_days. /// /// In en, this message translates to: /// **'Last 30 days'** - String get timeframe_last_30_days; + String get last_30_days; - /// No description provided for @timeframe_last_7_days. + /// No description provided for @last_7_days. /// /// In en, this message translates to: /// **'Last 7 days'** - String get timeframe_last_7_days; + String get last_7_days; - /// No description provided for @timeframe_last_90_days. + /// No description provided for @last_90_days. /// /// In en, this message translates to: /// **'Last 90 days'** - String get timeframe_last_90_days; + String get last_90_days; - /// No description provided for @timeframe_last_year. + /// No description provided for @last_year. /// /// In en, this message translates to: /// **'Last year'** - String get timeframe_last_year; + String get last_year; /// No description provided for @today_at. /// diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 9457988..35984a7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -95,33 +95,26 @@ class AppLocalizationsDe extends AppLocalizations { String get create_statistic => 'Statistik erstellen'; @override - String get which_key_metric => 'Select which key metric you want to display'; + String get classifier => 'Klassifikator'; @override - String get classifier => 'Classifier'; + String get select_the_filtered_games => 'Wähle die gefilterten Spielvorlagen'; @override - String get select_the_filtered_games => 'Select the filtered games'; + String get games => 'Spielvorlagen'; @override - String get games => 'Games'; + String get select_the_filtered_groups => 'Wähle die gefilterten Gruppen'; @override - String get select_the_filtered_groups => 'Select the filtered groups'; - - @override - String get select_main_filter => - 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; - - @override - String get scope => 'Scope'; + String get scope => 'Bereich'; @override String get select_a_timeframe_for_which_data_will_be_filtered => - 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; + 'Wähle einen Zeitraum, für den die Daten gefiltert werden sollen'; @override - String get timeframe => 'Timeframe'; + String get timeframe => 'Zeitraum'; @override String get created_on => 'Erstellt am'; @@ -370,7 +363,7 @@ class AppLocalizationsDe extends AppLocalizations { String get no_statistics_available => 'Keine Statistiken verfügbar'; @override - String get no_statistics_created_yet => 'No statistics created yet'; + String get no_statistics_created_yet => 'Noch keine Statistiken erstellt'; @override String get none => 'Kein'; @@ -498,40 +491,31 @@ class AppLocalizationsDe extends AppLocalizations { String get stats => 'Statistiken'; @override - String get statistic_scope_all_players => 'Alle Spieler:innen'; + String get selected_games => 'Ausgewählte Spielvorlagen'; @override - String get statistic_scope_selected_games => 'Ausgewählte Spielvorlagen'; + String get selected_groups => 'Ausgewählte Gruppen'; @override - String get statistic_scope_selected_groups => 'Ausgewählte Gruppen'; + String get average_score => 'Durchschnittliche Punktzahl'; @override - String get statistic_scope_timeframe => 'Zeitraum'; + String get best_score => 'Beste Punktzahl'; @override - String get statistic_type_average_score => 'Durchschnittliche Punktzahl'; + String get total_losses => 'Niederlagen insgesamt'; @override - String get statistic_type_best_score => 'Beste Punktzahl'; + String get total_matches => 'Spiele insgesamt'; @override - String get statistic_type_total_losses => 'Niederlagen insgesamt'; + String get total_score => 'Punktzahl insgesamt'; @override - String get statistic_type_total_matches => 'Spiele insgesamt'; + String get total_wins => 'Siege insgesamt'; @override - String get statistic_type_total_score => 'Punktzahl insgesamt'; - - @override - String get statistic_type_total_wins => 'Siege insgesamt'; - - @override - String get statistic_type_winrate => 'Siegquote'; - - @override - String get statistic_type_worst_score => 'Schlechteste Punktzahl'; + String get worst_score => 'Schlechteste Punktzahl'; @override String successfully_added_player(String playerName) { @@ -554,22 +538,22 @@ class AppLocalizationsDe extends AppLocalizations { String get tie => 'Unentschieden'; @override - String get timeframe_all_time => 'Gesamter Zeitraum'; + String get all_time => 'Gesamter Zeitraum'; @override - String get timeframe_last_180_days => 'Letzte 180 Tage'; + String get last_180_days => 'Letzte 180 Tage'; @override - String get timeframe_last_30_days => 'Letzte 30 Tage'; + String get last_30_days => 'Letzte 30 Tage'; @override - String get timeframe_last_7_days => 'Letzte 7 Tage'; + String get last_7_days => 'Letzte 7 Tage'; @override - String get timeframe_last_90_days => 'Letzte 90 Tage'; + String get last_90_days => 'Letzte 90 Tage'; @override - String get timeframe_last_year => 'Letztes Jahr'; + String get last_year => 'Letztes Jahr'; @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 99ecca3..8f7236a 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -94,9 +94,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_statistic => 'Create statistic'; - @override - String get which_key_metric => 'Select which key metric you want to display'; - @override String get classifier => 'Classifier'; @@ -109,10 +106,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get select_the_filtered_groups => 'Select the filtered groups'; - @override - String get select_main_filter => - 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; - @override String get scope => 'Scope'; @@ -498,40 +491,31 @@ class AppLocalizationsEn extends AppLocalizations { String get stats => 'Stats'; @override - String get statistic_scope_all_players => 'All players'; + String get selected_games => 'Selected games'; @override - String get statistic_scope_selected_games => 'Selected games'; + String get selected_groups => 'Selected groups'; @override - String get statistic_scope_selected_groups => 'Selected groups'; + String get average_score => 'Average score'; @override - String get statistic_scope_timeframe => 'Timeframe'; + String get best_score => 'Best score'; @override - String get statistic_type_average_score => 'Average score'; + String get total_losses => 'Total losses'; @override - String get statistic_type_best_score => 'Best score'; + String get total_matches => 'Total matches'; @override - String get statistic_type_total_losses => 'Total losses'; + String get total_score => 'Total score'; @override - String get statistic_type_total_matches => 'Total matches'; + String get total_wins => 'Total wins'; @override - String get statistic_type_total_score => 'Total score'; - - @override - String get statistic_type_total_wins => 'Total wins'; - - @override - String get statistic_type_winrate => 'Winrate'; - - @override - String get statistic_type_worst_score => 'Worst score'; + String get worst_score => 'Worst score'; @override String successfully_added_player(String playerName) { @@ -553,22 +537,22 @@ class AppLocalizationsEn extends AppLocalizations { String get tie => 'Tie'; @override - String get timeframe_all_time => 'All time'; + String get all_time => 'All time'; @override - String get timeframe_last_180_days => 'Last 180 days'; + String get last_180_days => 'Last 180 days'; @override - String get timeframe_last_30_days => 'Last 30 days'; + String get last_30_days => 'Last 30 days'; @override - String get timeframe_last_7_days => 'Last 7 days'; + String get last_7_days => 'Last 7 days'; @override - String get timeframe_last_90_days => 'Last 90 days'; + String get last_90_days => 'Last 90 days'; @override - String get timeframe_last_year => 'Last year'; + String get last_year => 'Last year'; @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart index 92a01bb..00542cf 100644 --- a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -76,10 +76,10 @@ class _CreateStatisticViewState extends State { fontWeight: FontWeight.bold, ), ), - Text( - loc.select_a_classifier, + const Text( + 'description', textAlign: TextAlign.start, - style: const TextStyle( + style: TextStyle( color: CustomTheme.textColor, fontSize: 12, ), @@ -581,17 +581,17 @@ String translateTimeframeToString(Timeframe timeframe, BuildContext context) { final loc = AppLocalizations.of(context); switch (timeframe) { case Timeframe.last7Days: - return loc.timeframe_last_7_days; + return loc.last_7_days; case Timeframe.last30Days: - return loc.timeframe_last_30_days; + return loc.last_30_days; case Timeframe.last90Days: - return loc.timeframe_last_90_days; + return loc.last_90_days; case Timeframe.last180Days: - return loc.timeframe_last_180_days; + return loc.last_180_days; case Timeframe.lastYear: - return loc.timeframe_last_year; + return loc.last_year; case Timeframe.allTime: - return loc.timeframe_all_time; + return loc.all_time; } } @@ -599,13 +599,13 @@ String translateScopeToString(StatisticScope scope, BuildContext context) { final loc = AppLocalizations.of(context); switch (scope) { case StatisticScope.allPlayers: - return loc.statistic_scope_all_players; + return loc.all_players; case StatisticScope.selectedGroups: - return loc.statistic_scope_selected_groups; + return loc.selected_groups; case StatisticScope.selectedGames: - return loc.statistic_scope_selected_games; + return loc.selected_games; case StatisticScope.timeframe: - return loc.statistic_scope_timeframe; + return loc.timeframe; } } @@ -616,20 +616,20 @@ String translateStatisticTypeToString( final loc = AppLocalizations.of(context); switch (type) { case StatisticType.totalMatches: - return loc.statistic_type_total_matches; + return loc.total_matches; case StatisticType.totalWins: - return loc.statistic_type_total_wins; + return loc.total_wins; case StatisticType.totalScore: - return loc.statistic_type_total_score; + return loc.total_score; case StatisticType.totalLosses: - return loc.statistic_type_total_losses; + return loc.total_losses; case StatisticType.averageScore: - return loc.statistic_type_average_score; + return loc.average_score; case StatisticType.bestScore: - return loc.statistic_type_best_score; + return loc.best_score; case StatisticType.worstScore: - return loc.statistic_type_worst_score; + return loc.worst_score; case StatisticType.winrate: - return loc.statistic_type_winrate; + return loc.winrate; } } -- 2.49.1 From 4bd2f972df26cdd036f33a4064900e24a8b3b247 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 13:17:02 +0200 Subject: [PATCH 21/39] fix: localizations --- lib/l10n/arb/app_de.arb | 36 ++++-- lib/l10n/arb/app_en.arb | 41 +++---- lib/l10n/generated/app_localizations.dart | 106 +++++++----------- lib/l10n/generated/app_localizations_de.dart | 66 +++++------ lib/l10n/generated/app_localizations_en.dart | 60 ++++------ .../create_statistic_view.dart | 50 ++++----- 6 files changed, 155 insertions(+), 204 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index aef31dc..4eb5fd1 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -2,14 +2,18 @@ "@@locale": "de", "all_players": "Alle Spieler:innen", "all_players_selected": "Alle Spieler:innen ausgewählt", + "all_time": "Gesamter Zeitraum", "amount_of_matches": "Anzahl der Spiele", "app_name": "Tallee", + "average_score": "Durchschnittliche Punktzahl", "best_player": "Beste:r Spieler:in", + "best_score": "Beste Punktzahl", "cancel": "Abbrechen", "choose_color": "Farbe wählen", "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", + "classifier": "Klassifikator", "color": "Farbe", "color_blue": "Blau", "color_green": "Grün", @@ -19,7 +23,6 @@ "color_red": "Rot", "color_teal": "Türkis", "color_yellow": "Gelb", - "displayed_entries": "Angezeigte Einträge", "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", "@could_not_add_player": { @@ -66,6 +69,7 @@ "delete_match": "Spiel löschen", "delete_player": "Spieler:in löschen", "description": "Beschreibung", + "displayed_entries": "Angezeigte Einträge", "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_game": "Spielvorlage bearbeiten", "edit_group": "Gruppe bearbeiten", @@ -82,9 +86,11 @@ "exit_view": "Ansicht verlassen", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", + "filter": "Filter", "format_exception": "Formatfehler (siehe Konsole)", "game": "Spielvorlage", "game_name": "Spielvorlagenname", + "games": "Spielvorlagen", "group": "Gruppe", "group_name": "Gruppenname", "group_profile": "Gruppenprofil", @@ -96,6 +102,11 @@ "import_data": "Daten importieren", "info": "Info", "invalid_schema": "Ungültiges Schema", + "last_180_days": "Letzte 180 Tage", + "last_30_days": "Letzte 30 Tage", + "last_7_days": "Letzte 7 Tage", + "last_90_days": "Letzte 90 Tage", + "last_year": "Letztes Jahr", "least_points": "Niedrigste Punkte", "legal": "Rechtliches", "legal_notice": "Impressum", @@ -128,6 +139,7 @@ "no_results_entered_yet": "Noch keine Ergebnisse eingetragen", "no_second_match_available": "Kein zweites Spiel verfügbar", "no_statistics_available": "Keine Statistiken verfügbar", + "no_statistics_created_yet": "Noch keine Statistiken erstellt", "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", @@ -152,6 +164,7 @@ "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.", "save_changes": "Änderungen speichern", + "scope": "Bereich", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", "select_a_classifier": "Klassifikator auswählen", @@ -160,17 +173,18 @@ "select_a_scope": "Bereich auswählen", "select_a_timeframe": "Zeitraum auswählen", "select_loser": "Verlierer:in wählen", + "select_the_filtered_games": "Wähle Spiele, nach denen gefiltert werden soll.", + "select_the_filtered_groups": "Wähle Gruppen, nach denen gefiltert werden soll.", + "select_the_filtered_timeframe": "Wähle einen Zeitraum, nach dem gefiltert werden soll.", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", + "selected_games": "Ausgewählte Spielvorlagen", + "selected_groups": "Ausgewählte Gruppen", "selected_players": "Ausgewählte Spieler:innen", "set_name": "Name setzen", "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", - "statistic_scope_all_players": "Alle Spieler:innen", - "statistic_scope_selected_games": "Ausgewählte Spielvorlagen", - "statistic_scope_selected_groups": "Ausgewählte Gruppen", - "statistic_scope_timeframe": "Zeitraum", "statistic_type_average_score": "Durchschnittliche Punktzahl", "statistic_type_best_score": "Beste Punktzahl", "statistic_type_total_losses": "Niederlagen insgesamt", @@ -186,18 +200,18 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", - "timeframe_all_time": "Gesamter Zeitraum", - "timeframe_last_180_days": "Letzte 180 Tage", - "timeframe_last_30_days": "Letzte 30 Tage", - "timeframe_last_7_days": "Letzte 7 Tage", - "timeframe_last_90_days": "Letzte 90 Tage", - "timeframe_last_year": "Letztes Jahr", + "timeframe": "Zeitraum", "today_at": "Heute um", + "total_losses": "Niederlagen insgesamt", + "total_matches": "Spiele insgesamt", + "total_score": "Punktzahl insgesamt", + "total_wins": "Siege insgesamt", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", "winner": "Gewinner:in", "winners": "Gewinner:innen", "winrate": "Siegquote", "wins": "Siege", + "worst_score": "Schlechteste Punktzahl", "yesterday_at": "Gestern um" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 902caad..988433b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -35,14 +35,12 @@ "create_new_group": "Create new group", "create_new_match": "Create new match", "create_statistic": "Create statistic", - "which_key_metric": "Select which key metric you want to display", "classifier": "Classifier", - "select_the_filtered_games": "Select the filtered games", + "select_the_filtered_timeframe": "Select the timeframe you want to filter by.", + "select_the_filtered_games": "Select the games you want to filter by.", "games": "Games", - "select_the_filtered_groups": "Select the filtered groups", - "select_main_filter": "Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.", + "select_the_filtered_groups": "Select the groups you want to filter by.", "scope": "Scope", - "select_a_timeframe_for_which_data_will_be_filtered": "Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.", "timeframe": "Timeframe", "created_on": "Created on", "data": "Data", @@ -169,18 +167,15 @@ "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", - "statistic_scope_all_players": "All players", - "statistic_scope_selected_games": "Selected games", - "statistic_scope_selected_groups": "Selected groups", - "statistic_scope_timeframe": "Timeframe", - "statistic_type_average_score": "Average score", - "statistic_type_best_score": "Best score", - "statistic_type_total_losses": "Total losses", - "statistic_type_total_matches": "Total matches", - "statistic_type_total_score": "Total score", - "statistic_type_total_wins": "Total wins", - "statistic_type_winrate": "Winrate", - "statistic_type_worst_score": "Worst score", + "selected_games": "Selected games", + "selected_groups": "Selected groups", + "average_score": "Average score", + "best_score": "Best score", + "total_losses": "Total losses", + "total_matches": "Total matches", + "total_score": "Total score", + "total_wins": "Total wins", + "worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -195,12 +190,12 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", - "timeframe_all_time": "All time", - "timeframe_last_180_days": "Last 180 days", - "timeframe_last_30_days": "Last 30 days", - "timeframe_last_7_days": "Last 7 days", - "timeframe_last_90_days": "Last 90 days", - "timeframe_last_year": "Last year", + "all_time": "All time", + "last_180_days": "Last 180 days", + "last_30_days": "Last 30 days", + "last_7_days": "Last 7 days", + "last_90_days": "Last 90 days", + "last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 37d0763..30e1b33 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -266,22 +266,22 @@ abstract class AppLocalizations { /// **'Create statistic'** String get create_statistic; - /// No description provided for @which_key_metric. - /// - /// In en, this message translates to: - /// **'Select which key metric you want to display'** - String get which_key_metric; - /// No description provided for @classifier. /// /// In en, this message translates to: /// **'Classifier'** String get classifier; + /// No description provided for @select_the_filtered_timeframe. + /// + /// In en, this message translates to: + /// **'Select the timeframe you want to filter by.'** + String get select_the_filtered_timeframe; + /// No description provided for @select_the_filtered_games. /// /// In en, this message translates to: - /// **'Select the filtered games'** + /// **'Select the games you want to filter by.'** String get select_the_filtered_games; /// No description provided for @games. @@ -293,27 +293,15 @@ abstract class AppLocalizations { /// No description provided for @select_the_filtered_groups. /// /// In en, this message translates to: - /// **'Select the filtered groups'** + /// **'Select the groups you want to filter by.'** String get select_the_filtered_groups; - /// No description provided for @select_main_filter. - /// - /// In en, this message translates to: - /// **'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'** - String get select_main_filter; - /// No description provided for @scope. /// /// In en, this message translates to: /// **'Scope'** String get scope; - /// No description provided for @select_a_timeframe_for_which_data_will_be_filtered. - /// - /// In en, this message translates to: - /// **'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'** - String get select_a_timeframe_for_which_data_will_be_filtered; - /// No description provided for @timeframe. /// /// In en, this message translates to: @@ -1028,77 +1016,59 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; - /// No description provided for @statistic_scope_all_players. - /// - /// In en, this message translates to: - /// **'All players'** - String get statistic_scope_all_players; - - /// No description provided for @statistic_scope_selected_games. + /// No description provided for @selected_games. /// /// In en, this message translates to: /// **'Selected games'** - String get statistic_scope_selected_games; + String get selected_games; - /// No description provided for @statistic_scope_selected_groups. + /// No description provided for @selected_groups. /// /// In en, this message translates to: /// **'Selected groups'** - String get statistic_scope_selected_groups; + String get selected_groups; - /// No description provided for @statistic_scope_timeframe. - /// - /// In en, this message translates to: - /// **'Timeframe'** - String get statistic_scope_timeframe; - - /// No description provided for @statistic_type_average_score. + /// No description provided for @average_score. /// /// In en, this message translates to: /// **'Average score'** - String get statistic_type_average_score; + String get average_score; - /// No description provided for @statistic_type_best_score. + /// No description provided for @best_score. /// /// In en, this message translates to: /// **'Best score'** - String get statistic_type_best_score; + String get best_score; - /// No description provided for @statistic_type_total_losses. + /// No description provided for @total_losses. /// /// In en, this message translates to: /// **'Total losses'** - String get statistic_type_total_losses; + String get total_losses; - /// No description provided for @statistic_type_total_matches. + /// No description provided for @total_matches. /// /// In en, this message translates to: /// **'Total matches'** - String get statistic_type_total_matches; + String get total_matches; - /// No description provided for @statistic_type_total_score. + /// No description provided for @total_score. /// /// In en, this message translates to: /// **'Total score'** - String get statistic_type_total_score; + String get total_score; - /// No description provided for @statistic_type_total_wins. + /// No description provided for @total_wins. /// /// In en, this message translates to: /// **'Total wins'** - String get statistic_type_total_wins; + String get total_wins; - /// No description provided for @statistic_type_winrate. - /// - /// In en, this message translates to: - /// **'Winrate'** - String get statistic_type_winrate; - - /// No description provided for @statistic_type_worst_score. + /// No description provided for @worst_score. /// /// In en, this message translates to: /// **'Worst score'** - String get statistic_type_worst_score; + String get worst_score; /// Success message when adding a player /// @@ -1130,41 +1100,41 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; - /// No description provided for @timeframe_all_time. + /// No description provided for @all_time. /// /// In en, this message translates to: /// **'All time'** - String get timeframe_all_time; + String get all_time; - /// No description provided for @timeframe_last_180_days. + /// No description provided for @last_180_days. /// /// In en, this message translates to: /// **'Last 180 days'** - String get timeframe_last_180_days; + String get last_180_days; - /// No description provided for @timeframe_last_30_days. + /// No description provided for @last_30_days. /// /// In en, this message translates to: /// **'Last 30 days'** - String get timeframe_last_30_days; + String get last_30_days; - /// No description provided for @timeframe_last_7_days. + /// No description provided for @last_7_days. /// /// In en, this message translates to: /// **'Last 7 days'** - String get timeframe_last_7_days; + String get last_7_days; - /// No description provided for @timeframe_last_90_days. + /// No description provided for @last_90_days. /// /// In en, this message translates to: /// **'Last 90 days'** - String get timeframe_last_90_days; + String get last_90_days; - /// No description provided for @timeframe_last_year. + /// No description provided for @last_year. /// /// In en, this message translates to: /// **'Last year'** - String get timeframe_last_year; + String get last_year; /// No description provided for @today_at. /// diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 9457988..1417bb7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -95,33 +95,28 @@ class AppLocalizationsDe extends AppLocalizations { String get create_statistic => 'Statistik erstellen'; @override - String get which_key_metric => 'Select which key metric you want to display'; + String get classifier => 'Klassifikator'; @override - String get classifier => 'Classifier'; + String get select_the_filtered_timeframe => + 'Wähle einen Zeitraum, nach dem gefiltert werden soll.'; @override - String get select_the_filtered_games => 'Select the filtered games'; + String get select_the_filtered_games => + 'Wähle Spiele, nach denen gefiltert werden soll.'; @override - String get games => 'Games'; + String get games => 'Spielvorlagen'; @override - String get select_the_filtered_groups => 'Select the filtered groups'; + String get select_the_filtered_groups => + 'Wähle Gruppen, nach denen gefiltert werden soll.'; @override - String get select_main_filter => - 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; + String get scope => 'Bereich'; @override - String get scope => 'Scope'; - - @override - String get select_a_timeframe_for_which_data_will_be_filtered => - 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; - - @override - String get timeframe => 'Timeframe'; + String get timeframe => 'Zeitraum'; @override String get created_on => 'Erstellt am'; @@ -370,7 +365,7 @@ class AppLocalizationsDe extends AppLocalizations { String get no_statistics_available => 'Keine Statistiken verfügbar'; @override - String get no_statistics_created_yet => 'No statistics created yet'; + String get no_statistics_created_yet => 'Noch keine Statistiken erstellt'; @override String get none => 'Kein'; @@ -498,40 +493,31 @@ class AppLocalizationsDe extends AppLocalizations { String get stats => 'Statistiken'; @override - String get statistic_scope_all_players => 'Alle Spieler:innen'; + String get selected_games => 'Ausgewählte Spielvorlagen'; @override - String get statistic_scope_selected_games => 'Ausgewählte Spielvorlagen'; + String get selected_groups => 'Ausgewählte Gruppen'; @override - String get statistic_scope_selected_groups => 'Ausgewählte Gruppen'; + String get average_score => 'Durchschnittliche Punktzahl'; @override - String get statistic_scope_timeframe => 'Zeitraum'; + String get best_score => 'Beste Punktzahl'; @override - String get statistic_type_average_score => 'Durchschnittliche Punktzahl'; + String get total_losses => 'Niederlagen insgesamt'; @override - String get statistic_type_best_score => 'Beste Punktzahl'; + String get total_matches => 'Spiele insgesamt'; @override - String get statistic_type_total_losses => 'Niederlagen insgesamt'; + String get total_score => 'Punktzahl insgesamt'; @override - String get statistic_type_total_matches => 'Spiele insgesamt'; + String get total_wins => 'Siege insgesamt'; @override - String get statistic_type_total_score => 'Punktzahl insgesamt'; - - @override - String get statistic_type_total_wins => 'Siege insgesamt'; - - @override - String get statistic_type_winrate => 'Siegquote'; - - @override - String get statistic_type_worst_score => 'Schlechteste Punktzahl'; + String get worst_score => 'Schlechteste Punktzahl'; @override String successfully_added_player(String playerName) { @@ -554,22 +540,22 @@ class AppLocalizationsDe extends AppLocalizations { String get tie => 'Unentschieden'; @override - String get timeframe_all_time => 'Gesamter Zeitraum'; + String get all_time => 'Gesamter Zeitraum'; @override - String get timeframe_last_180_days => 'Letzte 180 Tage'; + String get last_180_days => 'Letzte 180 Tage'; @override - String get timeframe_last_30_days => 'Letzte 30 Tage'; + String get last_30_days => 'Letzte 30 Tage'; @override - String get timeframe_last_7_days => 'Letzte 7 Tage'; + String get last_7_days => 'Letzte 7 Tage'; @override - String get timeframe_last_90_days => 'Letzte 90 Tage'; + String get last_90_days => 'Letzte 90 Tage'; @override - String get timeframe_last_year => 'Letztes Jahr'; + String get last_year => 'Letztes Jahr'; @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 99ecca3..2275b97 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -94,32 +94,27 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_statistic => 'Create statistic'; - @override - String get which_key_metric => 'Select which key metric you want to display'; - @override String get classifier => 'Classifier'; @override - String get select_the_filtered_games => 'Select the filtered games'; + String get select_the_filtered_timeframe => + 'Select the timeframe you want to filter by.'; + + @override + String get select_the_filtered_games => + 'Select the games you want to filter by.'; @override String get games => 'Games'; @override - String get select_the_filtered_groups => 'Select the filtered groups'; - - @override - String get select_main_filter => - 'Select the main filter for your statistic. This will determine which data is used to calculate the selected classifier.'; + String get select_the_filtered_groups => + 'Select the groups you want to filter by.'; @override String get scope => 'Scope'; - @override - String get select_a_timeframe_for_which_data_will_be_filtered => - 'Select a timeframe for which the data will be filtered. Only matches that ended within the selected timeframe will be included in the statistic.'; - @override String get timeframe => 'Timeframe'; @@ -498,40 +493,31 @@ class AppLocalizationsEn extends AppLocalizations { String get stats => 'Stats'; @override - String get statistic_scope_all_players => 'All players'; + String get selected_games => 'Selected games'; @override - String get statistic_scope_selected_games => 'Selected games'; + String get selected_groups => 'Selected groups'; @override - String get statistic_scope_selected_groups => 'Selected groups'; + String get average_score => 'Average score'; @override - String get statistic_scope_timeframe => 'Timeframe'; + String get best_score => 'Best score'; @override - String get statistic_type_average_score => 'Average score'; + String get total_losses => 'Total losses'; @override - String get statistic_type_best_score => 'Best score'; + String get total_matches => 'Total matches'; @override - String get statistic_type_total_losses => 'Total losses'; + String get total_score => 'Total score'; @override - String get statistic_type_total_matches => 'Total matches'; + String get total_wins => 'Total wins'; @override - String get statistic_type_total_score => 'Total score'; - - @override - String get statistic_type_total_wins => 'Total wins'; - - @override - String get statistic_type_winrate => 'Winrate'; - - @override - String get statistic_type_worst_score => 'Worst score'; + String get worst_score => 'Worst score'; @override String successfully_added_player(String playerName) { @@ -553,22 +539,22 @@ class AppLocalizationsEn extends AppLocalizations { String get tie => 'Tie'; @override - String get timeframe_all_time => 'All time'; + String get all_time => 'All time'; @override - String get timeframe_last_180_days => 'Last 180 days'; + String get last_180_days => 'Last 180 days'; @override - String get timeframe_last_30_days => 'Last 30 days'; + String get last_30_days => 'Last 30 days'; @override - String get timeframe_last_7_days => 'Last 7 days'; + String get last_7_days => 'Last 7 days'; @override - String get timeframe_last_90_days => 'Last 90 days'; + String get last_90_days => 'Last 90 days'; @override - String get timeframe_last_year => 'Last year'; + String get last_year => 'Last year'; @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart index 92a01bb..a88432a 100644 --- a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -76,10 +76,10 @@ class _CreateStatisticViewState extends State { fontWeight: FontWeight.bold, ), ), - Text( - loc.select_a_classifier, + const Text( + 'description', textAlign: TextAlign.start, - style: const TextStyle( + style: TextStyle( color: CustomTheme.textColor, fontSize: 12, ), @@ -147,10 +147,10 @@ class _CreateStatisticViewState extends State { fontWeight: FontWeight.bold, ), ), - Text( - loc.select_a_scope, + const Text( + 'description', textAlign: TextAlign.start, - style: const TextStyle( + style: TextStyle( color: CustomTheme.textColor, fontSize: 12, ), @@ -405,7 +405,7 @@ class _CreateStatisticViewState extends State { ), ), Text( - loc.select_a_timeframe_for_which_data_will_be_filtered, + loc.select_the_filtered_timeframe, textAlign: TextAlign.start, style: const TextStyle( color: CustomTheme.textColor, @@ -581,17 +581,17 @@ String translateTimeframeToString(Timeframe timeframe, BuildContext context) { final loc = AppLocalizations.of(context); switch (timeframe) { case Timeframe.last7Days: - return loc.timeframe_last_7_days; + return loc.last_7_days; case Timeframe.last30Days: - return loc.timeframe_last_30_days; + return loc.last_30_days; case Timeframe.last90Days: - return loc.timeframe_last_90_days; + return loc.last_90_days; case Timeframe.last180Days: - return loc.timeframe_last_180_days; + return loc.last_180_days; case Timeframe.lastYear: - return loc.timeframe_last_year; + return loc.last_year; case Timeframe.allTime: - return loc.timeframe_all_time; + return loc.all_time; } } @@ -599,13 +599,13 @@ String translateScopeToString(StatisticScope scope, BuildContext context) { final loc = AppLocalizations.of(context); switch (scope) { case StatisticScope.allPlayers: - return loc.statistic_scope_all_players; + return loc.all_players; case StatisticScope.selectedGroups: - return loc.statistic_scope_selected_groups; + return loc.selected_groups; case StatisticScope.selectedGames: - return loc.statistic_scope_selected_games; + return loc.selected_games; case StatisticScope.timeframe: - return loc.statistic_scope_timeframe; + return loc.timeframe; } } @@ -616,20 +616,20 @@ String translateStatisticTypeToString( final loc = AppLocalizations.of(context); switch (type) { case StatisticType.totalMatches: - return loc.statistic_type_total_matches; + return loc.total_matches; case StatisticType.totalWins: - return loc.statistic_type_total_wins; + return loc.total_wins; case StatisticType.totalScore: - return loc.statistic_type_total_score; + return loc.total_score; case StatisticType.totalLosses: - return loc.statistic_type_total_losses; + return loc.total_losses; case StatisticType.averageScore: - return loc.statistic_type_average_score; + return loc.average_score; case StatisticType.bestScore: - return loc.statistic_type_best_score; + return loc.best_score; case StatisticType.worstScore: - return loc.statistic_type_worst_score; + return loc.worst_score; case StatisticType.winrate: - return loc.statistic_type_winrate; + return loc.winrate; } } -- 2.49.1 From 84bb8ccccc60e91705fc08d873dbe214832d16a0 Mon Sep 17 00:00:00 2001 From: Gitea Actions Date: Mon, 25 May 2026 12:00:57 +0000 Subject: [PATCH 22/39] fix(deps): update dart dependencies (non-major) (#248) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [build_runner](https://github.com/dart-lang/build) ([source](https://github.com/dart-lang/build/tree/HEAD/build_runner)) | dev_dependencies | minor | `^2.7.0` -> `^2.15.0` | | [cupertino_icons](https://github.com/flutter/packages) ([source](https://github.com/flutter/packages/tree/HEAD/third_party/packages/cupertino_icons)) | dependencies | patch | `^1.0.6` -> `^1.0.9` | | [dart](https://dart.dev/) ([source](https://github.com/dart-lang/sdk)) | | minor | `^3.8.1` -> `^3.12.0` | | [dart_pubspec_licenses](https://github.com/espresso3389/flutter_oss_licenses/tree/master/packages/dart_pubspec_licenses) ([source](https://github.com/espresso3389/flutter_oss_licenses)) | dev_dependencies | minor | `^3.0.14` -> `^3.2.0` | | [drift](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dependencies | minor | `^2.27.0` -> `^2.33.0` | | [drift_dev](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dev_dependencies | minor | `^2.27.0` -> `^2.33.0` | | [drift_flutter](https://drift.simonbinder.eu/) ([source](https://github.com/simolus3/drift)) | dependencies | minor | `^0.2.4` -> `^0.3.0` | | [file_saver](https://hassanansari.dev) ([source](https://github.com/incrediblezayed/file_saver)) | dependencies | minor | `^0.3.1` -> `^0.4.0` | | [package_info_plus](https://github.com/fluttercommunity/plus_plugins) ([source](https://github.com/fluttercommunity/plus_plugins/tree/HEAD/packages/package_info_plus/package_info_plus)) | dependencies | patch | `^9.0.0` -> `^9.0.1` | | [skeletonizer](https://github.com/Milad-Akarie/skeletonizer) | dependencies | patch | `^2.1.0+1` -> `^2.1.3` | | [uuid](https://github.com/Daegalus/dart-uuid) | dependencies | patch | `^4.5.2` -> `^4.5.3` | > :exclamation: **Important** > > Release Notes retrieval for this PR were skipped because no github.com credentials were available. > If you are self-hosted, please see [this instruction](https://github.com/renovatebot/renovate/blob/master/docs/usage/examples/self-hosting.md#githubcom-token-for-release-notes). --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Co-authored-by: Felix Kirchner Reviewed-on: https://git.yannick-weigert.de/liquid-development/game-tracker/pulls/248 Co-authored-by: Gitea Actions Co-committed-by: Gitea Actions --- .gitea/workflows/pull_request.yaml | 16 +++--- .../xcshareddata/swiftpm/Package.resolved | 8 --- pubspec.lock | 52 +++++++++---------- pubspec.yaml | 22 ++++---- 4 files changed, 43 insertions(+), 55 deletions(-) diff --git a/.gitea/workflows/pull_request.yaml b/.gitea/workflows/pull_request.yaml index 29c6799..78d3269 100644 --- a/.gitea/workflows/pull_request.yaml +++ b/.gitea/workflows/pull_request.yaml @@ -31,20 +31,16 @@ jobs: test: runs-on: ubuntu-latest + container: + image: ghcr.io/cirruslabs/flutter:stable steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Required for Flutter action - - name: Install jq + - name: Install Node run: | apt-get update - apt-get install -y jq + apt-get install -y nodejs npm - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable + - name: Checkout code + uses: actions/checkout@v4 - name: Get dependencies run: | diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7442998..4d7193e 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,5 @@ { "pins" : [ - { - "identity" : "csqlite", - "kind" : "remoteSourceControl", - "location" : "https://github.com/simolus3/CSQLite.git", - "state" : { - "revision" : "1ee46d19a4f451a7aa64ffc64fc99b4748131e62" - } - }, { "identity" : "dkcamera", "kind" : "remoteSourceControl", diff --git a/pubspec.lock b/pubspec.lock index 96b2cc5..2ec2197 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -85,10 +85,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" + sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.15.0" built_collection: dependency: transitive description: @@ -177,14 +177,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" collection: dependency: "direct main" description: @@ -333,26 +325,26 @@ packages: dependency: "direct main" description: name: drift - sha256: "970cd188fddb111b26ea6a9b07a62bf5c2432d74147b8122c67044ae3b97e99e" + sha256: "8033500116b24398fba0cca0369cc31678cd627c01e41753a61186911cea743e" url: "https://pub.dev" source: hosted - version: "2.31.0" + version: "2.33.0" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: "917184b2fb867b70a548a83bf0d36268423b38d39968c06cce4905683da49587" + sha256: b3dd5b75e30522a91da8abda9f5bb17230cb038097f6d15fa75d42bb563428aa url: "https://pub.dev" source: hosted - version: "2.31.0" + version: "2.33.0" drift_flutter: dependency: "direct main" description: name: drift_flutter - sha256: c07120854742a0cae2f7501a0da02493addde550db6641d284983c08762e60a7 + sha256: "887fdec622174dc7eaefd0048403e34ee07cc18626ac8a7544cc3b8a4a172166" url: "https://pub.dev" source: hosted - version: "0.2.8" + version: "0.3.0" equatable: dependency: transitive description: @@ -397,10 +389,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "68c9a085d9bb4546e0a31d1e583a48d7c17a6987d538788ea064f0043b1fc02d" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.4.0" fixnum: dependency: transitive description: @@ -1122,30 +1114,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.2" + sqlcipher_flutter_libs: + dependency: transitive + description: + name: sqlcipher_flutter_libs + sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929" + url: "https://pub.dev" + source: hosted + version: "0.7.0+eol" sqlite3: dependency: transitive description: name: sqlite3 - sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" + sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5" url: "https://pub.dev" source: hosted - version: "2.9.4" + version: "3.3.1" sqlite3_flutter_libs: dependency: transitive description: name: sqlite3_flutter_libs - sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad + sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" url: "https://pub.dev" source: hosted - version: "0.5.42" + version: "0.6.0+eol" sqlparser: dependency: transitive description: name: sqlparser - sha256: "337e9997f7141ffdd054259128553c348635fa318f7ca492f07a4ab76f850d19" + sha256: ecdc06d4a7d79dcbc928d99afd2f7f5b0f98a637c46f89be83d911617f759978 url: "https://pub.dev" source: hosted - version: "0.43.1" + version: "0.44.4" stack_trace: dependency: transitive description: @@ -1427,5 +1427,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.41.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3d8d99f..4605476 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,16 +4,16 @@ publish_to: 'none' version: 0.0.33+273 environment: - sdk: ^3.8.1 + sdk: ^3.12.0 dependencies: clock: ^1.1.2 collection: ^1.19.1 - cupertino_icons: ^1.0.6 - drift: ^2.27.0 - drift_flutter: ^0.2.4 + cupertino_icons: ^1.0.9 + drift: ^2.33.0 + drift_flutter: ^0.3.0 file_picker: ^11.0.2 - file_saver: ^0.3.1 + file_saver: ^0.4.0 flutter: sdk: flutter flutter_localizations: @@ -24,20 +24,20 @@ dependencies: font_awesome_flutter: ^11.0.0 intl: any json_schema: ^5.2.2 - package_info_plus: ^9.0.0 + package_info_plus: ^9.0.1 path_provider: ^2.1.5 provider: ^6.1.5 - skeletonizer: ^2.1.0+1 + skeletonizer: ^2.1.3 url_launcher: ^6.3.2 - uuid: ^4.5.2 + uuid: ^4.5.3 dev_dependencies: arb_utils: ^0.11.0 flutter_test: sdk: flutter - build_runner: ^2.7.0 - dart_pubspec_licenses: ^3.0.14 - drift_dev: ^2.27.0 + build_runner: ^2.15.0 + dart_pubspec_licenses: ^3.2.0 + drift_dev: ^2.33.0 flutter_lints: ^6.0.0 flutter: -- 2.49.1 From 9fe74c291c65cb4b3f2d01955cadae8ff9c8b647 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:01:06 +0000 Subject: [PATCH 23/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4605476..58fdc9b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+273 +version: 0.0.34+274 environment: sdk: ^3.12.0 -- 2.49.1 From d4a67f4086f69dcd879cd5c01feaf0f5720a8d09 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:01:43 +0000 Subject: [PATCH 24/39] Updated licenses [skip ci] --- .../settings_view/licenses/oss_licenses.dart | 343 ++++++++++++++---- 1 file changed, 274 insertions(+), 69 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 591b7fe..a9b8d15 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -34,7 +34,6 @@ const allDependencies = [ _cli_util, _clock, _code_assets, - _code_builder, _collection, _convert, _coverage, @@ -154,6 +153,7 @@ const allDependencies = [ _source_map_stack_trace, _source_maps, _source_span, + _sqlcipher_flutter_libs, _sqlite3, _sqlite3_flutter_libs, _sqlparser, @@ -670,17 +670,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// build_runner 2.13.1 +/// build_runner 2.15.0 const _build_runner = Package( name: 'build_runner', description: 'A build system for Dart code generation and modular compilation.', repository: 'https://github.com/dart-lang/build/tree/master/build_runner', authors: [], - version: '2.13.1', + version: '2.15.0', spdxIdentifiers: ['BSD-3-Clause'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('code_builder'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')], + dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')], devDependencies: [PackageRef('stream_channel'), PackageRef('test')], license: '''Copyright 2016, the Dart project authors. @@ -1510,47 +1510,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// code_builder 4.11.1 -const _code_builder = Package( - name: 'code_builder', - description: 'A fluent, builder-based library for generating valid Dart code.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/code_builder', - authors: [], - version: '4.11.1', - spdxIdentifiers: ['BSD-3-Clause'], - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('matcher'), PackageRef('meta')], - devDependencies: [PackageRef('build'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('source_gen'), PackageRef('test')], - license: '''Copyright 2016, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - ); - /// collection 1.19.1 const _collection = Package( name: 'collection', @@ -2581,14 +2540,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift 2.31.0 +/// drift 2.33.0 const _drift = Package( name: 'drift', description: 'Drift is a reactive library to store relational data in Dart and Flutter applications.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '2.31.0', + version: '2.33.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -2617,14 +2576,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift_dev 2.31.0 +/// drift_dev 2.33.0 const _drift_dev = Package( name: 'drift_dev', description: 'Dev-dependency for users of drift. Contains the generator and development tools.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '2.31.0', + version: '2.33.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -2653,18 +2612,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift_flutter 0.2.8 +/// drift_flutter 0.3.0 const _drift_flutter = Package( name: 'drift_flutter', description: 'Easily set up drift databases across platforms in Flutter apps.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '0.2.8', + version: '0.3.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs')], + dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs'), PackageRef('sqlcipher_flutter_libs')], devDependencies: [PackageRef('build_runner'), PackageRef('drift_dev'), PackageRef('lints'), PackageRef('test'), PackageRef('flutter_test'), PackageRef('async')], license: '''MIT License @@ -3057,18 +3016,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// file_saver 0.3.1 +/// file_saver 0.4.0 const _file_saver = Package( name: 'file_saver', - description: 'A Flutter plugin for saving files across all platforms (Android, iOS, Web, Windows, macOS, Linux). Save files from bytes, File objects, file paths, or download from URLs with a single method call. Features include MIME type support, Dio integration, and platform-specific save locations. Supports saveAs() dialog for user-selected locations on supported platforms.', + description: 'Save files from bytes, paths, streams, and URLs across Android, iOS, Web, Windows, macOS, and Linux.', homepage: 'https://hassanansari.dev', repository: 'https://github.com/incrediblezayed/file_saver', authors: [], - version: '0.3.1', + version: '0.4.0', spdxIdentifiers: ['BSD-3-Clause'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('path_provider_linux'), PackageRef('path_provider_windows'), PackageRef('web')], + dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('web')], devDependencies: [PackageRef('flutter_lints'), PackageRef('flutter_test')], license: '''BSD 3-Clause License @@ -37683,18 +37642,264 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// sqlite3 2.9.4 +/// sqlcipher_flutter_libs 0.7.0+eol +const _sqlcipher_flutter_libs = Package( + name: 'sqlcipher_flutter_libs', + description: 'Not used anymore, update to version 3.x of package:sqlite3 instead', + homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlcipher_flutter_libs', + authors: [], + version: '0.7.0+eol', + spdxIdentifiers: ['Pixar', 'MIT', 'BSD-3-Clause-HP'], + isMarkdown: false, + isSdk: false, + dependencies: [], + devDependencies: [], + license: '''sqlcipher_flutter_libs + +MIT License + +Copyright (c) 2020 Simon Binder + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +OpenSSL + + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +-------------------------------------------------------------------------------- +SQLCipher + +Copyright (c) 2008-2020 Zetetic LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the ZETETIC LLC nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + ); + +/// sqlite3 3.3.1 const _sqlite3 = Package( name: 'sqlite3', description: 'Provides lightweight yet convenient bindings to SQLite by using dart:ffi', homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3', authors: [], - version: '2.9.4', + version: '3.3.1', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data')], - devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert')], + dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data'), PackageRef('hooks'), PackageRef('code_assets'), PackageRef('native_toolchain_c'), PackageRef('crypto')], + devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert'), PackageRef('package_config'), PackageRef('logging')], license: '''MIT License Copyright (c) 2020 Simon Binder @@ -37718,17 +37923,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// sqlite3_flutter_libs 0.5.42 +/// sqlite3_flutter_libs 0.6.0+eol const _sqlite3_flutter_libs = Package( name: 'sqlite3_flutter_libs', - description: 'Flutter plugin to include native sqlite3 libraries with your app', - homepage: 'https://github.com/simolus3/sqlite3.dart/tree/v2/sqlite3_flutter_libs', + description: 'Not used anymore, update to version 3.x of package:sqlite3 instead', + homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlite3_flutter_libs', authors: [], - version: '0.5.42', + version: '0.6.0+eol', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')], + dependencies: [], devDependencies: [], license: '''MIT License @@ -37753,14 +37958,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// sqlparser 0.43.1 +/// sqlparser 0.44.4 const _sqlparser = Package( name: 'sqlparser', description: 'Parses sqlite statements and performs static analysis on them', homepage: 'https://github.com/simolus3/drift/tree/develop/sqlparser', repository: 'https://github.com/simolus3/drift', authors: [], - version: '0.43.1', + version: '0.44.4', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -39415,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.33+273 +/// tallee 0.0.34+274 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.33+273', + version: '0.0.34+274', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, -- 2.49.1 From 362ab2a945a74253a29892a4cd723093ae2cefbc Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 14:03:18 +0200 Subject: [PATCH 25/39] Updated test job --- .gitea/workflows/push.yaml | 17 +++++++---------- pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.gitea/workflows/push.yaml b/.gitea/workflows/push.yaml index e836082..3f6bd00 100644 --- a/.gitea/workflows/push.yaml +++ b/.gitea/workflows/push.yaml @@ -7,22 +7,19 @@ on: - "main" jobs: + test: runs-on: ubuntu-latest + container: + image: ghcr.io/cirruslabs/flutter:stable steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Required for Flutter action - - name: Install jq + - name: Install Node run: | apt-get update - apt-get install -y jq + apt-get install -y nodejs npm - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable + - name: Checkout code + uses: actions/checkout@v4 - name: Get dependencies run: | diff --git a/pubspec.yaml b/pubspec.yaml index 58fdc9b..2ad29fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.34+274 +version: 0.0.33+274 environment: sdk: ^3.12.0 -- 2.49.1 From e7f904296d611dd65b6c26a16bade454cc99399a Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:03:30 +0000 Subject: [PATCH 26/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2ad29fd..c8f594c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+274 +version: 0.0.34+275 environment: sdk: ^3.12.0 -- 2.49.1 From 250c647fb2945c1c48b70c61c288251ee38540f4 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:04:06 +0000 Subject: [PATCH 27/39] Updated licenses [skip ci] --- .../views/main_menu/settings_view/licenses/oss_licenses.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index a9b8d15..9547f3f 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -39620,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.34+274 +/// tallee 0.0.34+275 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.34+274', + version: '0.0.34+275', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, -- 2.49.1 From ad5cd98327164048b319db879fb99bff5ee9e734 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 14:08:10 +0200 Subject: [PATCH 28/39] fix: build job --- .gitea/workflows/push.yaml | 5 +++++ pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/push.yaml b/.gitea/workflows/push.yaml index 3f6bd00..4d2d68c 100644 --- a/.gitea/workflows/push.yaml +++ b/.gitea/workflows/push.yaml @@ -292,6 +292,11 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 + with: + packages: |- + platform-tools + platforms;android-34 + build-tools;34.0.0 # Required for Flutter action - name: Install jq diff --git a/pubspec.yaml b/pubspec.yaml index c8f594c..c166959 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.34+275 +version: 0.0.33+275 environment: sdk: ^3.12.0 -- 2.49.1 From a951f3c9b2e5754e5ca5d4e95520f9611d8831fd Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:08:20 +0000 Subject: [PATCH 29/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index c166959..2eca5dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+275 +version: 0.0.34+276 environment: sdk: ^3.12.0 -- 2.49.1 From 258e668a5e4b4026e92b1fff497c60b3ad658f1b Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:08:58 +0000 Subject: [PATCH 30/39] Updated licenses [skip ci] --- .../views/main_menu/settings_view/licenses/oss_licenses.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 9547f3f..2c9e50a 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -39620,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.34+275 +/// tallee 0.0.34+276 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.34+275', + version: '0.0.34+276', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, -- 2.49.1 From f7973a4bc22fc37b3ef9d07b1d7853e7ebceb370 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 14:12:01 +0200 Subject: [PATCH 31/39] fix: build job --- .gitea/workflows/push.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitea/workflows/push.yaml b/.gitea/workflows/push.yaml index 4d2d68c..a006e74 100644 --- a/.gitea/workflows/push.yaml +++ b/.gitea/workflows/push.yaml @@ -293,10 +293,7 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 with: - packages: |- - platform-tools - platforms;android-34 - build-tools;34.0.0 + packages: "platform-tools platforms;android-34 build-tools;34.0.0" # Required for Flutter action - name: Install jq -- 2.49.1 From 82325ea2718ebf6f0c217764b93fea5ae5382e33 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:12:09 +0000 Subject: [PATCH 32/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 2eca5dd..7906d03 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.34+276 +version: 0.0.35+277 environment: sdk: ^3.12.0 -- 2.49.1 From dba6c218d6d503f012149a59b58368b7e0731362 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:12:46 +0000 Subject: [PATCH 33/39] Updated licenses [skip ci] --- .../views/main_menu/settings_view/licenses/oss_licenses.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 2c9e50a..9bd4e0b 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -39620,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.34+276 +/// tallee 0.0.35+277 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.34+276', + version: '0.0.35+277', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, -- 2.49.1 From 712d48b1d7c37bdaa48f7fd66bb967b890ed4c89 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:51:48 +0000 Subject: [PATCH 34/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 183335c..e90b0ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+281 +version: 0.0.34+282 environment: sdk: ^3.12.0 -- 2.49.1 From 5659dc36c2411d7f2c3fd06492b9b8e4b2c602b2 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:52:27 +0000 Subject: [PATCH 35/39] Updated licenses [skip ci] --- .../settings_view/licenses/oss_licenses.dart | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 9bd4e0b..7614949 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -14,6 +14,7 @@ const thisPackage = _tallee; const allDependencies = [ __fe_analyzer_shared, _analyzer, + _animated_custom_dropdown, _arb_utils, _archive, _args, @@ -55,6 +56,7 @@ const allDependencies = [ _drift, _drift_dev, _drift_flutter, + _dropdown_flutter, _equatable, _fake_async, _ffi, @@ -196,8 +198,10 @@ const allDependencies = [ /// Direct `dependencies`. const dependencies = [ + _animated_custom_dropdown, _clock, _collection, + _dropdown_flutter, _cupertino_icons, _drift, _drift_flutter, @@ -362,6 +366,47 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); +/// animated_custom_dropdown 3.1.1 +const _animated_custom_dropdown = Package( + name: 'animated_custom_dropdown', + description: 'Custom dropdown widget allows to add highly customizable dropdown widget in your projects. Features includes Search on list data, Network search, Multi-selection and many more.', + homepage: 'https://github.com/AbdullahChauhan/custom-dropdown', + authors: [], + version: '3.1.1', + spdxIdentifiers: ['BSD-3-Clause'], + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('flutter')], + devDependencies: [PackageRef('flutter_test'), PackageRef('flutter_lints')], + license: '''Copyright (c) 2024 Abdullah Chauhan + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + ); + /// arb_utils 0.11.0 const _arb_utils = Package( name: 'arb_utils', @@ -2639,6 +2684,41 @@ furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.''', + ); + +/// dropdown_flutter 1.0.3 +const _dropdown_flutter = Package( + name: 'dropdown_flutter', + description: 'A Flutter package designed to enhance your app with customizable dropdowns, featuring list data search, network search, and multi-selection.', + homepage: 'https://github.com/farhansadikgalib/dropdown_flutter', + authors: [], + version: '1.0.3', + spdxIdentifiers: ['MIT'], + isMarkdown: false, + isSdk: false, + dependencies: [PackageRef('flutter')], + devDependencies: [PackageRef('flutter_test'), PackageRef('flutter_lints')], + license: '''MIT License + +Copyright (c) 2024 Farhan Sadik Galib + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39620,16 +39700,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.35+277 +/// tallee 0.0.34+282 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.35+277', + version: '0.0.34+282', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('clock'), PackageRef('collection'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_numeric_text'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')], + dependencies: [PackageRef('animated_custom_dropdown'), PackageRef('clock'), PackageRef('collection'), PackageRef('dropdown_flutter'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_numeric_text'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')], devDependencies: [PackageRef('arb_utils'), PackageRef('flutter_test'), PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')], license: '''GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 -- 2.49.1 From 9b208f478025748cdd12255b62edf1bf1007c32b Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 14:55:19 +0200 Subject: [PATCH 36/39] Revert "Merge branch 'feature/193-statisticsview-rework' into development" This reverts commit 24f49e17b92c653e1b8a7bde31ab8a9181535b24, reversing changes made to dba6c218d6d503f012149a59b58368b7e0731362. # Conflicts: # pubspec.yaml --- lib/core/common.dart | 44 +- lib/core/enums.dart | 30 +- lib/data/dao/game_dao.dart | 36 +- lib/data/dao/group_dao.dart | 70 +- lib/data/dao/match_dao.dart | 96 +- lib/data/dao/player_dao.dart | 155 +- lib/data/dao/player_group_dao.dart | 39 +- lib/data/dao/player_match_dao.dart | 23 +- lib/data/dao/score_entry_dao.dart | 42 +- lib/data/dao/statistic_dao.dart | 127 - lib/data/dao/statistic_dao.g.dart | 19 - lib/data/dao/statistic_game_dao.dart | 61 - lib/data/dao/statistic_game_dao.g.dart | 29 - lib/data/dao/statistic_group_dao.dart | 67 - lib/data/dao/statistic_group_dao.g.dart | 29 - lib/data/dao/statistic_scope_dao.dart | 55 - lib/data/dao/statistic_scope_dao.g.dart | 26 - lib/data/dao/team_dao.dart | 22 +- lib/data/db/database.dart | 16 - lib/data/db/database.g.dart | 2912 +---------------- lib/data/db/tables/statistic_game_table.dart | 13 - lib/data/db/tables/statistic_group_table.dart | 13 - lib/data/db/tables/statistic_scope_table.dart | 11 - lib/data/db/tables/statistic_table.dart | 11 - lib/data/models/game.dart | 8 +- lib/data/models/group.dart | 8 +- lib/data/models/match.dart | 2 +- lib/data/models/statistic.dart | 48 - lib/l10n/arb/app_de.arb | 70 - lib/l10n/arb/app_en.arb | 53 +- lib/l10n/generated/app_localizations.dart | 268 +- lib/l10n/generated/app_localizations_de.dart | 137 +- lib/l10n/generated/app_localizations_en.dart | 139 +- .../main_menu/custom_navigation_bar.dart | 2 +- .../group_view/create_group_view.dart | 3 +- .../main_menu/group_view/group_view.dart | 6 +- .../create_match/choose_game_view.dart | 2 +- .../create_match/create_game_view.dart | 17 +- .../create_match/create_match_view.dart | 1 - .../main_menu/match_view/match_view.dart | 5 +- .../views/main_menu/player_detail_view.dart | 394 --- .../views/main_menu/statistics_view.dart | 311 ++ .../create_statistic_view.dart | 635 ---- .../statistic_detail_view.dart | 191 -- .../statistic_tile_factory.dart | 322 -- .../statistics_view/statistics_view.dart | 190 -- lib/presentation/widgets/app_skeleton.dart | 16 +- .../buttons/animated_dialog_button.dart | 56 +- .../widgets/dialog/custom_alert_dialog.dart | 1 + .../widgets/dialog/custom_dialog_action.dart | 15 +- lib/presentation/widgets/game_label.dart | 60 +- .../widgets/player_selection.dart | 5 - lib/presentation/widgets/tiles/game_tile.dart | 2 +- .../widgets/tiles/group_tile.dart | 19 - .../widgets/tiles/match_tile.dart | 19 - .../widgets/tiles/statistics_tile.dart | 288 +- .../widgets/tiles/text_icon_tile.dart | 85 +- lib/services/data_transfer_service.dart | 3 +- pubspec.lock | 68 +- pubspec.yaml | 2 - test/db_tests/aggregates/group_test.dart | 25 - test/db_tests/aggregates/match_test.dart | 30 +- test/db_tests/aggregates/team_test.dart | 2 +- test/db_tests/entities/game_test.dart | 16 +- test/db_tests/entities/player_test.dart | 189 +- .../relationships/player_match_test.dart | 2 +- test/db_tests/statistics/statistic_test.dart | 122 - test/db_tests/values/score_entry_test.dart | 2 +- test/services/data_transfer_service_test.dart | 14 +- 69 files changed, 834 insertions(+), 6965 deletions(-) delete mode 100644 lib/data/dao/statistic_dao.dart delete mode 100644 lib/data/dao/statistic_dao.g.dart delete mode 100644 lib/data/dao/statistic_game_dao.dart delete mode 100644 lib/data/dao/statistic_game_dao.g.dart delete mode 100644 lib/data/dao/statistic_group_dao.dart delete mode 100644 lib/data/dao/statistic_group_dao.g.dart delete mode 100644 lib/data/dao/statistic_scope_dao.dart delete mode 100644 lib/data/dao/statistic_scope_dao.g.dart delete mode 100644 lib/data/db/tables/statistic_game_table.dart delete mode 100644 lib/data/db/tables/statistic_group_table.dart delete mode 100644 lib/data/db/tables/statistic_scope_table.dart delete mode 100644 lib/data/db/tables/statistic_table.dart delete mode 100644 lib/data/models/statistic.dart delete mode 100644 lib/presentation/views/main_menu/player_detail_view.dart create mode 100644 lib/presentation/views/main_menu/statistics_view.dart delete mode 100644 lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart delete mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart delete mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart delete mode 100644 lib/presentation/views/main_menu/statistics_view/statistics_view.dart delete mode 100644 test/db_tests/statistics/statistic_test.dart diff --git a/lib/core/common.dart b/lib/core/common.dart index df88ea3..312e3fa 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -24,48 +24,48 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } } -/// Translates a [AppColor] enum value to its corresponding localized string. -String translateAppColorToString(AppColor color, BuildContext context) { +/// Translates a [GameColor] enum value to its corresponding localized string. +String translateGameColorToString(GameColor color, BuildContext context) { final loc = AppLocalizations.of(context); switch (color) { - case AppColor.red: + case GameColor.red: return loc.color_red; - case AppColor.blue: + case GameColor.blue: return loc.color_blue; - case AppColor.green: + case GameColor.green: return loc.color_green; - case AppColor.yellow: + case GameColor.yellow: return loc.color_yellow; - case AppColor.purple: + case GameColor.purple: return loc.color_purple; - case AppColor.orange: + case GameColor.orange: return loc.color_orange; - case AppColor.pink: + case GameColor.pink: return loc.color_pink; - case AppColor.teal: + case GameColor.teal: return loc.color_teal; } } -/// Returns the [Color] object corresponding to a [AppColor] enum value. -Color getColorFromAppColor(AppColor color) { +/// Returns the [Color] object corresponding to a [GameColor] enum value. +Color getColorFromGameColor(GameColor color) { switch (color) { - case AppColor.red: + case GameColor.red: return Colors.red; - case AppColor.blue: + case GameColor.blue: return Colors.blue; - case AppColor.green: + case GameColor.green: return Colors.green; - case AppColor.yellow: + case GameColor.yellow: return const Color(0xFFF7CA28); - case AppColor.purple: + case GameColor.purple: return Colors.purple; - case AppColor.orange: + case GameColor.orange: return const Color(0xFFef681f); - case AppColor.pink: - return const Color(0xFFE91E63); - case AppColor.teal: - return const Color(0xFF00BCD4); + case GameColor.pink: + return Colors.pink; + case GameColor.teal: + return Colors.teal; } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 073fd7a..99141e4 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -43,32 +43,4 @@ enum Ruleset { } /// Different colors for highlighting games -enum AppColor { red, orange, yellow, green, teal, blue, purple, pink } - -enum StatisticType { - totalMatches, - totalWins, - totalScore, - totalLosses, - averageScore, - bestScore, - worstScore, - winrate, -} - -enum StatisticScope { - allPlayers, - //selectedPlayer, - selectedGroups, - selectedGames, - timeframe, -} - -enum Timeframe { - last7Days, - last30Days, - last90Days, - last180Days, - lastYear, - allTime, -} +enum GameColor { red, orange, yellow, green, teal, blue, purple, pink } diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index 3ee8ebd..a4c2300 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -77,8 +77,8 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Returns `true` if the game exists, `false` otherwise. Future gameExists({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final row = await query.getSingleOrNull(); - return row != null; + final result = await query.getSingleOrNull(); + return result != null; } /// Retrieves all games from the database. @@ -92,7 +92,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { name: row.name, ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), description: row.description, - color: AppColor.values.firstWhere((e) => e.name == row.color), + color: GameColor.values.firstWhere((e) => e.name == row.color), icon: row.icon, createdAt: row.createdAt, ), @@ -103,15 +103,15 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Retrieves a [Game] by its [gameId]. Future getGameById({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final row = await query.getSingle(); + final result = await query.getSingle(); return Game( - id: row.id, - name: row.name, - ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), - description: row.description, - color: AppColor.values.firstWhere((e) => e.name == row.color), - icon: row.icon, - createdAt: row.createdAt, + id: result.id, + name: result.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), + description: result.description, + color: GameColor.values.firstWhere((e) => e.name == result.color), + icon: result.icon, + createdAt: result.createdAt, ); } @@ -123,7 +123,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String name, }) async { final rowsAffected = - await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -135,7 +135,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required Ruleset ruleset, }) async { final rowsAffected = - await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(ruleset: Value(ruleset.name)), ); return rowsAffected > 0; @@ -147,7 +147,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String description, }) async { final rowsAffected = - await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(description: Value(description)), ); return rowsAffected > 0; @@ -156,10 +156,10 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Updates the color of the game with the given [gameId]. Future updateGameColor({ required String gameId, - required AppColor color, + required GameColor color, }) async { final rowsAffected = - await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(color: Value(color.name)), ); return rowsAffected > 0; @@ -171,7 +171,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String icon, }) async { final rowsAffected = - await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(icon: Value(icon)), ); return rowsAffected > 0; @@ -182,7 +182,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Deletes the game with the given [gameId] from the database. /// Returns `true` if the game was deleted, `false` if the game did not exist. Future deleteGame({required String gameId}) async { - final query = delete(gameTable)..where((tbl) => tbl.id.equals(gameId)); + final query = delete(gameTable)..where((g) => g.id.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index 8d1c0a2..bffe5a4 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -143,16 +143,16 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final query = select(groupTable); final result = await query.get(); return Future.wait( - result.map((row) async { + result.map((groupData) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: row.id, + groupId: groupData.id, ); return Group( - id: row.id, - name: row.name, - description: row.description, + id: groupData.id, + name: groupData.name, + description: groupData.description, members: members, - createdAt: row.createdAt, + createdAt: groupData.createdAt, ); }), ); @@ -161,18 +161,18 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Retrieves a [Group] by its [groupId], including its members. Future getGroupById({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final row = await query.getSingle(); + final result = await query.getSingle(); List members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupId, ); return Group( - id: row.id, - name: row.name, - description: row.description, + id: result.id, + name: result.name, + description: result.description, members: members, - createdAt: row.createdAt, + createdAt: result.createdAt, ); } @@ -180,49 +180,17 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future getGroupCount() async { final count = await (selectOnly(groupTable)..addColumns([groupTable.id.count()])) - .map((tbl) => tbl.read(groupTable.id.count())) + .map((row) => row.read(groupTable.id.count())) .getSingle(); return count ?? 0; } - /// Retrieves all groups a specific player belongs to. - /// Returns an empty list if the player is not part of any group. - Future> getGroupsByPlayer({required String playerId}) async { - final playerGroups = await (select( - playerGroupTable, - )..where((tbl) => tbl.playerId.equals(playerId))).get(); - - if (playerGroups.isEmpty) return []; - - final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList(); - final result = - await (select(groupTable) - ..where((tbl) => tbl.id.isIn(groupIds)) - ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) - .get(); - - return Future.wait( - result.map((row) async { - final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: row.id, - ); - return Group( - id: row.id, - name: row.name, - description: row.description, - members: members, - createdAt: row.createdAt, - ); - }), - ); - } - /// Checks if a group with the given [groupId] exists in the database. /// Returns `true` if the group exists, `false` otherwise. Future groupExists({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final row = await query.getSingleOrNull(); - return row != null; + final result = await query.getSingleOrNull(); + return result != null; } /* Delete */ @@ -252,8 +220,9 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String name, }) async { final rowsAffected = - await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) - .write(GroupTableCompanion(name: Value(name))); + await (update(groupTable)..where((g) => g.id.equals(groupId))).write( + GroupTableCompanion(name: Value(name)), + ); return rowsAffected > 0; } @@ -264,8 +233,9 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String description, }) async { final rowsAffected = - await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) - .write(GroupTableCompanion(description: Value(description))); + await (update(groupTable)..where((g) => g.id.equals(groupId))).write( + GroupTableCompanion(description: Value(description)), + ); return rowsAffected > 0; } } diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index 74611b6..88cca35 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -258,15 +258,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Returns `true` if the match exists, otherwise `false`. Future matchExists({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final row = await query.getSingleOrNull(); - return row != null; + final result = await query.getSingleOrNull(); + return result != null; } /// Retrieves the number of matches in the database. Future getMatchCount() async { final count = await (selectOnly(matchTable)..addColumns([matchTable.id.count()])) - .map((tbl) => tbl.read(matchTable.id.count())) + .map((row) => row.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -279,12 +279,10 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return Future.wait( result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); - Group? group; if (row.groupId != null) { group = await db.groupDao.getGroupById(groupId: row.groupId!); } - final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); @@ -314,13 +312,13 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Retrieves a [Match] by its [matchId]. Future getMatchById({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final row = await query.getSingle(); + final result = await query.getSingle(); - final game = await db.gameDao.getGameById(gameId: row.gameId); + final game = await db.gameDao.getGameById(gameId: result.gameId); Group? group; - if (row.groupId != null) { - group = await db.groupDao.getGroupById(groupId: row.groupId!); + if (result.groupId != null) { + group = await db.groupDao.getGroupById(groupId: result.groupId!); } final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); @@ -330,15 +328,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { final teams = await _getMatchTeams(matchId: matchId); return Match( - id: row.id, - name: row.name, + id: result.id, + name: result.name, game: game, group: group, players: players, teams: teams.isEmpty ? null : teams, - notes: row.notes, - createdAt: row.createdAt, - endedAt: row.endedAt, + notes: result.notes, + createdAt: result.createdAt, + endedAt: result.endedAt, scores: scores, ); } @@ -349,73 +347,25 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { await (selectOnly(matchTable) ..where(matchTable.gameId.equals(gameId)) ..addColumns([matchTable.id.count()])) - .map((tbl) => tbl.read(matchTable.id.count())) + .map((row) => row.read(matchTable.id.count())) .getSingle(); return count ?? 0; } - Future> getMatchesByPlayer({required String playerId}) async { - final playerMatches = await (select( - playerMatchTable, - )..where((tbl) => tbl.playerId.equals(playerId))).get(); - - if (playerMatches.isEmpty) return []; - - final matchIds = playerMatches.map((tbl) => tbl.matchId).toSet().toList(); - final result = - await (select(matchTable) - ..where((tbl) => tbl.id.isIn(matchIds)) - ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) - .get(); - - return Future.wait( - result.map((row) async { - final game = await db.gameDao.getGameById(gameId: row.gameId); - - Group? group; - if (row.groupId != null) { - group = await db.groupDao.getGroupById(groupId: row.groupId!); - } - - final players = await db.playerMatchDao.getPlayersOfMatch( - matchId: row.id, - ); - final scores = await db.scoreEntryDao.getAllMatchScores( - matchId: row.id, - ); - final teams = await _getMatchTeams(matchId: row.id); - - return Match( - id: row.id, - name: row.name, - game: game, - group: group, - players: players, - teams: teams.isEmpty ? null : teams, - notes: row.notes, - createdAt: row.createdAt, - endedAt: row.endedAt, - scores: scores, - ); - }), - ); - } - /// Retrieves all matches associated with the given [groupId]. /// Queries the database directly, filtering by [groupId]. Future> getMatchesByGroup({required String groupId}) async { final query = select(matchTable)..where((m) => m.groupId.equals(groupId)); - final result = await query.get(); + final rows = await query.get(); return Future.wait( - result.map((row) async { + rows.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); final group = await db.groupDao.getGroupById(groupId: groupId); final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); final teams = await _getMatchTeams(matchId: row.id); - return Match( id: row.id, name: row.name, @@ -435,7 +385,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> _getMatchTeams({required String matchId}) async { // Get all unique team IDs from PlayerMatchTable for this match final playerMatchQuery = select(db.playerMatchTable) - ..where((tbl) => tbl.matchId.equals(matchId) & tbl.teamId.isNotNull()); + ..where((pm) => pm.matchId.equals(matchId) & pm.teamId.isNotNull()); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; @@ -462,7 +412,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String name, }) async { - final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(name: Value(name)), ); @@ -477,7 +427,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String? groupId, }) async { - final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(groupId: Value(groupId)), ); @@ -490,7 +440,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String notes, }) async { - final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(notes: Value(notes)), ); @@ -501,7 +451,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Sets the groupId to null. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future removeMatchGroup({required String matchId}) async { - final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( const MatchTableCompanion(groupId: Value(null)), ); @@ -515,7 +465,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required DateTime endedAt, }) async { - final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(endedAt: Value(endedAt)), ); @@ -527,7 +477,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes the match with the given [matchId] from the database. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future deleteMatch({required String matchId}) async { - final query = delete(matchTable)..where((tbl) => tbl.id.equals(matchId)); + final query = delete(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -543,7 +493,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes all matches associated with a specific game. /// Returns the number of matches deleted. Future deleteMatchesByGame({required String gameId}) async { - final query = delete(matchTable)..where((tbl) => tbl.gameId.equals(gameId)); + final query = delete(matchTable)..where((m) => m.gameId.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected; } diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index 1a60243..51e5845 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -17,7 +17,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// the new one. Future addPlayer({required Player player}) async { if (!await playerExists(playerId: player.id)) { - final int nameCount = await _processNameCount(name: player.name); + final int nameCount = await calculateNameCount(name: player.name); await into(playerTable).insert( PlayerTableCompanion.insert( @@ -64,7 +64,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final playersWithName = entry.value; // Get the current nameCount - var nameCount = await _processNameCount(name: name); + var nameCount = await calculateNameCount(name: name); // One player with the same name if (playersWithName.length == 1) { @@ -113,7 +113,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { Future getPlayerCount() async { final count = await (selectOnly(playerTable)..addColumns([playerTable.id.count()])) - .map((tbl) => tbl.read(playerTable.id.count())) + .map((row) => row.read(playerTable.id.count())) .getSingle(); return count ?? 0; } @@ -122,8 +122,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns `true` if the player exists, `false` otherwise. Future playerExists({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final row = await query.getSingleOrNull(); - return row != null; + final result = await query.getSingleOrNull(); + return result != null; } /// Retrieves all players from the database. @@ -146,76 +146,57 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Retrieves a [Player] by their [id]. Future getPlayerById({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final row = await query.getSingle(); + final result = await query.getSingle(); return Player( - id: row.id, - name: row.name, - description: row.description, - createdAt: row.createdAt, - nameCount: row.nameCount, + id: result.id, + name: result.name, + description: result.description, + createdAt: result.createdAt, + nameCount: result.nameCount, ); } /* Update */ /// Updates the name of the player with the given [playerId] to [name]. - /// - /// Keeps the `nameCount` values of the affected name groups consistent: - /// - The renamed player gets a fresh `nameCount` for the new name group. - /// - All players in the previous name group whose `nameCount` was greater - /// than the removed one get decremented by 1, so the numbering stays - /// contiguous (1..N) in `createdAt` order. - /// - If only one player remains in the previous name group, their - /// `nameCount` is reset to 0. Future updatePlayerName({ required String playerId, required String name, }) async { - return transaction(() async { - final previousPlayer = await (select( - playerTable, - )..where((tbl) => tbl.id.equals(playerId))).getSingleOrNull(); - if (previousPlayer == null) return false; + // Get previous name and name count for the player before updating + final previousPlayerName = + await (select(playerTable)..where((p) => p.id.equals(playerId))) + .map((row) => row.name) + .getSingleOrNull() ?? + ''; + final previousNameCount = await getNameCount(name: previousPlayerName); - final previousName = previousPlayer.name; - final previousCount = previousPlayer.nameCount; + final rowsAffected = + await (update(playerTable)..where((p) => p.id.equals(playerId))).write( + PlayerTableCompanion(name: Value(name)), + ); - // Determine the nameCount for the renamed player in the new group. - final newNameCount = await _processNameCount(name: name); + // Update name count for the new name + final count = await calculateNameCount(name: name); + if (count > 0) { + await (update(playerTable)..where((p) => p.name.equals(name))).write( + PlayerTableCompanion(nameCount: Value(count)), + ); + } - final rowsAffected = - await (update( - playerTable, - )..where((tbl) => tbl.id.equals(playerId))).write( - PlayerTableCompanion( - name: Value(name), - nameCount: Value(newNameCount), - ), - ); - - // Consolidate the previous name group. - final remainingCount = await getNameCount(name: previousName); - - if (remainingCount == 1) { - // Only one player left - await (update(playerTable)..where((p) => p.name.equals(previousName))) - .write(const PlayerTableCompanion(nameCount: Value(0))); - } else if (remainingCount > 1 && previousCount > 0) { - // Shift every player above the gap down by one to keep numbering in order. - await (update(playerTable)..where( - (tbl) => - tbl.name.equals(previousName) & - tbl.nameCount.isBiggerThanValue(previousCount), - )) - .write( - PlayerTableCompanion.custom( - nameCount: playerTable.nameCount - const Constant(1), - ), - ); + if (previousNameCount > 0) { + // Get the player with that name and the hightest nameCount, and update their nameCount to previousNameCount + final player = await getPlayerWithHighestNameCount( + name: previousPlayerName, + ); + if (player != null) { + await updateNameCount( + playerId: player.id, + nameCount: previousNameCount, + ); } - - return rowsAffected > 0; - }); + } + return rowsAffected > 0; } /// Updates the description of the player with the given [playerId] to @@ -226,8 +207,9 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String description, }) async { final rowsAffected = - await (update(playerTable)..where((tbl) => tbl.id.equals(playerId))) - .write(PlayerTableCompanion(description: Value(description))); + await (update(playerTable)..where((g) => g.id.equals(playerId))).write( + PlayerTableCompanion(description: Value(description)), + ); return rowsAffected > 0; } @@ -236,7 +218,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Deletes the player with the given [id] from the database. /// Returns `true` if the player was deleted, `false` if the player did not exist. Future deletePlayer({required String playerId}) async { - final query = delete(playerTable)..where((tbl) => tbl.id.equals(playerId)); + final query = delete(playerTable)..where((p) => p.id.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -244,10 +226,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /* Name count management */ /// Retrieves the count of players with the given [name]. - /// Returns the highest name count if players with the same name exist, - /// otherwise `null`. Future getNameCount({required String name}) async { - final query = select(playerTable)..where((tbl) => tbl.name.equals(name)); + final query = select(playerTable)..where((p) => p.name.equals(name)); final result = await query.get(); return result.length; } @@ -258,7 +238,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String playerId, required int nameCount, }) async { - final query = update(playerTable)..where((tbl) => tbl.id.equals(playerId)); + final query = update(playerTable)..where((p) => p.id.equals(playerId)); final rowsAffected = await query.write( PlayerTableCompanion(nameCount: Value(nameCount)), ); @@ -268,8 +248,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future getPlayerWithHighestNameCount({required String name}) async { final query = select(playerTable) - ..where((tbl) => tbl.name.equals(name)) - ..orderBy([(tbl) => OrderingTerm.desc(tbl.nameCount)]) + ..where((p) => p.name.equals(name)) + ..orderBy([(p) => OrderingTerm.desc(p.nameCount)]) ..limit(1); final result = await query.getSingleOrNull(); if (result != null) { @@ -284,47 +264,34 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return null; } - /// Processes the name count for a new player with the given [name]. - ///- 0 Player: returning 0 - ///- 1 Player: returning 2, and initializes the nameCount for the existing player to 1 - ///- Other: returning the existing count + 1 - Future _processNameCount({required String name}) async { - final nameCount = await calculateNameCount(name: name); - if (nameCount == 2) { - // If one other player exists with the same name, initialize the nameCount - await initializeNameCount(name: name); - } - return nameCount; - } - @visibleForTesting - /// Calculates the name count for a new player with the given [name]. - /// - 0 Players: Name count is 0 - /// - 1 Player: Name count is 2 (since the existing player will be 1) - /// - Other: Name count is the existing count + 1 Future calculateNameCount({required String name}) async { final count = await getNameCount(name: name); final int nameCount; - if (count == 0) { - // If no other players exist with the same name, the returned nameCount is 0 - nameCount = 0; - } else if (count == 1) { - // If one other player with the name count exists, the returned name count is 2 + if (count == 1) { + // If one other player exists with the same name, initialize the nameCount + await initializeNameCount(name: name); + // And for the new player, set nameCount to 2 nameCount = 2; - } else { + } else if (count > 1) { // If more than one player exists with the same name, just increment // the nameCount for the new player nameCount = count + 1; + } else { + // If no other players exist with the same name, set nameCount to 0 + nameCount = 0; } + return nameCount; } @visibleForTesting Future initializeNameCount({required String name}) async { final rowsAffected = - await (update(playerTable)..where((tbl) => tbl.name.equals(name))) - .write(const PlayerTableCompanion(nameCount: Value(1))); + await (update(playerTable)..where((p) => p.name.equals(name))).write( + const PlayerTableCompanion(nameCount: Value(1)), + ); return rowsAffected > 0; } diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index b48dc23..48c5653 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -39,25 +39,18 @@ class PlayerGroupDao extends DatabaseAccessor /// Retrieves all players belonging to a specific group by [groupId]. Future> getPlayersOfGroup({required String groupId}) async { - final query = select(playerGroupTable).join([ - innerJoin( - playerTable, - playerTable.id.equalsExp(playerGroupTable.playerId), - ), - ])..where(playerGroupTable.groupId.equals(groupId)); + final query = select(playerGroupTable) + ..where((pG) => pG.groupId.equals(groupId)); + final result = await query.get(); - final result = await query.map((row) => row.readTable(playerTable)).get(); - return result - .map( - (row) => Player( - id: row.id, - createdAt: row.createdAt, - name: row.name, - nameCount: row.nameCount, - description: row.description, - ), - ) - .toList(); + List groupMembers = List.empty(growable: true); + + for (var entry in result) { + final player = await db.playerDao.getPlayerById(playerId: entry.playerId); + groupMembers.add(player); + } + + return groupMembers; } /// Checks if a player with [playerId] is in the group with [groupId]. @@ -67,9 +60,7 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = select(playerGroupTable) - ..where( - (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), - ); + ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); final result = await query.getSingleOrNull(); return result != null; } @@ -90,7 +81,7 @@ class PlayerGroupDao extends DatabaseAccessor await db.transaction(() async { // Remove all existing players from the group final deleteQuery = delete(db.playerGroupTable) - ..where((tbl) => tbl.groupId.equals(groupId)); + ..where((p) => p.groupId.equals(groupId)); await deleteQuery.go(); // Add new players to the player table if they don't exist @@ -130,9 +121,7 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = delete(playerGroupTable) - ..where( - (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), - ); + ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index 912cfcc..d119468 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -40,7 +40,7 @@ class PlayerMatchDao extends DatabaseAccessor await (selectOnly(playerMatchTable) ..where(playerMatchTable.matchId.equals(matchId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((tbl) => tbl.read(playerMatchTable.playerId.count())) + .map((row) => row.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -56,7 +56,7 @@ class PlayerMatchDao extends DatabaseAccessor ..where(playerMatchTable.matchId.equals(matchId)) ..where(playerMatchTable.playerId.equals(playerId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((tbl) => tbl.read(playerMatchTable.playerId.count())) + .map((row) => row.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -66,7 +66,7 @@ class PlayerMatchDao extends DatabaseAccessor Future> getPlayersOfMatch({required String matchId}) async { final result = await (select( playerMatchTable, - )..where((tbl) => tbl.matchId.equals(matchId))).get(); + )..where((p) => p.matchId.equals(matchId))).get(); if (result.isEmpty) return []; @@ -85,8 +85,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final result = await (select(playerMatchTable) - ..where((tbl) => tbl.matchId.equals(matchId)) - ..where((tbl) => tbl.teamId.equals(teamId))) + ..where((p) => p.matchId.equals(matchId)) + ..where((p) => p.teamId.equals(teamId))) .get(); if (result.isEmpty) return []; @@ -109,8 +109,7 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final rowsAffected = await (update(playerMatchTable)..where( - (tbl) => - tbl.matchId.equals(matchId) & tbl.playerId.equals(playerId), + (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), )) .write(PlayerMatchTableCompanion(teamId: Value(teamId))); return rowsAffected > 0; @@ -144,9 +143,9 @@ class PlayerMatchDao extends DatabaseAccessor // Remove old players if (playersToRemove.isNotEmpty) { await (delete(playerMatchTable)..where( - (tbl) => - tbl.matchId.equals(matchId) & - tbl.playerId.isIn(playersToRemove.toList()), + (pg) => + pg.matchId.equals(matchId) & + pg.playerId.isIn(playersToRemove.toList()), )) .go(); } @@ -183,8 +182,8 @@ class PlayerMatchDao extends DatabaseAccessor required String playerId, }) async { final query = delete(playerMatchTable) - ..where((tbl) => tbl.matchId.equals(matchId)) - ..where((tbl) => tbl.playerId.equals(playerId)); + ..where((pg) => pg.matchId.equals(matchId)) + ..where((pg) => pg.playerId.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/score_entry_dao.dart b/lib/data/dao/score_entry_dao.dart index 276e8fd..830135d 100644 --- a/lib/data/dao/score_entry_dao.dart +++ b/lib/data/dao/score_entry_dao.dart @@ -70,10 +70,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = select(scoreEntryTable) ..where( - (tbl) => - tbl.playerId.equals(playerId) & - tbl.matchId.equals(matchId) & - tbl.roundNumber.equals(roundNumber), + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(roundNumber), ); final result = await query.getSingleOrNull(); @@ -91,7 +91,7 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((tbl) => tbl.matchId.equals(matchId)); + ..where((s) => s.matchId.equals(matchId)); final result = await query.get(); final Map scoresByPlayer = {}; @@ -113,10 +113,8 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where( - (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), - ) - ..orderBy([(tbl) => OrderingTerm.asc(tbl.roundNumber)]); + ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)) + ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); final result = await query.get(); return result .map( @@ -138,8 +136,8 @@ class ScoreEntryDao extends DatabaseAccessor final query = selectOnly(scoreEntryTable) ..where(scoreEntryTable.matchId.equals(matchId)) ..addColumns([scoreEntryTable.roundNumber.max()]); - final row = await query.getSingle(); - return row.read(scoreEntryTable.roundNumber.max()); + final result = await query.getSingle(); + return result.read(scoreEntryTable.roundNumber.max()); } /// Aggregates the total score for a player in a match by summing all their @@ -168,10 +166,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final rowsAffected = await (update(scoreEntryTable)..where( - (tbl) => - tbl.playerId.equals(playerId) & - tbl.matchId.equals(matchId) & - tbl.roundNumber.equals(entry.roundNumber), + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(entry.roundNumber), )) .write( ScoreEntryTableCompanion( @@ -192,10 +190,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = delete(scoreEntryTable) ..where( - (tbl) => - tbl.playerId.equals(playerId) & - tbl.matchId.equals(matchId) & - tbl.roundNumber.equals(roundNumber), + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(roundNumber), ); final rowsAffected = await query.go(); return rowsAffected > 0; @@ -203,7 +201,7 @@ class ScoreEntryDao extends DatabaseAccessor Future deleteAllScoresForMatch({required String matchId}) async { final query = delete(scoreEntryTable) - ..where((tbl) => tbl.matchId.equals(matchId)); + ..where((s) => s.matchId.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -213,9 +211,7 @@ class ScoreEntryDao extends DatabaseAccessor required String playerId, }) async { final query = delete(scoreEntryTable) - ..where( - (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), - ); + ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart deleted file mode 100644 index 092ceb0..0000000 --- a/lib/data/dao/statistic_dao.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:drift/drift.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/db/tables/statistic_table.dart'; -import 'package:tallee/data/models/statistic.dart'; - -part 'statistic_dao.g.dart'; - -@DriftAccessor(tables: [StatisticTable]) -class StatisticDao extends DatabaseAccessor - with _$StatisticDaoMixin { - StatisticDao(super.db); - - /* Create */ - - Future addStatistic({required Statistic statistic}) async { - await into(statisticTable).insert( - StatisticTableCompanion.insert( - id: statistic.id, - type: statistic.type.name, - timeframe: Value(statistic.timeframe?.name), - displayCount: Value(statistic.displayCount), - ), - mode: InsertMode.insertOrReplace, - ); - - await db.statisticScopeDao.addStatisticScopes( - statisticId: statistic.id, - scopes: statistic.scopes, - ); - - if (statistic.selectedGroups != null) { - await db.statisticGroupDao.addStatisticGroups( - statisticId: statistic.id, - groups: statistic.selectedGroups!, - ); - } - - if (statistic.selectedGames != null) { - await db.statisticGameDao.addStatisticGames( - statisticId: statistic.id, - games: statistic.selectedGames!, - ); - } - - return true; - } - - /* Read */ - - Future getStatisticById(String statisticId) async { - final query = select(statisticTable); - final row = await query.getSingleOrNull(); - if (row != null) { - final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); - final games = await db.statisticGameDao.getGamesForStatistic(row.id); - final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); - - return Statistic( - type: StatisticType.values.firstWhere((type) => type.name == row.type), - scopes: scopes, - timeframe: Timeframe.values.firstWhereOrNull( - (t) => t.name == row.timeframe, - ), - selectedGroups: groups, - selectedGames: games, - displayCount: row.displayCount, - id: row.id, - ); - } - return null; - } - - /// Retrieves all statistics from the database, including their associated groups and games. - Future> getAllStatistics() async { - final query = select(statisticTable); - final result = await query.get(); - return Future.wait( - result.map((row) async { - final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); - final games = await db.statisticGameDao.getGamesForStatistic(row.id); - final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); - - return Statistic( - type: StatisticType.values.firstWhere( - (type) => type.name == row.type, - ), - scopes: scopes, - timeframe: Timeframe.values.firstWhereOrNull( - (t) => t.name == row.timeframe, - ), - selectedGroups: groups, - selectedGames: games, - displayCount: row.displayCount, - id: row.id, - ); - }), - ); - } - - /* Update */ - - Future updateDisplayCount(String statisticId, int displayCount) async { - final rowsUpdated = - await (update(statisticTable) - ..where((tbl) => tbl.id.equals(statisticId))) - .write(StatisticTableCompanion(displayCount: Value(displayCount))); - - return rowsUpdated > 0; - } - - /* Delete */ - - Future deleteStatistic(String statisticId) async { - final rowsDeleted = await (delete( - statisticTable, - )..where((tbl) => tbl.id.equals(statisticId))).go(); - - return rowsDeleted > 0; - } - - Future deleteAllStatistics() async { - final rowsDeleted = await delete(statisticTable).go(); - return rowsDeleted > 0; - } -} diff --git a/lib/data/dao/statistic_dao.g.dart b/lib/data/dao/statistic_dao.g.dart deleted file mode 100644 index 0ce36e1..0000000 --- a/lib/data/dao/statistic_dao.g.dart +++ /dev/null @@ -1,19 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'statistic_dao.dart'; - -// ignore_for_file: type=lint -mixin _$StatisticDaoMixin on DatabaseAccessor { - $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; - StatisticDaoManager get managers => StatisticDaoManager(this); -} - -class StatisticDaoManager { - final _$StatisticDaoMixin _db; - StatisticDaoManager(this._db); - $$StatisticTableTableTableManager get statisticTable => - $$StatisticTableTableTableManager( - _db.attachedDatabase, - _db.statisticTable, - ); -} diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart deleted file mode 100644 index d546ce0..0000000 --- a/lib/data/dao/statistic_game_dao.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/db/tables/statistic_game_table.dart'; -import 'package:tallee/data/models/game.dart'; - -part 'statistic_game_dao.g.dart'; - -@DriftAccessor(tables: [StatisticGameTable]) -class StatisticGameDao extends DatabaseAccessor - with _$StatisticGameDaoMixin { - StatisticGameDao(super.db); - - /// Retrieves a list of games associated with a specific statistic. - Future?> getGamesForStatistic(String statisticId) async { - final query = select(statisticGameTable).join([ - innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)), - ])..where(statisticGameTable.statisticId.equals(statisticId)); - - final results = await query.map((row) => row.readTable(gameTable)).get(); - if (results.isEmpty) return null; - return results - .map( - (row) => Game( - id: row.id, - name: row.name, - ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), - description: row.description, - color: AppColor.values.firstWhere((e) => e.name == row.color), - icon: row.icon, - createdAt: row.createdAt, - ), - ) - .toList(); - } - - Future addStatisticGames({ - required String statisticId, - required List games, - }) { - final entries = games - .map( - (game) => StatisticGameTableCompanion.insert( - statisticId: statisticId, - gameId: game.id, - ), - ) - .toList(); - - return batch((batch) { - batch.insertAll( - statisticGameTable, - entries, - mode: InsertMode.insertOrReplace, - ); - }).then((_) => true).catchError((error) { - print('Error adding statistic games: $error'); - return false; - }); - } -} diff --git a/lib/data/dao/statistic_game_dao.g.dart b/lib/data/dao/statistic_game_dao.g.dart deleted file mode 100644 index d6ee984..0000000 --- a/lib/data/dao/statistic_game_dao.g.dart +++ /dev/null @@ -1,29 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'statistic_game_dao.dart'; - -// ignore_for_file: type=lint -mixin _$StatisticGameDaoMixin on DatabaseAccessor { - $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; - $GameTableTable get gameTable => attachedDatabase.gameTable; - $StatisticGameTableTable get statisticGameTable => - attachedDatabase.statisticGameTable; - StatisticGameDaoManager get managers => StatisticGameDaoManager(this); -} - -class StatisticGameDaoManager { - final _$StatisticGameDaoMixin _db; - StatisticGameDaoManager(this._db); - $$StatisticTableTableTableManager get statisticTable => - $$StatisticTableTableTableManager( - _db.attachedDatabase, - _db.statisticTable, - ); - $$GameTableTableTableManager get gameTable => - $$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable); - $$StatisticGameTableTableTableManager get statisticGameTable => - $$StatisticGameTableTableTableManager( - _db.attachedDatabase, - _db.statisticGameTable, - ); -} diff --git a/lib/data/dao/statistic_group_dao.dart b/lib/data/dao/statistic_group_dao.dart deleted file mode 100644 index 449b6a8..0000000 --- a/lib/data/dao/statistic_group_dao.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/db/tables/group_table.dart'; -import 'package:tallee/data/db/tables/statistic_group_table.dart'; -import 'package:tallee/data/models/group.dart'; - -part 'statistic_group_dao.g.dart'; - -@DriftAccessor(tables: [StatisticGroupTable, GroupTable]) -class StatisticGroupDao extends DatabaseAccessor - with _$StatisticGroupDaoMixin { - StatisticGroupDao(super.db); - - /// Retrieves a list of groups associated with a specific statistic. - Future?> getGroupsForStatistic(String statisticId) async { - final query = select(statisticGroupTable).join([ - innerJoin( - groupTable, - groupTable.id.equalsExp(statisticGroupTable.groupId), - ), - ])..where(statisticGroupTable.statisticId.equals(statisticId)); - - final results = await query.map((row) => row.readTable(groupTable)).get(); - if (results.isEmpty) return null; - final groups = await Future.wait( - results.map((result) async { - final groupMembers = await db.playerGroupDao.getPlayersOfGroup( - groupId: result.id, - ); - return Group( - id: result.id, - createdAt: result.createdAt, - name: result.name, - description: result.description, - members: groupMembers, - ); - }), - ); - - return groups; - } - - Future addStatisticGroups({ - required String statisticId, - required List groups, - }) async { - final entries = groups - .map( - (group) => StatisticGroupTableCompanion.insert( - statisticId: statisticId, - groupId: group.id, - ), - ) - .toList(); - - return batch((batch) { - batch.insertAll( - statisticGroupTable, - entries, - mode: InsertMode.insertOrReplace, - ); - }).then((_) => true).catchError((error) { - print('Error adding statistic groups: $error'); - return false; - }); - } -} diff --git a/lib/data/dao/statistic_group_dao.g.dart b/lib/data/dao/statistic_group_dao.g.dart deleted file mode 100644 index 57a83c5..0000000 --- a/lib/data/dao/statistic_group_dao.g.dart +++ /dev/null @@ -1,29 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'statistic_group_dao.dart'; - -// ignore_for_file: type=lint -mixin _$StatisticGroupDaoMixin on DatabaseAccessor { - $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; - $GroupTableTable get groupTable => attachedDatabase.groupTable; - $StatisticGroupTableTable get statisticGroupTable => - attachedDatabase.statisticGroupTable; - StatisticGroupDaoManager get managers => StatisticGroupDaoManager(this); -} - -class StatisticGroupDaoManager { - final _$StatisticGroupDaoMixin _db; - StatisticGroupDaoManager(this._db); - $$StatisticTableTableTableManager get statisticTable => - $$StatisticTableTableTableManager( - _db.attachedDatabase, - _db.statisticTable, - ); - $$GroupTableTableTableManager get groupTable => - $$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable); - $$StatisticGroupTableTableTableManager get statisticGroupTable => - $$StatisticGroupTableTableTableManager( - _db.attachedDatabase, - _db.statisticGroupTable, - ); -} diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart deleted file mode 100644 index eb286af..0000000 --- a/lib/data/dao/statistic_scope_dao.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/db/tables/statistic_scope_table.dart'; - -part 'statistic_scope_dao.g.dart'; - -@DriftAccessor(tables: [StatisticScopeTable]) -class StatisticScopeDao extends DatabaseAccessor - with _$StatisticScopeDaoMixin { - StatisticScopeDao(super.db); - - /// Retrieves a list of statistic scopes associated with a specific statistic ID. - Future> getScopeForStatistic(String statisticId) async { - final query = select(statisticScopeTable) - ..where((tbl) => tbl.statisticId.equals(statisticId)); - - final result = await query.get(); - return result - .map( - (row) => StatisticScope.values.firstWhere( - (e) => e.name == row.scope, - orElse: () => throw Exception( - 'Invalid scope value: ${row.scope} for statistic ID: $statisticId', - ), - ), - ) - .toList(); - } - - Future addStatisticScopes({ - required String statisticId, - required List scopes, - }) async { - final entries = scopes - .map( - (scope) => StatisticScopeTableCompanion.insert( - statisticId: statisticId, - scope: scope.name, - ), - ) - .toList(); - - return batch((batch) { - batch.insertAll( - statisticScopeTable, - entries, - mode: InsertMode.insertOrReplace, - ); - }).then((_) => true).catchError((error) { - print('Error adding statistic scopes: $error'); - return false; - }); - } -} diff --git a/lib/data/dao/statistic_scope_dao.g.dart b/lib/data/dao/statistic_scope_dao.g.dart deleted file mode 100644 index adaa171..0000000 --- a/lib/data/dao/statistic_scope_dao.g.dart +++ /dev/null @@ -1,26 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'statistic_scope_dao.dart'; - -// ignore_for_file: type=lint -mixin _$StatisticScopeDaoMixin on DatabaseAccessor { - $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; - $StatisticScopeTableTable get statisticScopeTable => - attachedDatabase.statisticScopeTable; - StatisticScopeDaoManager get managers => StatisticScopeDaoManager(this); -} - -class StatisticScopeDaoManager { - final _$StatisticScopeDaoMixin _db; - StatisticScopeDaoManager(this._db); - $$StatisticTableTableTableManager get statisticTable => - $$StatisticTableTableTableManager( - _db.attachedDatabase, - _db.statisticTable, - ); - $$StatisticScopeTableTableTableManager get statisticScopeTable => - $$StatisticScopeTableTableTableManager( - _db.attachedDatabase, - _db.statisticScopeTable, - ); -} diff --git a/lib/data/dao/team_dao.dart b/lib/data/dao/team_dao.dart index 333db68..cba68fb 100644 --- a/lib/data/dao/team_dao.dart +++ b/lib/data/dao/team_dao.dart @@ -86,7 +86,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future getTeamCount() async { final count = await (selectOnly(teamTable)..addColumns([teamTable.id.count()])) - .map((tbl) => tbl.read(teamTable.id.count())) + .map((row) => row.read(teamTable.id.count())) .getSingle(); return count ?? 0; } @@ -95,8 +95,8 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Returns `true` if the team exists, `false` otherwise. Future teamExists({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final row = await query.getSingleOrNull(); - return row != null; + final result = await query.getSingleOrNull(); + return result != null; } /// Retrieves all teams from the database. @@ -119,12 +119,12 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Retrieves a [Team] by its [teamId], including its members. Future getTeamById({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final row = await query.getSingle(); + final result = await query.getSingle(); final members = await _getTeamMembers(teamId: teamId); return Team( - id: row.id, - name: row.name, - createdAt: row.createdAt, + id: result.id, + name: result.name, + createdAt: result.createdAt, members: members, ); } @@ -133,13 +133,13 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future> _getTeamMembers({required String teamId}) async { // Get all player_match entries with this teamId final playerMatchQuery = select(db.playerMatchTable) - ..where((tbl) => tbl.teamId.equals(teamId)); + ..where((pm) => pm.teamId.equals(teamId)); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; // Get unique player IDs - final playerIds = playerMatches.map((tbl) => tbl.playerId).toSet(); + final playerIds = playerMatches.map((pm) => pm.playerId).toSet(); // Fetch all players final players = await Future.wait( @@ -156,7 +156,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { required String name, }) async { final rowsAffected = - await (update(teamTable)..where((tbl) => tbl.id.equals(teamId))).write( + await (update(teamTable)..where((t) => t.id.equals(teamId))).write( TeamTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -175,7 +175,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Deletes the team with the given [teamId] from the database. /// Returns `true` if the team was deleted, `false` otherwise. Future deleteTeam({required String teamId}) async { - final query = delete(teamTable)..where((tbl) => tbl.id.equals(teamId)); + final query = delete(teamTable)..where((t) => t.id.equals(teamId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index 792da0e..a7e9c1d 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -8,10 +8,6 @@ import 'package:tallee/data/dao/player_dao.dart'; import 'package:tallee/data/dao/player_group_dao.dart'; import 'package:tallee/data/dao/player_match_dao.dart'; import 'package:tallee/data/dao/score_entry_dao.dart'; -import 'package:tallee/data/dao/statistic_dao.dart'; -import 'package:tallee/data/dao/statistic_game_dao.dart'; -import 'package:tallee/data/dao/statistic_group_dao.dart'; -import 'package:tallee/data/dao/statistic_scope_dao.dart'; import 'package:tallee/data/dao/team_dao.dart'; import 'package:tallee/data/db/tables/game_table.dart'; import 'package:tallee/data/db/tables/group_table.dart'; @@ -20,10 +16,6 @@ import 'package:tallee/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_match_table.dart'; import 'package:tallee/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/score_entry_table.dart'; -import 'package:tallee/data/db/tables/statistic_game_table.dart'; -import 'package:tallee/data/db/tables/statistic_group_table.dart'; -import 'package:tallee/data/db/tables/statistic_scope_table.dart'; -import 'package:tallee/data/db/tables/statistic_table.dart'; import 'package:tallee/data/db/tables/team_table.dart'; part 'database.g.dart'; @@ -38,10 +30,6 @@ part 'database.g.dart'; GameTable, TeamTable, ScoreEntryTable, - StatisticTable, - StatisticScopeTable, - StatisticGameTable, - StatisticGroupTable, ], daos: [ PlayerDao, @@ -52,10 +40,6 @@ part 'database.g.dart'; GameDao, ScoreEntryDao, TeamDao, - StatisticDao, - StatisticScopeDao, - StatisticGameDao, - StatisticGroupDao, ], ) class AppDatabase extends _$AppDatabase { diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index 3a9d277..c8d0faa 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -2732,1020 +2732,6 @@ class ScoreEntryTableCompanion extends UpdateCompanion { } } -class $StatisticTableTable extends StatisticTable - with TableInfo<$StatisticTableTable, StatisticTableData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StatisticTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _idMeta = const VerificationMeta('id'); - @override - late final GeneratedColumn id = GeneratedColumn( - 'id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - ); - static const VerificationMeta _typeMeta = const VerificationMeta('type'); - @override - late final GeneratedColumn type = GeneratedColumn( - 'type', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - ); - static const VerificationMeta _timeframeMeta = const VerificationMeta( - 'timeframe', - ); - @override - late final GeneratedColumn timeframe = GeneratedColumn( - 'timeframe', - aliasedName, - true, - type: DriftSqlType.string, - requiredDuringInsert: false, - ); - static const VerificationMeta _displayCountMeta = const VerificationMeta( - 'displayCount', - ); - @override - late final GeneratedColumn displayCount = GeneratedColumn( - 'display_count', - aliasedName, - false, - type: DriftSqlType.int, - requiredDuringInsert: false, - defaultValue: const Constant(5), - ); - @override - List get $columns => [id, type, timeframe, displayCount]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'statistic_table'; - @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); - } else if (isInserting) { - context.missing(_idMeta); - } - if (data.containsKey('type')) { - context.handle( - _typeMeta, - type.isAcceptableOrUnknown(data['type']!, _typeMeta), - ); - } else if (isInserting) { - context.missing(_typeMeta); - } - if (data.containsKey('timeframe')) { - context.handle( - _timeframeMeta, - timeframe.isAcceptableOrUnknown(data['timeframe']!, _timeframeMeta), - ); - } - if (data.containsKey('display_count')) { - context.handle( - _displayCountMeta, - displayCount.isAcceptableOrUnknown( - data['display_count']!, - _displayCountMeta, - ), - ); - } - return context; - } - - @override - Set get $primaryKey => {id}; - @override - StatisticTableData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StatisticTableData( - id: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}id'], - )!, - type: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}type'], - )!, - timeframe: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}timeframe'], - ), - displayCount: attachedDatabase.typeMapping.read( - DriftSqlType.int, - data['${effectivePrefix}display_count'], - )!, - ); - } - - @override - $StatisticTableTable createAlias(String alias) { - return $StatisticTableTable(attachedDatabase, alias); - } -} - -class StatisticTableData extends DataClass - implements Insertable { - final String id; - final String type; - final String? timeframe; - final int displayCount; - const StatisticTableData({ - required this.id, - required this.type, - this.timeframe, - required this.displayCount, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['id'] = Variable(id); - map['type'] = Variable(type); - if (!nullToAbsent || timeframe != null) { - map['timeframe'] = Variable(timeframe); - } - map['display_count'] = Variable(displayCount); - return map; - } - - StatisticTableCompanion toCompanion(bool nullToAbsent) { - return StatisticTableCompanion( - id: Value(id), - type: Value(type), - timeframe: timeframe == null && nullToAbsent - ? const Value.absent() - : Value(timeframe), - displayCount: Value(displayCount), - ); - } - - factory StatisticTableData.fromJson( - Map json, { - ValueSerializer? serializer, - }) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StatisticTableData( - id: serializer.fromJson(json['id']), - type: serializer.fromJson(json['type']), - timeframe: serializer.fromJson(json['timeframe']), - displayCount: serializer.fromJson(json['displayCount']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'id': serializer.toJson(id), - 'type': serializer.toJson(type), - 'timeframe': serializer.toJson(timeframe), - 'displayCount': serializer.toJson(displayCount), - }; - } - - StatisticTableData copyWith({ - String? id, - String? type, - Value timeframe = const Value.absent(), - int? displayCount, - }) => StatisticTableData( - id: id ?? this.id, - type: type ?? this.type, - timeframe: timeframe.present ? timeframe.value : this.timeframe, - displayCount: displayCount ?? this.displayCount, - ); - StatisticTableData copyWithCompanion(StatisticTableCompanion data) { - return StatisticTableData( - id: data.id.present ? data.id.value : this.id, - type: data.type.present ? data.type.value : this.type, - timeframe: data.timeframe.present ? data.timeframe.value : this.timeframe, - displayCount: data.displayCount.present - ? data.displayCount.value - : this.displayCount, - ); - } - - @override - String toString() { - return (StringBuffer('StatisticTableData(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('timeframe: $timeframe, ') - ..write('displayCount: $displayCount') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(id, type, timeframe, displayCount); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StatisticTableData && - other.id == this.id && - other.type == this.type && - other.timeframe == this.timeframe && - other.displayCount == this.displayCount); -} - -class StatisticTableCompanion extends UpdateCompanion { - final Value id; - final Value type; - final Value timeframe; - final Value displayCount; - final Value rowid; - const StatisticTableCompanion({ - this.id = const Value.absent(), - this.type = const Value.absent(), - this.timeframe = const Value.absent(), - this.displayCount = const Value.absent(), - this.rowid = const Value.absent(), - }); - StatisticTableCompanion.insert({ - required String id, - required String type, - this.timeframe = const Value.absent(), - this.displayCount = const Value.absent(), - this.rowid = const Value.absent(), - }) : id = Value(id), - type = Value(type); - static Insertable custom({ - Expression? id, - Expression? type, - Expression? timeframe, - Expression? displayCount, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (id != null) 'id': id, - if (type != null) 'type': type, - if (timeframe != null) 'timeframe': timeframe, - if (displayCount != null) 'display_count': displayCount, - if (rowid != null) 'rowid': rowid, - }); - } - - StatisticTableCompanion copyWith({ - Value? id, - Value? type, - Value? timeframe, - Value? displayCount, - Value? rowid, - }) { - return StatisticTableCompanion( - id: id ?? this.id, - type: type ?? this.type, - timeframe: timeframe ?? this.timeframe, - displayCount: displayCount ?? this.displayCount, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (id.present) { - map['id'] = Variable(id.value); - } - if (type.present) { - map['type'] = Variable(type.value); - } - if (timeframe.present) { - map['timeframe'] = Variable(timeframe.value); - } - if (displayCount.present) { - map['display_count'] = Variable(displayCount.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StatisticTableCompanion(') - ..write('id: $id, ') - ..write('type: $type, ') - ..write('timeframe: $timeframe, ') - ..write('displayCount: $displayCount, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $StatisticScopeTableTable extends StatisticScopeTable - with TableInfo<$StatisticScopeTableTable, StatisticScopeTableData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StatisticScopeTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _statisticIdMeta = const VerificationMeta( - 'statisticId', - ); - @override - late final GeneratedColumn statisticId = GeneratedColumn( - 'statistic_id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES statistic_table (id) ON DELETE CASCADE', - ), - ); - static const VerificationMeta _scopeMeta = const VerificationMeta('scope'); - @override - late final GeneratedColumn scope = GeneratedColumn( - 'scope', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - ); - @override - List get $columns => [statisticId, scope]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'statistic_scope_table'; - @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('statistic_id')) { - context.handle( - _statisticIdMeta, - statisticId.isAcceptableOrUnknown( - data['statistic_id']!, - _statisticIdMeta, - ), - ); - } else if (isInserting) { - context.missing(_statisticIdMeta); - } - if (data.containsKey('scope')) { - context.handle( - _scopeMeta, - scope.isAcceptableOrUnknown(data['scope']!, _scopeMeta), - ); - } else if (isInserting) { - context.missing(_scopeMeta); - } - return context; - } - - @override - Set get $primaryKey => {statisticId, scope}; - @override - StatisticScopeTableData map( - Map data, { - String? tablePrefix, - }) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StatisticScopeTableData( - statisticId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}statistic_id'], - )!, - scope: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}scope'], - )!, - ); - } - - @override - $StatisticScopeTableTable createAlias(String alias) { - return $StatisticScopeTableTable(attachedDatabase, alias); - } -} - -class StatisticScopeTableData extends DataClass - implements Insertable { - final String statisticId; - final String scope; - const StatisticScopeTableData({ - required this.statisticId, - required this.scope, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['statistic_id'] = Variable(statisticId); - map['scope'] = Variable(scope); - return map; - } - - StatisticScopeTableCompanion toCompanion(bool nullToAbsent) { - return StatisticScopeTableCompanion( - statisticId: Value(statisticId), - scope: Value(scope), - ); - } - - factory StatisticScopeTableData.fromJson( - Map json, { - ValueSerializer? serializer, - }) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StatisticScopeTableData( - statisticId: serializer.fromJson(json['statisticId']), - scope: serializer.fromJson(json['scope']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'statisticId': serializer.toJson(statisticId), - 'scope': serializer.toJson(scope), - }; - } - - StatisticScopeTableData copyWith({String? statisticId, String? scope}) => - StatisticScopeTableData( - statisticId: statisticId ?? this.statisticId, - scope: scope ?? this.scope, - ); - StatisticScopeTableData copyWithCompanion(StatisticScopeTableCompanion data) { - return StatisticScopeTableData( - statisticId: data.statisticId.present - ? data.statisticId.value - : this.statisticId, - scope: data.scope.present ? data.scope.value : this.scope, - ); - } - - @override - String toString() { - return (StringBuffer('StatisticScopeTableData(') - ..write('statisticId: $statisticId, ') - ..write('scope: $scope') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(statisticId, scope); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StatisticScopeTableData && - other.statisticId == this.statisticId && - other.scope == this.scope); -} - -class StatisticScopeTableCompanion - extends UpdateCompanion { - final Value statisticId; - final Value scope; - final Value rowid; - const StatisticScopeTableCompanion({ - this.statisticId = const Value.absent(), - this.scope = const Value.absent(), - this.rowid = const Value.absent(), - }); - StatisticScopeTableCompanion.insert({ - required String statisticId, - required String scope, - this.rowid = const Value.absent(), - }) : statisticId = Value(statisticId), - scope = Value(scope); - static Insertable custom({ - Expression? statisticId, - Expression? scope, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (statisticId != null) 'statistic_id': statisticId, - if (scope != null) 'scope': scope, - if (rowid != null) 'rowid': rowid, - }); - } - - StatisticScopeTableCompanion copyWith({ - Value? statisticId, - Value? scope, - Value? rowid, - }) { - return StatisticScopeTableCompanion( - statisticId: statisticId ?? this.statisticId, - scope: scope ?? this.scope, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (statisticId.present) { - map['statistic_id'] = Variable(statisticId.value); - } - if (scope.present) { - map['scope'] = Variable(scope.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StatisticScopeTableCompanion(') - ..write('statisticId: $statisticId, ') - ..write('scope: $scope, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $StatisticGameTableTable extends StatisticGameTable - with TableInfo<$StatisticGameTableTable, StatisticGameTableData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StatisticGameTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _statisticIdMeta = const VerificationMeta( - 'statisticId', - ); - @override - late final GeneratedColumn statisticId = GeneratedColumn( - 'statistic_id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES statistic_table (id) ON DELETE CASCADE', - ), - ); - static const VerificationMeta _gameIdMeta = const VerificationMeta('gameId'); - @override - late final GeneratedColumn gameId = GeneratedColumn( - 'game_id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES game_table (id) ON DELETE CASCADE', - ), - ); - @override - List get $columns => [statisticId, gameId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'statistic_game_table'; - @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('statistic_id')) { - context.handle( - _statisticIdMeta, - statisticId.isAcceptableOrUnknown( - data['statistic_id']!, - _statisticIdMeta, - ), - ); - } else if (isInserting) { - context.missing(_statisticIdMeta); - } - if (data.containsKey('game_id')) { - context.handle( - _gameIdMeta, - gameId.isAcceptableOrUnknown(data['game_id']!, _gameIdMeta), - ); - } else if (isInserting) { - context.missing(_gameIdMeta); - } - return context; - } - - @override - Set get $primaryKey => {statisticId, gameId}; - @override - StatisticGameTableData map(Map data, {String? tablePrefix}) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StatisticGameTableData( - statisticId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}statistic_id'], - )!, - gameId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}game_id'], - )!, - ); - } - - @override - $StatisticGameTableTable createAlias(String alias) { - return $StatisticGameTableTable(attachedDatabase, alias); - } -} - -class StatisticGameTableData extends DataClass - implements Insertable { - final String statisticId; - final String gameId; - const StatisticGameTableData({ - required this.statisticId, - required this.gameId, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['statistic_id'] = Variable(statisticId); - map['game_id'] = Variable(gameId); - return map; - } - - StatisticGameTableCompanion toCompanion(bool nullToAbsent) { - return StatisticGameTableCompanion( - statisticId: Value(statisticId), - gameId: Value(gameId), - ); - } - - factory StatisticGameTableData.fromJson( - Map json, { - ValueSerializer? serializer, - }) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StatisticGameTableData( - statisticId: serializer.fromJson(json['statisticId']), - gameId: serializer.fromJson(json['gameId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'statisticId': serializer.toJson(statisticId), - 'gameId': serializer.toJson(gameId), - }; - } - - StatisticGameTableData copyWith({String? statisticId, String? gameId}) => - StatisticGameTableData( - statisticId: statisticId ?? this.statisticId, - gameId: gameId ?? this.gameId, - ); - StatisticGameTableData copyWithCompanion(StatisticGameTableCompanion data) { - return StatisticGameTableData( - statisticId: data.statisticId.present - ? data.statisticId.value - : this.statisticId, - gameId: data.gameId.present ? data.gameId.value : this.gameId, - ); - } - - @override - String toString() { - return (StringBuffer('StatisticGameTableData(') - ..write('statisticId: $statisticId, ') - ..write('gameId: $gameId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(statisticId, gameId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StatisticGameTableData && - other.statisticId == this.statisticId && - other.gameId == this.gameId); -} - -class StatisticGameTableCompanion - extends UpdateCompanion { - final Value statisticId; - final Value gameId; - final Value rowid; - const StatisticGameTableCompanion({ - this.statisticId = const Value.absent(), - this.gameId = const Value.absent(), - this.rowid = const Value.absent(), - }); - StatisticGameTableCompanion.insert({ - required String statisticId, - required String gameId, - this.rowid = const Value.absent(), - }) : statisticId = Value(statisticId), - gameId = Value(gameId); - static Insertable custom({ - Expression? statisticId, - Expression? gameId, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (statisticId != null) 'statistic_id': statisticId, - if (gameId != null) 'game_id': gameId, - if (rowid != null) 'rowid': rowid, - }); - } - - StatisticGameTableCompanion copyWith({ - Value? statisticId, - Value? gameId, - Value? rowid, - }) { - return StatisticGameTableCompanion( - statisticId: statisticId ?? this.statisticId, - gameId: gameId ?? this.gameId, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (statisticId.present) { - map['statistic_id'] = Variable(statisticId.value); - } - if (gameId.present) { - map['game_id'] = Variable(gameId.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StatisticGameTableCompanion(') - ..write('statisticId: $statisticId, ') - ..write('gameId: $gameId, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - -class $StatisticGroupTableTable extends StatisticGroupTable - with TableInfo<$StatisticGroupTableTable, StatisticGroupTableData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $StatisticGroupTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _statisticIdMeta = const VerificationMeta( - 'statisticId', - ); - @override - late final GeneratedColumn statisticId = GeneratedColumn( - 'statistic_id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES statistic_table (id) ON DELETE CASCADE', - ), - ); - static const VerificationMeta _groupIdMeta = const VerificationMeta( - 'groupId', - ); - @override - late final GeneratedColumn groupId = GeneratedColumn( - 'group_id', - aliasedName, - false, - type: DriftSqlType.string, - requiredDuringInsert: true, - defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES group_table (id) ON DELETE CASCADE', - ), - ); - @override - List get $columns => [statisticId, groupId]; - @override - String get aliasedName => _alias ?? actualTableName; - @override - String get actualTableName => $name; - static const String $name = 'statistic_group_table'; - @override - VerificationContext validateIntegrity( - Insertable instance, { - bool isInserting = false, - }) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('statistic_id')) { - context.handle( - _statisticIdMeta, - statisticId.isAcceptableOrUnknown( - data['statistic_id']!, - _statisticIdMeta, - ), - ); - } else if (isInserting) { - context.missing(_statisticIdMeta); - } - if (data.containsKey('group_id')) { - context.handle( - _groupIdMeta, - groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), - ); - } else if (isInserting) { - context.missing(_groupIdMeta); - } - return context; - } - - @override - Set get $primaryKey => {statisticId, groupId}; - @override - StatisticGroupTableData map( - Map data, { - String? tablePrefix, - }) { - final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return StatisticGroupTableData( - statisticId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}statistic_id'], - )!, - groupId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}group_id'], - )!, - ); - } - - @override - $StatisticGroupTableTable createAlias(String alias) { - return $StatisticGroupTableTable(attachedDatabase, alias); - } -} - -class StatisticGroupTableData extends DataClass - implements Insertable { - final String statisticId; - final String groupId; - const StatisticGroupTableData({ - required this.statisticId, - required this.groupId, - }); - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['statistic_id'] = Variable(statisticId); - map['group_id'] = Variable(groupId); - return map; - } - - StatisticGroupTableCompanion toCompanion(bool nullToAbsent) { - return StatisticGroupTableCompanion( - statisticId: Value(statisticId), - groupId: Value(groupId), - ); - } - - factory StatisticGroupTableData.fromJson( - Map json, { - ValueSerializer? serializer, - }) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return StatisticGroupTableData( - statisticId: serializer.fromJson(json['statisticId']), - groupId: serializer.fromJson(json['groupId']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'statisticId': serializer.toJson(statisticId), - 'groupId': serializer.toJson(groupId), - }; - } - - StatisticGroupTableData copyWith({String? statisticId, String? groupId}) => - StatisticGroupTableData( - statisticId: statisticId ?? this.statisticId, - groupId: groupId ?? this.groupId, - ); - StatisticGroupTableData copyWithCompanion(StatisticGroupTableCompanion data) { - return StatisticGroupTableData( - statisticId: data.statisticId.present - ? data.statisticId.value - : this.statisticId, - groupId: data.groupId.present ? data.groupId.value : this.groupId, - ); - } - - @override - String toString() { - return (StringBuffer('StatisticGroupTableData(') - ..write('statisticId: $statisticId, ') - ..write('groupId: $groupId') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(statisticId, groupId); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is StatisticGroupTableData && - other.statisticId == this.statisticId && - other.groupId == this.groupId); -} - -class StatisticGroupTableCompanion - extends UpdateCompanion { - final Value statisticId; - final Value groupId; - final Value rowid; - const StatisticGroupTableCompanion({ - this.statisticId = const Value.absent(), - this.groupId = const Value.absent(), - this.rowid = const Value.absent(), - }); - StatisticGroupTableCompanion.insert({ - required String statisticId, - required String groupId, - this.rowid = const Value.absent(), - }) : statisticId = Value(statisticId), - groupId = Value(groupId); - static Insertable custom({ - Expression? statisticId, - Expression? groupId, - Expression? rowid, - }) { - return RawValuesInsertable({ - if (statisticId != null) 'statistic_id': statisticId, - if (groupId != null) 'group_id': groupId, - if (rowid != null) 'rowid': rowid, - }); - } - - StatisticGroupTableCompanion copyWith({ - Value? statisticId, - Value? groupId, - Value? rowid, - }) { - return StatisticGroupTableCompanion( - statisticId: statisticId ?? this.statisticId, - groupId: groupId ?? this.groupId, - rowid: rowid ?? this.rowid, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (statisticId.present) { - map['statistic_id'] = Variable(statisticId.value); - } - if (groupId.present) { - map['group_id'] = Variable(groupId.value); - } - if (rowid.present) { - map['rowid'] = Variable(rowid.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('StatisticGroupTableCompanion(') - ..write('statisticId: $statisticId, ') - ..write('groupId: $groupId, ') - ..write('rowid: $rowid') - ..write(')')) - .toString(); - } -} - abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); @@ -3763,13 +2749,6 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final $ScoreEntryTableTable scoreEntryTable = $ScoreEntryTableTable( this, ); - late final $StatisticTableTable statisticTable = $StatisticTableTable(this); - late final $StatisticScopeTableTable statisticScopeTable = - $StatisticScopeTableTable(this); - late final $StatisticGameTableTable statisticGameTable = - $StatisticGameTableTable(this); - late final $StatisticGroupTableTable statisticGroupTable = - $StatisticGroupTableTable(this); late final PlayerDao playerDao = PlayerDao(this as AppDatabase); late final GroupDao groupDao = GroupDao(this as AppDatabase); late final MatchDao matchDao = MatchDao(this as AppDatabase); @@ -3782,16 +2761,6 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final GameDao gameDao = GameDao(this as AppDatabase); late final ScoreEntryDao scoreEntryDao = ScoreEntryDao(this as AppDatabase); late final TeamDao teamDao = TeamDao(this as AppDatabase); - late final StatisticDao statisticDao = StatisticDao(this as AppDatabase); - late final StatisticScopeDao statisticScopeDao = StatisticScopeDao( - this as AppDatabase, - ); - late final StatisticGameDao statisticGameDao = StatisticGameDao( - this as AppDatabase, - ); - late final StatisticGroupDao statisticGroupDao = StatisticGroupDao( - this as AppDatabase, - ); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -3805,10 +2774,6 @@ abstract class _$AppDatabase extends GeneratedDatabase { teamTable, playerMatchTable, scoreEntryTable, - statisticTable, - statisticScopeTable, - statisticGameTable, - statisticGroupTable, ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ @@ -3875,41 +2840,6 @@ abstract class _$AppDatabase extends GeneratedDatabase { ), result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)], ), - WritePropagation( - on: TableUpdateQuery.onTableName( - 'statistic_table', - limitUpdateKind: UpdateKind.delete, - ), - result: [TableUpdate('statistic_scope_table', kind: UpdateKind.delete)], - ), - WritePropagation( - on: TableUpdateQuery.onTableName( - 'statistic_table', - limitUpdateKind: UpdateKind.delete, - ), - result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], - ), - WritePropagation( - on: TableUpdateQuery.onTableName( - 'game_table', - limitUpdateKind: UpdateKind.delete, - ), - result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], - ), - WritePropagation( - on: TableUpdateQuery.onTableName( - 'statistic_table', - limitUpdateKind: UpdateKind.delete, - ), - result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], - ), - WritePropagation( - on: TableUpdateQuery.onTableName( - 'group_table', - limitUpdateKind: UpdateKind.delete, - ), - result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], - ), ]); } @@ -4489,33 +3419,6 @@ final class $$GroupTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } - - static MultiTypedResultKey< - $StatisticGroupTableTable, - List - > - _statisticGroupTableRefsTable(_$AppDatabase db) => - MultiTypedResultKey.fromTable( - db.statisticGroupTable, - aliasName: $_aliasNameGenerator( - db.groupTable.id, - db.statisticGroupTable.groupId, - ), - ); - - $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { - final manager = $$StatisticGroupTableTableTableManager( - $_db, - $_db.statisticGroupTable, - ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _statisticGroupTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } } class $$GroupTableTableFilterComposer @@ -4596,31 +3499,6 @@ class $$GroupTableTableFilterComposer ); return f(composer); } - - Expression statisticGroupTableRefs( - Expression Function($$StatisticGroupTableTableFilterComposer f) f, - ) { - final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGroupTable, - getReferencedColumn: (t) => t.groupId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGroupTableTableFilterComposer( - $db: $db, - $table: $db.statisticGroupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GroupTableTableOrderingComposer @@ -4725,32 +3603,6 @@ class $$GroupTableTableAnnotationComposer ); return f(composer); } - - Expression statisticGroupTableRefs( - Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, - ) { - final $$StatisticGroupTableTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGroupTable, - getReferencedColumn: (t) => t.groupId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGroupTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticGroupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GroupTableTableTableManager @@ -4769,7 +3621,6 @@ class $$GroupTableTableTableManager PrefetchHooks Function({ bool matchTableRefs, bool playerGroupTableRefs, - bool statisticGroupTableRefs, }) > { $$GroupTableTableTableManager(_$AppDatabase db, $GroupTableTable table) @@ -4820,17 +3671,12 @@ class $$GroupTableTableTableManager ) .toList(), prefetchHooksCallback: - ({ - matchTableRefs = false, - playerGroupTableRefs = false, - statisticGroupTableRefs = false, - }) { + ({matchTableRefs = false, playerGroupTableRefs = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (matchTableRefs) db.matchTable, if (playerGroupTableRefs) db.playerGroupTable, - if (statisticGroupTableRefs) db.statisticGroupTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -4877,27 +3723,6 @@ class $$GroupTableTableTableManager ), typedResults: items, ), - if (statisticGroupTableRefs) - await $_getPrefetchedData< - GroupTableData, - $GroupTableTable, - StatisticGroupTableData - >( - currentTable: table, - referencedTable: $$GroupTableTableReferences - ._statisticGroupTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GroupTableTableReferences( - db, - table, - p0, - ).statisticGroupTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.groupId == item.id, - ), - typedResults: items, - ), ]; }, ); @@ -4918,11 +3743,7 @@ typedef $$GroupTableTableProcessedTableManager = $$GroupTableTableUpdateCompanionBuilder, (GroupTableData, $$GroupTableTableReferences), GroupTableData, - PrefetchHooks Function({ - bool matchTableRefs, - bool playerGroupTableRefs, - bool statisticGroupTableRefs, - }) + PrefetchHooks Function({bool matchTableRefs, bool playerGroupTableRefs}) >; typedef $$GameTableTableCreateCompanionBuilder = GameTableCompanion Function({ @@ -4968,33 +3789,6 @@ final class $$GameTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } - - static MultiTypedResultKey< - $StatisticGameTableTable, - List - > - _statisticGameTableRefsTable(_$AppDatabase db) => - MultiTypedResultKey.fromTable( - db.statisticGameTable, - aliasName: $_aliasNameGenerator( - db.gameTable.id, - db.statisticGameTable.gameId, - ), - ); - - $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { - final manager = $$StatisticGameTableTableTableManager( - $_db, - $_db.statisticGameTable, - ).filter((f) => f.gameId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _statisticGameTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } } class $$GameTableTableFilterComposer @@ -5065,31 +3859,6 @@ class $$GameTableTableFilterComposer ); return f(composer); } - - Expression statisticGameTableRefs( - Expression Function($$StatisticGameTableTableFilterComposer f) f, - ) { - final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGameTable, - getReferencedColumn: (t) => t.gameId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGameTableTableFilterComposer( - $db: $db, - $table: $db.statisticGameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GameTableTableOrderingComposer @@ -5193,32 +3962,6 @@ class $$GameTableTableAnnotationComposer ); return f(composer); } - - Expression statisticGameTableRefs( - Expression Function($$StatisticGameTableTableAnnotationComposer a) f, - ) { - final $$StatisticGameTableTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGameTable, - getReferencedColumn: (t) => t.gameId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGameTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticGameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GameTableTableTableManager @@ -5234,10 +3977,7 @@ class $$GameTableTableTableManager $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({ - bool matchTableRefs, - bool statisticGameTableRefs, - }) + PrefetchHooks Function({bool matchTableRefs}) > { $$GameTableTableTableManager(_$AppDatabase db, $GameTableTable table) : super( @@ -5298,63 +4038,36 @@ class $$GameTableTableTableManager ), ) .toList(), - prefetchHooksCallback: - ({matchTableRefs = false, statisticGameTableRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [ - if (matchTableRefs) db.matchTable, - if (statisticGameTableRefs) db.statisticGameTable, - ], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (matchTableRefs) - await $_getPrefetchedData< - GameTableData, - $GameTableTable, - MatchTableData - >( - currentTable: table, - referencedTable: $$GameTableTableReferences - ._matchTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GameTableTableReferences( - db, - table, - p0, - ).matchTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.gameId == item.id, - ), - typedResults: items, - ), - if (statisticGameTableRefs) - await $_getPrefetchedData< - GameTableData, - $GameTableTable, - StatisticGameTableData - >( - currentTable: table, - referencedTable: $$GameTableTableReferences - ._statisticGameTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GameTableTableReferences( - db, - table, - p0, - ).statisticGameTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.gameId == item.id, - ), - typedResults: items, - ), - ]; - }, - ); + prefetchHooksCallback: ({matchTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [if (matchTableRefs) db.matchTable], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (matchTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + MatchTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._matchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).matchTableRefs, + referencedItemsForCurrentItem: (item, referencedItems) => + referencedItems.where((e) => e.gameId == item.id), + typedResults: items, + ), + ]; }, + ); + }, ), ); } @@ -5371,7 +4084,7 @@ typedef $$GameTableTableProcessedTableManager = $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs, bool statisticGameTableRefs}) + PrefetchHooks Function({bool matchTableRefs}) >; typedef $$MatchTableTableCreateCompanionBuilder = MatchTableCompanion Function({ @@ -7560,1557 +6273,6 @@ typedef $$ScoreEntryTableTableProcessedTableManager = ScoreEntryTableData, PrefetchHooks Function({bool playerId, bool matchId}) >; -typedef $$StatisticTableTableCreateCompanionBuilder = - StatisticTableCompanion Function({ - required String id, - required String type, - Value timeframe, - Value displayCount, - Value rowid, - }); -typedef $$StatisticTableTableUpdateCompanionBuilder = - StatisticTableCompanion Function({ - Value id, - Value type, - Value timeframe, - Value displayCount, - Value rowid, - }); - -final class $$StatisticTableTableReferences - extends - BaseReferences< - _$AppDatabase, - $StatisticTableTable, - StatisticTableData - > { - $$StatisticTableTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static MultiTypedResultKey< - $StatisticScopeTableTable, - List - > - _statisticScopeTableRefsTable(_$AppDatabase db) => - MultiTypedResultKey.fromTable( - db.statisticScopeTable, - aliasName: $_aliasNameGenerator( - db.statisticTable.id, - db.statisticScopeTable.statisticId, - ), - ); - - $$StatisticScopeTableTableProcessedTableManager get statisticScopeTableRefs { - final manager = $$StatisticScopeTableTableTableManager( - $_db, - $_db.statisticScopeTable, - ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _statisticScopeTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } - - static MultiTypedResultKey< - $StatisticGameTableTable, - List - > - _statisticGameTableRefsTable(_$AppDatabase db) => - MultiTypedResultKey.fromTable( - db.statisticGameTable, - aliasName: $_aliasNameGenerator( - db.statisticTable.id, - db.statisticGameTable.statisticId, - ), - ); - - $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { - final manager = $$StatisticGameTableTableTableManager( - $_db, - $_db.statisticGameTable, - ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _statisticGameTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } - - static MultiTypedResultKey< - $StatisticGroupTableTable, - List - > - _statisticGroupTableRefsTable(_$AppDatabase db) => - MultiTypedResultKey.fromTable( - db.statisticGroupTable, - aliasName: $_aliasNameGenerator( - db.statisticTable.id, - db.statisticGroupTable.statisticId, - ), - ); - - $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { - final manager = $$StatisticGroupTableTableTableManager( - $_db, - $_db.statisticGroupTable, - ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _statisticGroupTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } -} - -class $$StatisticTableTableFilterComposer - extends Composer<_$AppDatabase, $StatisticTableTable> { - $$StatisticTableTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnFilters(column), - ); - - ColumnFilters get type => $composableBuilder( - column: $table.type, - builder: (column) => ColumnFilters(column), - ); - - ColumnFilters get timeframe => $composableBuilder( - column: $table.timeframe, - builder: (column) => ColumnFilters(column), - ); - - ColumnFilters get displayCount => $composableBuilder( - column: $table.displayCount, - builder: (column) => ColumnFilters(column), - ); - - Expression statisticScopeTableRefs( - Expression Function($$StatisticScopeTableTableFilterComposer f) f, - ) { - final $$StatisticScopeTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticScopeTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticScopeTableTableFilterComposer( - $db: $db, - $table: $db.statisticScopeTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } - - Expression statisticGameTableRefs( - Expression Function($$StatisticGameTableTableFilterComposer f) f, - ) { - final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGameTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGameTableTableFilterComposer( - $db: $db, - $table: $db.statisticGameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } - - Expression statisticGroupTableRefs( - Expression Function($$StatisticGroupTableTableFilterComposer f) f, - ) { - final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGroupTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGroupTableTableFilterComposer( - $db: $db, - $table: $db.statisticGroupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } -} - -class $$StatisticTableTableOrderingComposer - extends Composer<_$AppDatabase, $StatisticTableTable> { - $$StatisticTableTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get id => $composableBuilder( - column: $table.id, - builder: (column) => ColumnOrderings(column), - ); - - ColumnOrderings get type => $composableBuilder( - column: $table.type, - builder: (column) => ColumnOrderings(column), - ); - - ColumnOrderings get timeframe => $composableBuilder( - column: $table.timeframe, - builder: (column) => ColumnOrderings(column), - ); - - ColumnOrderings get displayCount => $composableBuilder( - column: $table.displayCount, - builder: (column) => ColumnOrderings(column), - ); -} - -class $$StatisticTableTableAnnotationComposer - extends Composer<_$AppDatabase, $StatisticTableTable> { - $$StatisticTableTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get id => - $composableBuilder(column: $table.id, builder: (column) => column); - - GeneratedColumn get type => - $composableBuilder(column: $table.type, builder: (column) => column); - - GeneratedColumn get timeframe => - $composableBuilder(column: $table.timeframe, builder: (column) => column); - - GeneratedColumn get displayCount => $composableBuilder( - column: $table.displayCount, - builder: (column) => column, - ); - - Expression statisticScopeTableRefs( - Expression Function($$StatisticScopeTableTableAnnotationComposer a) f, - ) { - final $$StatisticScopeTableTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticScopeTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticScopeTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticScopeTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } - - Expression statisticGameTableRefs( - Expression Function($$StatisticGameTableTableAnnotationComposer a) f, - ) { - final $$StatisticGameTableTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGameTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGameTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticGameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } - - Expression statisticGroupTableRefs( - Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, - ) { - final $$StatisticGroupTableTableAnnotationComposer composer = - $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.statisticGroupTable, - getReferencedColumn: (t) => t.statisticId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticGroupTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticGroupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } -} - -class $$StatisticTableTableTableManager - extends - RootTableManager< - _$AppDatabase, - $StatisticTableTable, - StatisticTableData, - $$StatisticTableTableFilterComposer, - $$StatisticTableTableOrderingComposer, - $$StatisticTableTableAnnotationComposer, - $$StatisticTableTableCreateCompanionBuilder, - $$StatisticTableTableUpdateCompanionBuilder, - (StatisticTableData, $$StatisticTableTableReferences), - StatisticTableData, - PrefetchHooks Function({ - bool statisticScopeTableRefs, - bool statisticGameTableRefs, - bool statisticGroupTableRefs, - }) - > { - $$StatisticTableTableTableManager( - _$AppDatabase db, - $StatisticTableTable table, - ) : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$StatisticTableTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$StatisticTableTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$StatisticTableTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: - ({ - Value id = const Value.absent(), - Value type = const Value.absent(), - Value timeframe = const Value.absent(), - Value displayCount = const Value.absent(), - Value rowid = const Value.absent(), - }) => StatisticTableCompanion( - id: id, - type: type, - timeframe: timeframe, - displayCount: displayCount, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String id, - required String type, - Value timeframe = const Value.absent(), - Value displayCount = const Value.absent(), - Value rowid = const Value.absent(), - }) => StatisticTableCompanion.insert( - id: id, - type: type, - timeframe: timeframe, - displayCount: displayCount, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - $$StatisticTableTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: - ({ - statisticScopeTableRefs = false, - statisticGameTableRefs = false, - statisticGroupTableRefs = false, - }) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [ - if (statisticScopeTableRefs) db.statisticScopeTable, - if (statisticGameTableRefs) db.statisticGameTable, - if (statisticGroupTableRefs) db.statisticGroupTable, - ], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (statisticScopeTableRefs) - await $_getPrefetchedData< - StatisticTableData, - $StatisticTableTable, - StatisticScopeTableData - >( - currentTable: table, - referencedTable: $$StatisticTableTableReferences - ._statisticScopeTableRefsTable(db), - managerFromTypedResult: (p0) => - $$StatisticTableTableReferences( - db, - table, - p0, - ).statisticScopeTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.statisticId == item.id, - ), - typedResults: items, - ), - if (statisticGameTableRefs) - await $_getPrefetchedData< - StatisticTableData, - $StatisticTableTable, - StatisticGameTableData - >( - currentTable: table, - referencedTable: $$StatisticTableTableReferences - ._statisticGameTableRefsTable(db), - managerFromTypedResult: (p0) => - $$StatisticTableTableReferences( - db, - table, - p0, - ).statisticGameTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.statisticId == item.id, - ), - typedResults: items, - ), - if (statisticGroupTableRefs) - await $_getPrefetchedData< - StatisticTableData, - $StatisticTableTable, - StatisticGroupTableData - >( - currentTable: table, - referencedTable: $$StatisticTableTableReferences - ._statisticGroupTableRefsTable(db), - managerFromTypedResult: (p0) => - $$StatisticTableTableReferences( - db, - table, - p0, - ).statisticGroupTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.statisticId == item.id, - ), - typedResults: items, - ), - ]; - }, - ); - }, - ), - ); -} - -typedef $$StatisticTableTableProcessedTableManager = - ProcessedTableManager< - _$AppDatabase, - $StatisticTableTable, - StatisticTableData, - $$StatisticTableTableFilterComposer, - $$StatisticTableTableOrderingComposer, - $$StatisticTableTableAnnotationComposer, - $$StatisticTableTableCreateCompanionBuilder, - $$StatisticTableTableUpdateCompanionBuilder, - (StatisticTableData, $$StatisticTableTableReferences), - StatisticTableData, - PrefetchHooks Function({ - bool statisticScopeTableRefs, - bool statisticGameTableRefs, - bool statisticGroupTableRefs, - }) - >; -typedef $$StatisticScopeTableTableCreateCompanionBuilder = - StatisticScopeTableCompanion Function({ - required String statisticId, - required String scope, - Value rowid, - }); -typedef $$StatisticScopeTableTableUpdateCompanionBuilder = - StatisticScopeTableCompanion Function({ - Value statisticId, - Value scope, - Value rowid, - }); - -final class $$StatisticScopeTableTableReferences - extends - BaseReferences< - _$AppDatabase, - $StatisticScopeTableTable, - StatisticScopeTableData - > { - $$StatisticScopeTableTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => - db.statisticTable.createAlias( - $_aliasNameGenerator( - db.statisticScopeTable.statisticId, - db.statisticTable.id, - ), - ); - - $$StatisticTableTableProcessedTableManager get statisticId { - final $_column = $_itemColumn('statistic_id')!; - - final manager = $$StatisticTableTableTableManager( - $_db, - $_db.statisticTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } -} - -class $$StatisticScopeTableTableFilterComposer - extends Composer<_$AppDatabase, $StatisticScopeTableTable> { - $$StatisticScopeTableTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnFilters get scope => $composableBuilder( - column: $table.scope, - builder: (column) => ColumnFilters(column), - ); - - $$StatisticTableTableFilterComposer get statisticId { - final $$StatisticTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableFilterComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticScopeTableTableOrderingComposer - extends Composer<_$AppDatabase, $StatisticScopeTableTable> { - $$StatisticScopeTableTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - ColumnOrderings get scope => $composableBuilder( - column: $table.scope, - builder: (column) => ColumnOrderings(column), - ); - - $$StatisticTableTableOrderingComposer get statisticId { - final $$StatisticTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableOrderingComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticScopeTableTableAnnotationComposer - extends Composer<_$AppDatabase, $StatisticScopeTableTable> { - $$StatisticScopeTableTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - GeneratedColumn get scope => - $composableBuilder(column: $table.scope, builder: (column) => column); - - $$StatisticTableTableAnnotationComposer get statisticId { - final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticScopeTableTableTableManager - extends - RootTableManager< - _$AppDatabase, - $StatisticScopeTableTable, - StatisticScopeTableData, - $$StatisticScopeTableTableFilterComposer, - $$StatisticScopeTableTableOrderingComposer, - $$StatisticScopeTableTableAnnotationComposer, - $$StatisticScopeTableTableCreateCompanionBuilder, - $$StatisticScopeTableTableUpdateCompanionBuilder, - (StatisticScopeTableData, $$StatisticScopeTableTableReferences), - StatisticScopeTableData, - PrefetchHooks Function({bool statisticId}) - > { - $$StatisticScopeTableTableTableManager( - _$AppDatabase db, - $StatisticScopeTableTable table, - ) : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$StatisticScopeTableTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$StatisticScopeTableTableOrderingComposer( - $db: db, - $table: table, - ), - createComputedFieldComposer: () => - $$StatisticScopeTableTableAnnotationComposer( - $db: db, - $table: table, - ), - updateCompanionCallback: - ({ - Value statisticId = const Value.absent(), - Value scope = const Value.absent(), - Value rowid = const Value.absent(), - }) => StatisticScopeTableCompanion( - statisticId: statisticId, - scope: scope, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String statisticId, - required String scope, - Value rowid = const Value.absent(), - }) => StatisticScopeTableCompanion.insert( - statisticId: statisticId, - scope: scope, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - $$StatisticScopeTableTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: ({statisticId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: - < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic - > - >(state) { - if (statisticId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.statisticId, - referencedTable: - $$StatisticScopeTableTableReferences - ._statisticIdTable(db), - referencedColumn: - $$StatisticScopeTableTableReferences - ._statisticIdTable(db) - .id, - ) - as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - ), - ); -} - -typedef $$StatisticScopeTableTableProcessedTableManager = - ProcessedTableManager< - _$AppDatabase, - $StatisticScopeTableTable, - StatisticScopeTableData, - $$StatisticScopeTableTableFilterComposer, - $$StatisticScopeTableTableOrderingComposer, - $$StatisticScopeTableTableAnnotationComposer, - $$StatisticScopeTableTableCreateCompanionBuilder, - $$StatisticScopeTableTableUpdateCompanionBuilder, - (StatisticScopeTableData, $$StatisticScopeTableTableReferences), - StatisticScopeTableData, - PrefetchHooks Function({bool statisticId}) - >; -typedef $$StatisticGameTableTableCreateCompanionBuilder = - StatisticGameTableCompanion Function({ - required String statisticId, - required String gameId, - Value rowid, - }); -typedef $$StatisticGameTableTableUpdateCompanionBuilder = - StatisticGameTableCompanion Function({ - Value statisticId, - Value gameId, - Value rowid, - }); - -final class $$StatisticGameTableTableReferences - extends - BaseReferences< - _$AppDatabase, - $StatisticGameTableTable, - StatisticGameTableData - > { - $$StatisticGameTableTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => - db.statisticTable.createAlias( - $_aliasNameGenerator( - db.statisticGameTable.statisticId, - db.statisticTable.id, - ), - ); - - $$StatisticTableTableProcessedTableManager get statisticId { - final $_column = $_itemColumn('statistic_id')!; - - final manager = $$StatisticTableTableTableManager( - $_db, - $_db.statisticTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } - - static $GameTableTable _gameIdTable(_$AppDatabase db) => - db.gameTable.createAlias( - $_aliasNameGenerator(db.statisticGameTable.gameId, db.gameTable.id), - ); - - $$GameTableTableProcessedTableManager get gameId { - final $_column = $_itemColumn('game_id')!; - - final manager = $$GameTableTableTableManager( - $_db, - $_db.gameTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_gameIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } -} - -class $$StatisticGameTableTableFilterComposer - extends Composer<_$AppDatabase, $StatisticGameTableTable> { - $$StatisticGameTableTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableFilterComposer get statisticId { - final $$StatisticTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableFilterComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GameTableTableFilterComposer get gameId { - final $$GameTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.gameId, - referencedTable: $db.gameTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GameTableTableFilterComposer( - $db: $db, - $table: $db.gameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGameTableTableOrderingComposer - extends Composer<_$AppDatabase, $StatisticGameTableTable> { - $$StatisticGameTableTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableOrderingComposer get statisticId { - final $$StatisticTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableOrderingComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GameTableTableOrderingComposer get gameId { - final $$GameTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.gameId, - referencedTable: $db.gameTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GameTableTableOrderingComposer( - $db: $db, - $table: $db.gameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGameTableTableAnnotationComposer - extends Composer<_$AppDatabase, $StatisticGameTableTable> { - $$StatisticGameTableTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableAnnotationComposer get statisticId { - final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GameTableTableAnnotationComposer get gameId { - final $$GameTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.gameId, - referencedTable: $db.gameTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GameTableTableAnnotationComposer( - $db: $db, - $table: $db.gameTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGameTableTableTableManager - extends - RootTableManager< - _$AppDatabase, - $StatisticGameTableTable, - StatisticGameTableData, - $$StatisticGameTableTableFilterComposer, - $$StatisticGameTableTableOrderingComposer, - $$StatisticGameTableTableAnnotationComposer, - $$StatisticGameTableTableCreateCompanionBuilder, - $$StatisticGameTableTableUpdateCompanionBuilder, - (StatisticGameTableData, $$StatisticGameTableTableReferences), - StatisticGameTableData, - PrefetchHooks Function({bool statisticId, bool gameId}) - > { - $$StatisticGameTableTableTableManager( - _$AppDatabase db, - $StatisticGameTableTable table, - ) : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$StatisticGameTableTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$StatisticGameTableTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$StatisticGameTableTableAnnotationComposer( - $db: db, - $table: table, - ), - updateCompanionCallback: - ({ - Value statisticId = const Value.absent(), - Value gameId = const Value.absent(), - Value rowid = const Value.absent(), - }) => StatisticGameTableCompanion( - statisticId: statisticId, - gameId: gameId, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String statisticId, - required String gameId, - Value rowid = const Value.absent(), - }) => StatisticGameTableCompanion.insert( - statisticId: statisticId, - gameId: gameId, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - $$StatisticGameTableTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: ({statisticId = false, gameId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: - < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic - > - >(state) { - if (statisticId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.statisticId, - referencedTable: - $$StatisticGameTableTableReferences - ._statisticIdTable(db), - referencedColumn: - $$StatisticGameTableTableReferences - ._statisticIdTable(db) - .id, - ) - as T; - } - if (gameId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.gameId, - referencedTable: - $$StatisticGameTableTableReferences - ._gameIdTable(db), - referencedColumn: - $$StatisticGameTableTableReferences - ._gameIdTable(db) - .id, - ) - as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - ), - ); -} - -typedef $$StatisticGameTableTableProcessedTableManager = - ProcessedTableManager< - _$AppDatabase, - $StatisticGameTableTable, - StatisticGameTableData, - $$StatisticGameTableTableFilterComposer, - $$StatisticGameTableTableOrderingComposer, - $$StatisticGameTableTableAnnotationComposer, - $$StatisticGameTableTableCreateCompanionBuilder, - $$StatisticGameTableTableUpdateCompanionBuilder, - (StatisticGameTableData, $$StatisticGameTableTableReferences), - StatisticGameTableData, - PrefetchHooks Function({bool statisticId, bool gameId}) - >; -typedef $$StatisticGroupTableTableCreateCompanionBuilder = - StatisticGroupTableCompanion Function({ - required String statisticId, - required String groupId, - Value rowid, - }); -typedef $$StatisticGroupTableTableUpdateCompanionBuilder = - StatisticGroupTableCompanion Function({ - Value statisticId, - Value groupId, - Value rowid, - }); - -final class $$StatisticGroupTableTableReferences - extends - BaseReferences< - _$AppDatabase, - $StatisticGroupTableTable, - StatisticGroupTableData - > { - $$StatisticGroupTableTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => - db.statisticTable.createAlias( - $_aliasNameGenerator( - db.statisticGroupTable.statisticId, - db.statisticTable.id, - ), - ); - - $$StatisticTableTableProcessedTableManager get statisticId { - final $_column = $_itemColumn('statistic_id')!; - - final manager = $$StatisticTableTableTableManager( - $_db, - $_db.statisticTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } - - static $GroupTableTable _groupIdTable(_$AppDatabase db) => - db.groupTable.createAlias( - $_aliasNameGenerator(db.statisticGroupTable.groupId, db.groupTable.id), - ); - - $$GroupTableTableProcessedTableManager get groupId { - final $_column = $_itemColumn('group_id')!; - - final manager = $$GroupTableTableTableManager( - $_db, - $_db.groupTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } -} - -class $$StatisticGroupTableTableFilterComposer - extends Composer<_$AppDatabase, $StatisticGroupTableTable> { - $$StatisticGroupTableTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableFilterComposer get statisticId { - final $$StatisticTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableFilterComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GroupTableTableFilterComposer get groupId { - final $$GroupTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableFilterComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGroupTableTableOrderingComposer - extends Composer<_$AppDatabase, $StatisticGroupTableTable> { - $$StatisticGroupTableTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableOrderingComposer get statisticId { - final $$StatisticTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableOrderingComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GroupTableTableOrderingComposer get groupId { - final $$GroupTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableOrderingComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGroupTableTableAnnotationComposer - extends Composer<_$AppDatabase, $StatisticGroupTableTable> { - $$StatisticGroupTableTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$StatisticTableTableAnnotationComposer get statisticId { - final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.statisticId, - referencedTable: $db.statisticTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$StatisticTableTableAnnotationComposer( - $db: $db, - $table: $db.statisticTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$GroupTableTableAnnotationComposer get groupId { - final $$GroupTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableAnnotationComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$StatisticGroupTableTableTableManager - extends - RootTableManager< - _$AppDatabase, - $StatisticGroupTableTable, - StatisticGroupTableData, - $$StatisticGroupTableTableFilterComposer, - $$StatisticGroupTableTableOrderingComposer, - $$StatisticGroupTableTableAnnotationComposer, - $$StatisticGroupTableTableCreateCompanionBuilder, - $$StatisticGroupTableTableUpdateCompanionBuilder, - (StatisticGroupTableData, $$StatisticGroupTableTableReferences), - StatisticGroupTableData, - PrefetchHooks Function({bool statisticId, bool groupId}) - > { - $$StatisticGroupTableTableTableManager( - _$AppDatabase db, - $StatisticGroupTableTable table, - ) : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$StatisticGroupTableTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$StatisticGroupTableTableOrderingComposer( - $db: db, - $table: table, - ), - createComputedFieldComposer: () => - $$StatisticGroupTableTableAnnotationComposer( - $db: db, - $table: table, - ), - updateCompanionCallback: - ({ - Value statisticId = const Value.absent(), - Value groupId = const Value.absent(), - Value rowid = const Value.absent(), - }) => StatisticGroupTableCompanion( - statisticId: statisticId, - groupId: groupId, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String statisticId, - required String groupId, - Value rowid = const Value.absent(), - }) => StatisticGroupTableCompanion.insert( - statisticId: statisticId, - groupId: groupId, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - $$StatisticGroupTableTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: ({statisticId = false, groupId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: - < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic - > - >(state) { - if (statisticId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.statisticId, - referencedTable: - $$StatisticGroupTableTableReferences - ._statisticIdTable(db), - referencedColumn: - $$StatisticGroupTableTableReferences - ._statisticIdTable(db) - .id, - ) - as T; - } - if (groupId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.groupId, - referencedTable: - $$StatisticGroupTableTableReferences - ._groupIdTable(db), - referencedColumn: - $$StatisticGroupTableTableReferences - ._groupIdTable(db) - .id, - ) - as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - ), - ); -} - -typedef $$StatisticGroupTableTableProcessedTableManager = - ProcessedTableManager< - _$AppDatabase, - $StatisticGroupTableTable, - StatisticGroupTableData, - $$StatisticGroupTableTableFilterComposer, - $$StatisticGroupTableTableOrderingComposer, - $$StatisticGroupTableTableAnnotationComposer, - $$StatisticGroupTableTableCreateCompanionBuilder, - $$StatisticGroupTableTableUpdateCompanionBuilder, - (StatisticGroupTableData, $$StatisticGroupTableTableReferences), - StatisticGroupTableData, - PrefetchHooks Function({bool statisticId, bool groupId}) - >; class $AppDatabaseManager { final _$AppDatabase _db; @@ -9131,12 +6293,4 @@ class $AppDatabaseManager { $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); $$ScoreEntryTableTableTableManager get scoreEntryTable => $$ScoreEntryTableTableTableManager(_db, _db.scoreEntryTable); - $$StatisticTableTableTableManager get statisticTable => - $$StatisticTableTableTableManager(_db, _db.statisticTable); - $$StatisticScopeTableTableTableManager get statisticScopeTable => - $$StatisticScopeTableTableTableManager(_db, _db.statisticScopeTable); - $$StatisticGameTableTableTableManager get statisticGameTable => - $$StatisticGameTableTableTableManager(_db, _db.statisticGameTable); - $$StatisticGroupTableTableTableManager get statisticGroupTable => - $$StatisticGroupTableTableTableManager(_db, _db.statisticGroupTable); } diff --git a/lib/data/db/tables/statistic_game_table.dart b/lib/data/db/tables/statistic_game_table.dart deleted file mode 100644 index e1cc7d4..0000000 --- a/lib/data/db/tables/statistic_game_table.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/tables/game_table.dart'; -import 'package:tallee/data/db/tables/statistic_table.dart'; - -class StatisticGameTable extends Table { - TextColumn get statisticId => - text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); - TextColumn get gameId => - text().references(GameTable, #id, onDelete: KeyAction.cascade)(); - - @override - Set> get primaryKey => {statisticId, gameId}; -} diff --git a/lib/data/db/tables/statistic_group_table.dart b/lib/data/db/tables/statistic_group_table.dart deleted file mode 100644 index cd642ad..0000000 --- a/lib/data/db/tables/statistic_group_table.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/tables/group_table.dart'; -import 'package:tallee/data/db/tables/statistic_table.dart'; - -class StatisticGroupTable extends Table { - TextColumn get statisticId => - text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); - TextColumn get groupId => - text().references(GroupTable, #id, onDelete: KeyAction.cascade)(); - - @override - Set> get primaryKey => {statisticId, groupId}; -} diff --git a/lib/data/db/tables/statistic_scope_table.dart b/lib/data/db/tables/statistic_scope_table.dart deleted file mode 100644 index 3a9bcdc..0000000 --- a/lib/data/db/tables/statistic_scope_table.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/tables/statistic_table.dart'; - -class StatisticScopeTable extends Table { - TextColumn get statisticId => - text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); - TextColumn get scope => text()(); - - @override - Set> get primaryKey => {statisticId, scope}; -} diff --git a/lib/data/db/tables/statistic_table.dart b/lib/data/db/tables/statistic_table.dart deleted file mode 100644 index ef368a5..0000000 --- a/lib/data/db/tables/statistic_table.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:drift/drift.dart'; - -class StatisticTable extends Table { - TextColumn get id => text()(); - TextColumn get type => text()(); - TextColumn get timeframe => text().nullable()(); - IntColumn get displayCount => integer().withDefault(const Constant(5))(); - - @override - Set> get primaryKey => {id}; -} diff --git a/lib/data/models/game.dart b/lib/data/models/game.dart index ec69204..89bbd30 100644 --- a/lib/data/models/game.dart +++ b/lib/data/models/game.dart @@ -8,13 +8,13 @@ class Game { final String name; final Ruleset ruleset; final String description; - final AppColor color; + final GameColor color; final String icon; Game({ required this.name, required this.ruleset, - this.color = AppColor.orange, + this.color = GameColor.orange, this.description = '', this.icon = '', String? id, @@ -33,7 +33,7 @@ class Game { String? name, Ruleset? ruleset, String? description, - AppColor? color, + GameColor? color, String? icon, }) { return Game( @@ -73,7 +73,7 @@ class Game { orElse: () => Ruleset.singleWinner, ), description = json['description'], - color = AppColor.values.firstWhere((e) => e.name == json['color']), + color = GameColor.values.firstWhere((e) => e.name == json['color']), icon = json['icon']; Map toJson() => { diff --git a/lib/data/models/group.dart b/lib/data/models/group.dart index 8b1fe92..5c1515c 100644 --- a/lib/data/models/group.dart +++ b/lib/data/models/group.dart @@ -5,17 +5,17 @@ import 'package:uuid/uuid.dart'; class Group { final String id; - final DateTime createdAt; final String name; - final List members; final String description; + final DateTime createdAt; + final List members; Group({ - required this.name, - required this.members, String? id, DateTime? createdAt, + required this.name, String? description, + required this.members, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? clock.now(), description = description ?? ''; diff --git a/lib/data/models/match.dart b/lib/data/models/match.dart index 601a01c..2c43fe3 100644 --- a/lib/data/models/match.dart +++ b/lib/data/models/match.dart @@ -107,7 +107,7 @@ class Match { name: '', ruleset: Ruleset.singleWinner, description: '', - color: AppColor.blue, + color: GameColor.blue, icon: '', ), group = null, diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart deleted file mode 100644 index e995d93..0000000 --- a/lib/data/models/statistic.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; -import 'package:uuid/uuid.dart'; - -class Statistic { - final String id; - final StatisticType type; - final List scopes; - final Timeframe? timeframe; - final List? selectedGroups; - final List? selectedGames; - final int displayCount; - - Statistic({ - required this.type, - required this.scopes, - this.timeframe, - this.selectedGroups, - this.selectedGames, - this.displayCount = 5, - String? id, - }) : id = id ?? const Uuid().v4(); - - @override - String toString() { - return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)'; - } - - Statistic copyWith({ - StatisticType? type, - List? scopes, - Timeframe? timeframe, - List? selectedGroups, - List? selectedGames, - int? displayCount, - }) { - return Statistic( - id: id, - type: type ?? this.type, - scopes: scopes ?? this.scopes, - timeframe: timeframe ?? this.timeframe, - selectedGroups: selectedGroups ?? this.selectedGroups, - selectedGames: selectedGames ?? this.selectedGames, - displayCount: displayCount ?? this.displayCount, - ); - } -} diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 7dc32f6..a1ed4af 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -2,18 +2,14 @@ "@@locale": "de", "all_players": "Alle Spieler:innen", "all_players_selected": "Alle Spieler:innen ausgewählt", - "all_time": "Gesamter Zeitraum", "amount_of_matches": "Anzahl der Spiele", "app_name": "Tallee", - "average_score": "Durchschnittliche Punktzahl", "best_player": "Beste:r Spieler:in", - "best_score": "Beste Punktzahl", "cancel": "Abbrechen", "choose_color": "Farbe wählen", "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", - "classifier": "Klassifikator", "color": "Farbe", "color_blue": "Blau", "color_green": "Grün", @@ -23,31 +19,12 @@ "color_red": "Rot", "color_teal": "Türkis", "color_yellow": "Gelb", - "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", - "@could_not_add_player": { - "placeholders": { - "playerName": { - "type": "String" - } - } - }, "create_game": "Spielvorlage erstellen", "create_group": "Gruppe erstellen", "create_match": "Spiel erstellen", "create_new_group": "Neue Gruppe erstellen", "create_new_match": "Neues Spiel erstellen", - "create_statistic": "Statistik erstellen", - "create_statistic_classifier_subtitle": "Wähle die anzuzeigende Hauptmetrik aus", - "create_statistic_classifier_title": "Klassifikator", - "create_statistic_games_subtitle": "Wähle die gefilterten Spielvorlagen", - "create_statistic_games_title": "Spielvorlagen", - "create_statistic_groups_subtitle": "Wähle die gefilterten Gruppen", - "create_statistic_groups_title": "Gruppen", - "create_statistic_scope_subtitle": "Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.", - "create_statistic_scope_title": "Bereich", - "create_statistic_timeframe_subtitle": "Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.", - "create_statistic_timeframe_title": "Zeitraum", "created_on": "Erstellt am", "data": "Daten", "data_successfully_deleted": "Daten erfolgreich gelöscht", @@ -67,15 +44,11 @@ }, "delete_group": "Gruppe löschen", "delete_match": "Spiel löschen", - "delete_player": "Spieler:in löschen", "description": "Beschreibung", - "displayed_entries": "Angezeigte Einträge", "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_game": "Spielvorlage bearbeiten", "edit_group": "Gruppe bearbeiten", "edit_match": "Gruppe bearbeiten", - "edit_name": "Name ändern", - "edit_player": "Spieler bearbeiten", "enter_points": "Punkte eingeben", "enter_results": "Ergebnisse eintragen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", @@ -86,42 +59,30 @@ "exit_view": "Ansicht verlassen", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", - "filter": "Filter", "format_exception": "Formatfehler (siehe Konsole)", "game": "Spielvorlage", "game_name": "Spielvorlagenname", - "games": "Spielvorlagen", "group": "Gruppe", "group_name": "Gruppenname", "group_profile": "Gruppenprofil", "groups": "Gruppen", - "groups_part_of": "Gruppen Teil von", "highest_score": "Höchste Punkte", "home": "Startseite", "import_canceled": "Import abgebrochen", "import_data": "Daten importieren", "info": "Info", "invalid_schema": "Ungültiges Schema", - "last_180_days": "Letzte 180 Tage", - "last_30_days": "Letzte 30 Tage", - "last_7_days": "Letzte 7 Tage", - "last_90_days": "Letzte 90 Tage", - "last_year": "Letztes Jahr", "least_points": "Niedrigste Punkte", "legal": "Rechtliches", "legal_notice": "Impressum", "licenses": "Lizenzen", "live_edit_mode": "Live-Bearbeitungsmodus", - "loading": "Lädt...", "loser": "Verlierer:in", "lowest_score": "Niedrigste Punkte", "match_in_progress": "Spiel läuft...", "match_name": "Spieltitel", "match_profile": "Spielprofil", "matches": "Spiele", - "matches_part_of": "Spiele Teil von", - "matches_played": "Spiele gespielt", - "matches_won": "Spiele gewonnen", "members": "Mitglieder", "most_points": "Höchste Punkte", "multiple_winners": "Mehrere Gewinner:innen", @@ -131,7 +92,6 @@ "no_license_text_available": "Kein Lizenztext verfügbar", "no_licenses_found": "Keine Lizenzen gefunden", "no_matches_created_yet": "Noch keine Spiele erstellt", - "no_matches_played_yet": "Noch kein Spiel gespielt", "no_players_created_yet": "Noch keine Spieler:in erstellt", "no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden", "no_players_selected": "Keine Spieler:innen ausgewählt", @@ -139,16 +99,13 @@ "no_results_entered_yet": "Noch keine Ergebnisse eingetragen", "no_second_match_available": "Kein zweites Spiel verfügbar", "no_statistics_available": "Keine Statistiken verfügbar", - "no_statistics_created_yet": "Noch keine Statistiken erstellt", "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", - "not_part_of_any_group": "Noch keiner Gruppe hinzugefügt", "place": "Platz", "placement": "Platzierung", "played_matches": "Gespielte Spiele", "player_name": "Spieler:innenname", - "player_profile": "Spieler:in-Profil", "players": "Spieler:innen", "point": "Punkt", "points": "Punkte", @@ -164,36 +121,15 @@ "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.", "save_changes": "Änderungen speichern", - "scope": "Bereich", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", - "select_a_classifier": "Klassifikator auswählen", - "select_a_game": "Spielvorlage auswählen", - "select_a_group": "Gruppe auswählen", - "select_a_scope": "Bereich auswählen", - "select_a_timeframe": "Zeitraum auswählen", - "select_a_timeframe_for_which_data_will_be_filtered": "Wähle einen Zeitraum, für den die Daten gefiltert werden sollen", "select_loser": "Verlierer:in wählen", - "select_the_filtered_games": "Wähle Spiele, nach denen gefiltert werden soll.", - "select_the_filtered_groups": "Wähle Gruppen, nach denen gefiltert werden soll.", - "select_the_filtered_timeframe": "Wähle einen Zeitraum, nach dem gefiltert werden soll.", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", - "selected_games": "Ausgewählte Spielvorlagen", - "selected_groups": "Ausgewählte Gruppen", "selected_players": "Ausgewählte Spieler:innen", - "set_name": "Name setzen", "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", - "statistic_type_average_score": "Durchschnittliche Punktzahl", - "statistic_type_best_score": "Beste Punktzahl", - "statistic_type_total_losses": "Niederlagen insgesamt", - "statistic_type_total_matches": "Spiele insgesamt", - "statistic_type_total_score": "Punktzahl insgesamt", - "statistic_type_total_wins": "Siege insgesamt", - "statistic_type_winrate": "Siegquote", - "statistic_type_worst_score": "Schlechteste Punktzahl", "statistics": "Statistiken", "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", @@ -201,18 +137,12 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", - "timeframe": "Zeitraum", "today_at": "Heute um", - "total_losses": "Niederlagen insgesamt", - "total_matches": "Spiele insgesamt", - "total_score": "Punktzahl insgesamt", - "total_wins": "Siege insgesamt", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", "winner": "Gewinner:in", "winners": "Gewinner:innen", "winrate": "Siegquote", "wins": "Siege", - "worst_score": "Schlechteste Punktzahl", "yesterday_at": "Gestern um" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 988433b..5ae94cd 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -18,30 +18,13 @@ "color_purple": "Purple", "color_red": "Red", "color_teal": "Teal", - "displayed_entries": "Displayed entries", "color_yellow": "Yellow", - "confirm": "Confirm", - "could_not_add_player": "Could not add player {playerName}", - "@could_not_add_player": { - "placeholders": { - "playerName": { - "type": "String" - } - } - }, + "could_not_add_player": "Could not add player", "create_game": "Create Game", "create_group": "Create Group", "create_match": "Create match", "create_new_group": "Create new group", "create_new_match": "Create new match", - "create_statistic": "Create statistic", - "classifier": "Classifier", - "select_the_filtered_timeframe": "Select the timeframe you want to filter by.", - "select_the_filtered_games": "Select the games you want to filter by.", - "games": "Games", - "select_the_filtered_groups": "Select the groups you want to filter by.", - "scope": "Scope", - "timeframe": "Timeframe", "created_on": "Created on", "data": "Data", "data_successfully_deleted": "Data successfully deleted", @@ -59,17 +42,13 @@ } } }, - "filter": "Filter", "delete_group": "Delete Group", "delete_match": "Delete Match", - "delete_player": "Delete player?", "description": "Description", "drag_to_set_placement": "Drag to set placement", "edit_game": "Edit Game", "edit_group": "Edit Group", "edit_match": "Edit Match", - "edit_name": "Edit name", - "edit_player": "Edit player", "enter_points": "Enter points", "enter_results": "Enter Results", "error_creating_group": "Error while creating group, please try again", @@ -87,7 +66,6 @@ "group_name": "Group name", "group_profile": "Group Profile", "groups": "Groups", - "groups_part_of": "Groups part of", "highest_score": "Highest Score", "home": "Home", "import_canceled": "Import canceled", @@ -99,16 +77,12 @@ "legal_notice": "Legal Notice", "licenses": "Licenses", "live_edit_mode": "Live Edit Mode", - "loading": "Loading...", "loser": "Loser", "lowest_score": "Lowest Score", "match_in_progress": "Match in progress...", "match_name": "Match name", "match_profile": "Match Profile", "matches": "Matches", - "matches_part_of": "Matches part of", - "matches_played": "Matches played", - "matches_won": "Matches won", "members": "Members", "most_points": "Most Points", "multiple_winners": "Multiple Winners", @@ -118,7 +92,6 @@ "no_license_text_available": "No license text available", "no_licenses_found": "No licenses found", "no_matches_created_yet": "No matches created yet", - "no_matches_played_yet": "No games played yet", "no_players_created_yet": "No players created yet", "no_players_found_with_that_name": "No players found with that name", "no_players_selected": "No players selected", @@ -126,16 +99,13 @@ "no_results_entered_yet": "No results entered yet", "no_second_match_available": "No second match available", "no_statistics_available": "No statistics available", - "no_statistics_created_yet": "No statistics created yet", "none": "None", "none_group": "None", "not_available": "Not available", - "not_part_of_any_group": "Not part of any group yet", "place": "place", "placement": "Placement", "played_matches": "Played Matches", "player_name": "Player name", - "player_profile": "Player Profile", "players": "Players", "point": "Point", "points": "Points", @@ -156,26 +126,11 @@ "select_winner": "Select Winner", "select_winners": "Select Winners", "selected_players": "Selected players", - "set_name": "Set name", "settings": "Settings", - "select_a_classifier": "Select a classifier", - "select_a_game": "Select a game", - "select_a_group": "Select a group", - "select_a_scope": "Select a scope", - "select_a_timeframe": "Select a timeframe", "single_loser": "Single Loser", "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", - "selected_games": "Selected games", - "selected_groups": "Selected groups", - "average_score": "Average score", - "best_score": "Best score", - "total_losses": "Total losses", - "total_matches": "Total matches", - "total_score": "Total score", - "total_wins": "Total wins", - "worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -190,12 +145,6 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", - "all_time": "All time", - "last_180_days": "Last 180 days", - "last_30_days": "Last 30 days", - "last_7_days": "Last 7 days", - "last_90_days": "Last 90 days", - "last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 30e1b33..dd538d5 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -206,29 +206,17 @@ abstract class AppLocalizations { /// **'Teal'** String get color_teal; - /// No description provided for @displayed_entries. - /// - /// In en, this message translates to: - /// **'Displayed entries'** - String get displayed_entries; - /// No description provided for @color_yellow. /// /// In en, this message translates to: /// **'Yellow'** String get color_yellow; - /// No description provided for @confirm. - /// - /// In en, this message translates to: - /// **'Confirm'** - String get confirm; - /// No description provided for @could_not_add_player. /// /// In en, this message translates to: - /// **'Could not add player {playerName}'** - String could_not_add_player(String playerName); + /// **'Could not add player'** + String could_not_add_player(Object playerName); /// No description provided for @create_game. /// @@ -260,54 +248,6 @@ abstract class AppLocalizations { /// **'Create new match'** String get create_new_match; - /// No description provided for @create_statistic. - /// - /// In en, this message translates to: - /// **'Create statistic'** - String get create_statistic; - - /// No description provided for @classifier. - /// - /// In en, this message translates to: - /// **'Classifier'** - String get classifier; - - /// No description provided for @select_the_filtered_timeframe. - /// - /// In en, this message translates to: - /// **'Select the timeframe you want to filter by.'** - String get select_the_filtered_timeframe; - - /// No description provided for @select_the_filtered_games. - /// - /// In en, this message translates to: - /// **'Select the games you want to filter by.'** - String get select_the_filtered_games; - - /// No description provided for @games. - /// - /// In en, this message translates to: - /// **'Games'** - String get games; - - /// No description provided for @select_the_filtered_groups. - /// - /// In en, this message translates to: - /// **'Select the groups you want to filter by.'** - String get select_the_filtered_groups; - - /// No description provided for @scope. - /// - /// In en, this message translates to: - /// **'Scope'** - String get scope; - - /// No description provided for @timeframe. - /// - /// In en, this message translates to: - /// **'Timeframe'** - String get timeframe; - /// No description provided for @created_on. /// /// In en, this message translates to: @@ -368,12 +308,6 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); - /// No description provided for @filter. - /// - /// In en, this message translates to: - /// **'Filter'** - String get filter; - /// No description provided for @delete_group. /// /// In en, this message translates to: @@ -386,12 +320,6 @@ abstract class AppLocalizations { /// **'Delete Match'** String get delete_match; - /// No description provided for @delete_player. - /// - /// In en, this message translates to: - /// **'Delete player?'** - String get delete_player; - /// No description provided for @description. /// /// In en, this message translates to: @@ -422,18 +350,6 @@ abstract class AppLocalizations { /// **'Edit Match'** String get edit_match; - /// No description provided for @edit_name. - /// - /// In en, this message translates to: - /// **'Edit name'** - String get edit_name; - - /// No description provided for @edit_player. - /// - /// In en, this message translates to: - /// **'Edit player'** - String get edit_player; - /// No description provided for @enter_points. /// /// In en, this message translates to: @@ -536,12 +452,6 @@ abstract class AppLocalizations { /// **'Groups'** String get groups; - /// No description provided for @groups_part_of. - /// - /// In en, this message translates to: - /// **'Groups part of'** - String get groups_part_of; - /// No description provided for @highest_score. /// /// In en, this message translates to: @@ -608,12 +518,6 @@ abstract class AppLocalizations { /// **'Live Edit Mode'** String get live_edit_mode; - /// No description provided for @loading. - /// - /// In en, this message translates to: - /// **'Loading...'** - String get loading; - /// No description provided for @loser. /// /// In en, this message translates to: @@ -650,24 +554,6 @@ abstract class AppLocalizations { /// **'Matches'** String get matches; - /// No description provided for @matches_part_of. - /// - /// In en, this message translates to: - /// **'Matches part of'** - String get matches_part_of; - - /// No description provided for @matches_played. - /// - /// In en, this message translates to: - /// **'Matches played'** - String get matches_played; - - /// No description provided for @matches_won. - /// - /// In en, this message translates to: - /// **'Matches won'** - String get matches_won; - /// No description provided for @members. /// /// In en, this message translates to: @@ -722,12 +608,6 @@ abstract class AppLocalizations { /// **'No matches created yet'** String get no_matches_created_yet; - /// No description provided for @no_matches_played_yet. - /// - /// In en, this message translates to: - /// **'No games played yet'** - String get no_matches_played_yet; - /// No description provided for @no_players_created_yet. /// /// In en, this message translates to: @@ -770,12 +650,6 @@ abstract class AppLocalizations { /// **'No statistics available'** String get no_statistics_available; - /// No description provided for @no_statistics_created_yet. - /// - /// In en, this message translates to: - /// **'No statistics created yet'** - String get no_statistics_created_yet; - /// No description provided for @none. /// /// In en, this message translates to: @@ -794,12 +668,6 @@ abstract class AppLocalizations { /// **'Not available'** String get not_available; - /// No description provided for @not_part_of_any_group. - /// - /// In en, this message translates to: - /// **'Not part of any group yet'** - String get not_part_of_any_group; - /// No description provided for @place. /// /// In en, this message translates to: @@ -824,12 +692,6 @@ abstract class AppLocalizations { /// **'Player name'** String get player_name; - /// No description provided for @player_profile. - /// - /// In en, this message translates to: - /// **'Player Profile'** - String get player_profile; - /// No description provided for @players. /// /// In en, this message translates to: @@ -950,48 +812,12 @@ abstract class AppLocalizations { /// **'Selected players'** String get selected_players; - /// No description provided for @set_name. - /// - /// In en, this message translates to: - /// **'Set name'** - String get set_name; - /// No description provided for @settings. /// /// In en, this message translates to: /// **'Settings'** String get settings; - /// No description provided for @select_a_classifier. - /// - /// In en, this message translates to: - /// **'Select a classifier'** - String get select_a_classifier; - - /// No description provided for @select_a_game. - /// - /// In en, this message translates to: - /// **'Select a game'** - String get select_a_game; - - /// No description provided for @select_a_group. - /// - /// In en, this message translates to: - /// **'Select a group'** - String get select_a_group; - - /// No description provided for @select_a_scope. - /// - /// In en, this message translates to: - /// **'Select a scope'** - String get select_a_scope; - - /// No description provided for @select_a_timeframe. - /// - /// In en, this message translates to: - /// **'Select a timeframe'** - String get select_a_timeframe; - /// No description provided for @single_loser. /// /// In en, this message translates to: @@ -1016,60 +842,6 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; - /// No description provided for @selected_games. - /// - /// In en, this message translates to: - /// **'Selected games'** - String get selected_games; - - /// No description provided for @selected_groups. - /// - /// In en, this message translates to: - /// **'Selected groups'** - String get selected_groups; - - /// No description provided for @average_score. - /// - /// In en, this message translates to: - /// **'Average score'** - String get average_score; - - /// No description provided for @best_score. - /// - /// In en, this message translates to: - /// **'Best score'** - String get best_score; - - /// No description provided for @total_losses. - /// - /// In en, this message translates to: - /// **'Total losses'** - String get total_losses; - - /// No description provided for @total_matches. - /// - /// In en, this message translates to: - /// **'Total matches'** - String get total_matches; - - /// No description provided for @total_score. - /// - /// In en, this message translates to: - /// **'Total score'** - String get total_score; - - /// No description provided for @total_wins. - /// - /// In en, this message translates to: - /// **'Total wins'** - String get total_wins; - - /// No description provided for @worst_score. - /// - /// In en, this message translates to: - /// **'Worst score'** - String get worst_score; - /// Success message when adding a player /// /// In en, this message translates to: @@ -1100,42 +872,6 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; - /// No description provided for @all_time. - /// - /// In en, this message translates to: - /// **'All time'** - String get all_time; - - /// No description provided for @last_180_days. - /// - /// In en, this message translates to: - /// **'Last 180 days'** - String get last_180_days; - - /// No description provided for @last_30_days. - /// - /// In en, this message translates to: - /// **'Last 30 days'** - String get last_30_days; - - /// No description provided for @last_7_days. - /// - /// In en, this message translates to: - /// **'Last 7 days'** - String get last_7_days; - - /// No description provided for @last_90_days. - /// - /// In en, this message translates to: - /// **'Last 90 days'** - String get last_90_days; - - /// No description provided for @last_year. - /// - /// In en, this message translates to: - /// **'Last year'** - String get last_year; - /// No description provided for @today_at. /// /// 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 1417bb7..7c5177a 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -62,17 +62,11 @@ class AppLocalizationsDe extends AppLocalizations { @override String get color_teal => 'Türkis'; - @override - String get displayed_entries => 'Angezeigte Einträge'; - @override String get color_yellow => 'Gelb'; @override - String get confirm => 'Bestätigen'; - - @override - String could_not_add_player(String playerName) { + String could_not_add_player(Object playerName) { return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; } @@ -91,33 +85,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get create_new_match => 'Neues Spiel erstellen'; - @override - String get create_statistic => 'Statistik erstellen'; - - @override - String get classifier => 'Klassifikator'; - - @override - String get select_the_filtered_timeframe => - 'Wähle einen Zeitraum, nach dem gefiltert werden soll.'; - - @override - String get select_the_filtered_games => - 'Wähle Spiele, nach denen gefiltert werden soll.'; - - @override - String get games => 'Spielvorlagen'; - - @override - String get select_the_filtered_groups => - 'Wähle Gruppen, nach denen gefiltert werden soll.'; - - @override - String get scope => 'Bereich'; - - @override - String get timeframe => 'Zeitraum'; - @override String get created_on => 'Erstellt am'; @@ -158,18 +125,12 @@ class AppLocalizationsDe extends AppLocalizations { return 'Wenn du diese Spielvorlage löschst, $_temp0 mit dieser Spielvorlage ebenfalls gelöscht.'; } - @override - String get filter => 'Filter'; - @override String get delete_group => 'Gruppe löschen'; @override String get delete_match => 'Spiel löschen'; - @override - String get delete_player => 'Spieler:in löschen'; - @override String get description => 'Beschreibung'; @@ -185,12 +146,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get edit_match => 'Gruppe bearbeiten'; - @override - String get edit_name => 'Name ändern'; - - @override - String get edit_player => 'Spieler bearbeiten'; - @override String get enter_points => 'Punkte eingeben'; @@ -246,9 +201,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get groups => 'Gruppen'; - @override - String get groups_part_of => 'Gruppen Teil von'; - @override String get highest_score => 'Höchste Punkte'; @@ -282,9 +234,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get live_edit_mode => 'Live-Bearbeitungsmodus'; - @override - String get loading => 'Lädt...'; - @override String get loser => 'Verlierer:in'; @@ -303,15 +252,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get matches => 'Spiele'; - @override - String get matches_part_of => 'Spiele Teil von'; - - @override - String get matches_played => 'Spiele gespielt'; - - @override - String get matches_won => 'Spiele gewonnen'; - @override String get members => 'Mitglieder'; @@ -339,9 +279,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_matches_created_yet => 'Noch keine Spiele erstellt'; - @override - String get no_matches_played_yet => 'Noch kein Spiel gespielt'; - @override String get no_players_created_yet => 'Noch keine Spieler:in erstellt'; @@ -364,9 +301,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_statistics_available => 'Keine Statistiken verfügbar'; - @override - String get no_statistics_created_yet => 'Noch keine Statistiken erstellt'; - @override String get none => 'Kein'; @@ -376,9 +310,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get not_available => 'Nicht verfügbar'; - @override - String get not_part_of_any_group => 'Noch keiner Gruppe hinzugefügt'; - @override String get place => 'Platz'; @@ -391,9 +322,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get player_name => 'Spieler:innenname'; - @override - String get player_profile => 'Spieler:in-Profil'; - @override String get players => 'Spieler:innen'; @@ -459,27 +387,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get selected_players => 'Ausgewählte Spieler:innen'; - @override - String get set_name => 'Name setzen'; - @override String get settings => 'Einstellungen'; - @override - String get select_a_classifier => 'Klassifikator auswählen'; - - @override - String get select_a_game => 'Spielvorlage auswählen'; - - @override - String get select_a_group => 'Gruppe auswählen'; - - @override - String get select_a_scope => 'Bereich auswählen'; - - @override - String get select_a_timeframe => 'Zeitraum auswählen'; - @override String get single_loser => 'Ein:e Verlierer:in'; @@ -492,33 +402,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get stats => 'Statistiken'; - @override - String get selected_games => 'Ausgewählte Spielvorlagen'; - - @override - String get selected_groups => 'Ausgewählte Gruppen'; - - @override - String get average_score => 'Durchschnittliche Punktzahl'; - - @override - String get best_score => 'Beste Punktzahl'; - - @override - String get total_losses => 'Niederlagen insgesamt'; - - @override - String get total_matches => 'Spiele insgesamt'; - - @override - String get total_score => 'Punktzahl insgesamt'; - - @override - String get total_wins => 'Siege insgesamt'; - - @override - String get worst_score => 'Schlechteste Punktzahl'; - @override String successfully_added_player(String playerName) { return 'Spieler:in $playerName erfolgreich hinzugefügt'; @@ -539,24 +422,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get tie => 'Unentschieden'; - @override - String get all_time => 'Gesamter Zeitraum'; - - @override - String get last_180_days => 'Letzte 180 Tage'; - - @override - String get last_30_days => 'Letzte 30 Tage'; - - @override - String get last_7_days => 'Letzte 7 Tage'; - - @override - String get last_90_days => 'Letzte 90 Tage'; - - @override - String get last_year => 'Letztes Jahr'; - @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 2275b97..bc083e5 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -62,18 +62,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get color_teal => 'Teal'; - @override - String get displayed_entries => 'Displayed entries'; - @override String get color_yellow => 'Yellow'; @override - String get confirm => 'Confirm'; - - @override - String could_not_add_player(String playerName) { - return 'Could not add player $playerName'; + String could_not_add_player(Object playerName) { + return 'Could not add player'; } @override @@ -91,33 +85,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_new_match => 'Create new match'; - @override - String get create_statistic => 'Create statistic'; - - @override - String get classifier => 'Classifier'; - - @override - String get select_the_filtered_timeframe => - 'Select the timeframe you want to filter by.'; - - @override - String get select_the_filtered_games => - 'Select the games you want to filter by.'; - - @override - String get games => 'Games'; - - @override - String get select_the_filtered_groups => - 'Select the groups you want to filter by.'; - - @override - String get scope => 'Scope'; - - @override - String get timeframe => 'Timeframe'; - @override String get created_on => 'Created on'; @@ -158,18 +125,12 @@ class AppLocalizationsEn extends AppLocalizations { return 'If you delete this game template, $_temp0 using this game template will also be deleted.'; } - @override - String get filter => 'Filter'; - @override String get delete_group => 'Delete Group'; @override String get delete_match => 'Delete Match'; - @override - String get delete_player => 'Delete player?'; - @override String get description => 'Description'; @@ -185,12 +146,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get edit_match => 'Edit Match'; - @override - String get edit_name => 'Edit name'; - - @override - String get edit_player => 'Edit player'; - @override String get enter_points => 'Enter points'; @@ -246,9 +201,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get groups => 'Groups'; - @override - String get groups_part_of => 'Groups part of'; - @override String get highest_score => 'Highest Score'; @@ -282,9 +234,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get live_edit_mode => 'Live Edit Mode'; - @override - String get loading => 'Loading...'; - @override String get loser => 'Loser'; @@ -303,15 +252,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get matches => 'Matches'; - @override - String get matches_part_of => 'Matches part of'; - - @override - String get matches_played => 'Matches played'; - - @override - String get matches_won => 'Matches won'; - @override String get members => 'Members'; @@ -339,9 +279,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_matches_created_yet => 'No matches created yet'; - @override - String get no_matches_played_yet => 'No games played yet'; - @override String get no_players_created_yet => 'No players created yet'; @@ -364,9 +301,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_statistics_available => 'No statistics available'; - @override - String get no_statistics_created_yet => 'No statistics created yet'; - @override String get none => 'None'; @@ -376,9 +310,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get not_available => 'Not available'; - @override - String get not_part_of_any_group => 'Not part of any group yet'; - @override String get place => 'place'; @@ -391,9 +322,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get player_name => 'Player name'; - @override - String get player_profile => 'Player Profile'; - @override String get players => 'Players'; @@ -459,27 +387,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get selected_players => 'Selected players'; - @override - String get set_name => 'Set name'; - @override String get settings => 'Settings'; - @override - String get select_a_classifier => 'Select a classifier'; - - @override - String get select_a_game => 'Select a game'; - - @override - String get select_a_group => 'Select a group'; - - @override - String get select_a_scope => 'Select a scope'; - - @override - String get select_a_timeframe => 'Select a timeframe'; - @override String get single_loser => 'Single Loser'; @@ -492,33 +402,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get stats => 'Stats'; - @override - String get selected_games => 'Selected games'; - - @override - String get selected_groups => 'Selected groups'; - - @override - String get average_score => 'Average score'; - - @override - String get best_score => 'Best score'; - - @override - String get total_losses => 'Total losses'; - - @override - String get total_matches => 'Total matches'; - - @override - String get total_score => 'Total score'; - - @override - String get total_wins => 'Total wins'; - - @override - String get worst_score => 'Worst score'; - @override String successfully_added_player(String playerName) { return 'Successfully added player $playerName'; @@ -538,24 +421,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get tie => 'Tie'; - @override - String get all_time => 'All time'; - - @override - String get last_180_days => 'Last 180 days'; - - @override - String get last_30_days => 'Last 30 days'; - - @override - String get last_7_days => 'Last 7 days'; - - @override - String get last_90_days => 'Last 90 days'; - - @override - String get last_year => 'Last year'; - @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 07d66b4..7e5434b 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -6,7 +6,7 @@ import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/statistics_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart'; diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index 72e4c69..84efbe1 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -89,7 +89,6 @@ class _CreateGroupViewState extends State { Expanded( child: PlayerSelection( initialSelectedPlayers: initialSelectedPlayers, - onPlayerCreated: () => widget.onMembersChanged?.call(), onChanged: (value) { setState(() { selectedPlayers = [...value]; @@ -135,7 +134,6 @@ class _CreateGroupViewState extends State { if (!mounted) return; if (success) { - widget.onMembersChanged?.call(); await HapticFeedback.successNotification(); if (mounted) { Navigator.pop(context, updatedGroup); @@ -159,6 +157,7 @@ class _CreateGroupViewState extends State { final success = await db.groupDao.addGroup( group: Group(name: groupName, members: selectedPlayers), ); + return success; } diff --git a/lib/presentation/views/main_menu/group_view/group_view.dart b/lib/presentation/views/main_menu/group_view/group_view.dart index e53b661..c8a9398 100644 --- a/lib/presentation/views/main_menu/group_view/group_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_view.dart @@ -77,7 +77,6 @@ class _GroupViewState extends State { ); } return GroupTile( - onPlayerChanged: loadGroups, group: groups[index], onTap: () async { await Navigator.push( @@ -107,10 +106,13 @@ class _GroupViewState extends State { context, adaptivePageRoute( builder: (context) { - return CreateGroupView(onMembersChanged: loadGroups); + return const CreateGroupView(); }, ), ); + setState(() { + loadGroups(); + }); }, ), ), diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 4d085d7..3c51cab 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -164,7 +164,7 @@ class _ChooseGameViewState extends State { game.ruleset, context, ), - badgeColor: getColorFromAppColor(game.color), + badgeColor: getColorFromGameColor(game.color), isHighlighted: selectedGameId == game.id, onTap: () async { setState(() { diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 0671055..998f4e1 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -49,10 +49,10 @@ class _CreateGameViewState extends State { late final AppDatabase db; late List<(Ruleset, String)> _rulesets; - late List<(AppColor, String)> _colors; + late List<(GameColor, String)> _colors; Ruleset? selectedRuleset = Ruleset.singleWinner; - AppColor? selectedColor = AppColor.orange; + GameColor? selectedColor = GameColor.orange; /// Controller for the game name input field. final _gameNameController = TextEditingController(); @@ -87,10 +87,10 @@ class _CreateGameViewState extends State { ), ); _colors = List.generate( - AppColor.values.length, + GameColor.values.length, (index) => ( - AppColor.values[index], - translateAppColorToString(AppColor.values[index], context), + GameColor.values[index], + translateGameColorToString(GameColor.values[index], context), ), ); @@ -117,6 +117,7 @@ class _CreateGameViewState extends State { return ScaffoldMessenger( child: Scaffold( + backgroundColor: CustomTheme.backgroundColor, appBar: AppBar( title: Text(isEditing ? loc.edit_game : loc.create_game), actions: [ @@ -467,7 +468,7 @@ class _CreateGameViewState extends State { height: 16, margin: const EdgeInsets.only(left: 12), decoration: BoxDecoration( - color: getColorFromAppColor( + color: getColorFromGameColor( _colors[index].$1, ), shape: BoxShape.circle, @@ -501,13 +502,13 @@ class _CreateGameViewState extends State { width: 16, height: 16, decoration: BoxDecoration( - color: getColorFromAppColor(selectedColor!), + color: getColorFromGameColor(selectedColor!), shape: BoxShape.circle, ), ), Padding( padding: const EdgeInsets.only(right: 5), - child: Text(translateAppColorToString(selectedColor!, context)), + child: Text(translateGameColorToString(selectedColor!, context)), ), Transform.rotate( angle: pi / 2, diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index c8790be..85bb936 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -196,7 +196,6 @@ class _CreateMatchViewState extends State { child: PlayerSelection( key: ValueKey(selectedGroup?.id ?? 'no_group'), initialSelectedPlayers: selectedPlayers, - onPlayerCreated: () => widget.onMatchesUpdated?.call(), onChanged: (value) { setState(() { selectedPlayers = value; diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index 1d30afb..a7f60c6 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -39,7 +39,7 @@ class _MatchViewState extends State { game: Game( name: 'Game name', ruleset: Ruleset.singleWinner, - color: AppColor.blue, + color: GameColor.blue, icon: '', ), group: Group( @@ -79,7 +79,7 @@ class _MatchViewState extends State { visible: matches.isNotEmpty, replacement: Center( child: TopCenteredMessage( - icon: Icons.info, + icon: Icons.report, title: loc.info, message: loc.no_matches_created_yet, ), @@ -97,7 +97,6 @@ class _MatchViewState extends State { child: Padding( padding: const EdgeInsets.only(bottom: 12.0), child: MatchTile( - onPlayerEdited: loadMatches, width: MediaQuery.sizeOf(context).width * 0.95, onTap: () async { Navigator.push( diff --git a/lib/presentation/views/main_menu/player_detail_view.dart b/lib/presentation/views/main_menu/player_detail_view.dart deleted file mode 100644 index e26c327..0000000 --- a/lib/presentation/views/main_menu/player_detail_view.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/core/common.dart'; -import 'package:tallee/core/custom_theme.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; -import 'package:tallee/data/models/match.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/widgets/app_skeleton.dart'; -import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; -import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; -import 'package:tallee/presentation/widgets/colored_icon_container.dart'; -import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; -import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; -import 'package:tallee/presentation/widgets/text_input/text_input_field.dart'; -import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; -import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; - -class PlayerDetailView extends StatefulWidget { - const PlayerDetailView({ - super.key, - required this.player, - required this.callback, - }); - - /// The player to display - final Player player; - - final VoidCallback callback; - - @override - State createState() => _PlayerDetailViewState(); -} - -class _PlayerDetailViewState extends State { - late final AppDatabase db; - late Player _player; - late String playerNameCount; - bool isLoading = true; - - /// Total matches played by this player - int totalMatches = 0; - - /// Total matches won by this player - int matchesWon = 0; - - /// Total groups this player belongs to - int totalGroups = 0; - - /// Full list of groups this player belongs to - List playerGroups = List.filled( - 4, - Group(name: 'Skeleton group', members: []), - ); - - /// Full list of matches this player played in - List playerMatches = List.filled( - 4, - Match( - name: 'Skeleton match', - game: Game(name: 'Game name', ruleset: Ruleset.singleWinner), - players: [], - ), - ); - - TextEditingController nameController = TextEditingController(); - - @override - void initState() { - super.initState(); - _player = widget.player; - db = Provider.of(context, listen: false); - playerNameCount = getNameCountText(_player); - _loadData(); - } - - @override - void dispose() { - nameController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context); - - return Scaffold( - appBar: AppBar( - title: Text(loc.player_profile), - actions: [ - HapticIconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - showDialog( - context: context, - builder: (context) => CustomAlertDialog( - title: loc.delete_player, - content: Text(loc.this_cannot_be_undone), - actions: [ - CustomDialogAction( - onPressed: () => Navigator.of(context).pop(true), - text: loc.delete, - ), - CustomDialogAction( - onPressed: () => Navigator.of(context).pop(false), - buttonType: ButtonType.secondary, - text: loc.cancel, - ), - ], - ), - ).then((confirmed) async { - if (confirmed! && context.mounted) { - //TODO: implement player deletion in db - if (!context.mounted) return; - Navigator.pop(context); - widget.callback(); - } - }); - }, - ), - ], - ), - body: SafeArea( - child: Stack( - alignment: Alignment.center, - children: [ - ListView( - padding: const EdgeInsets.only( - left: 12, - right: 12, - top: 20, - bottom: 100, - ), - children: [ - const Center( - child: ColoredIconContainer( - icon: Icons.person, - containerSize: 55, - iconSize: 38, - ), - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - _player.name, - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor, - ), - textAlign: TextAlign.center, - ), - Text( - playerNameCount, - style: TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor.withAlpha(120), - ), - textAlign: TextAlign.center, - ), - ], - ), - const SizedBox(height: 5), - Text( - '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_player.createdAt)}', - style: const TextStyle( - fontSize: 12, - color: CustomTheme.textColor, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 20), - InfoTile( - title: '${loc.matches_part_of} ($totalMatches)', - icon: Icons.sports_esports, - horizontalAlignment: CrossAxisAlignment.start, - content: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - alignment: Alignment.topLeft, - child: playerMatches.isNotEmpty - ? Wrap( - alignment: WrapAlignment.start, - crossAxisAlignment: WrapCrossAlignment.start, - spacing: 12, - runSpacing: 8, - children: playerMatches.map((match) { - return TextIconTile( - text: match.name, - iconEnabled: false, - ); - }).toList(), - ) - : Text( - loc.no_matches_played_yet, - style: const TextStyle( - fontSize: 14, - color: CustomTheme.textColor, - ), - ), - ), - ), - const SizedBox(height: 15), - InfoTile( - title: '${loc.groups_part_of} ($totalGroups)', - icon: Icons.people, - horizontalAlignment: CrossAxisAlignment.start, - content: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - alignment: Alignment.topLeft, - child: playerGroups.isNotEmpty - ? Wrap( - alignment: WrapAlignment.start, - crossAxisAlignment: WrapCrossAlignment.start, - spacing: 12, - runSpacing: 8, - children: playerGroups.map((group) { - return TextIconTile( - text: group.name, - iconEnabled: false, - ); - }).toList(), - ) - : Text( - loc.not_part_of_any_group, - style: const TextStyle( - fontSize: 14, - color: CustomTheme.textColor, - ), - ), - ), - ), - const SizedBox(height: 15), - InfoTile( - title: loc.statistics, - icon: Icons.bar_chart, - content: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: Column( - children: [ - _buildStatRow( - loc.matches_played, - totalMatches.toString(), - ), - _buildStatRow(loc.matches_won, matchesWon.toString()), - _buildStatRow( - loc.winrate, - '${totalMatches == 0 ? 0 : ((matchesWon / totalMatches) * 100).round()}%', - ), - ], - ), - ), - ), - ], - ), - Positioned( - bottom: MediaQuery.paddingOf(context).bottom, - child: MainMenuButton( - text: loc.edit_player, - icon: Icons.edit, - onPressed: () async { - nameController.text = _player.name; - showDialog( - context: context, - builder: (context) => StatefulBuilder( - builder: (context, setDialogState) { - return CustomAlertDialog( - title: loc.edit_name, - content: TextInputField( - controller: nameController, - hintText: loc.set_name, - onChanged: (_) => setDialogState(() {}), - ), - actions: [ - CustomDialogAction( - onPressed: isConfirmButtonEnabled() - ? () => Navigator.of(context).pop(true) - : null, - text: loc.confirm, - ), - CustomDialogAction( - onPressed: () => Navigator.of(context).pop(false), - buttonType: ButtonType.secondary, - text: loc.cancel, - ), - ], - ); - }, - ), - ).then((confirmed) async { - if (confirmed! && context.mounted) { - final newName = nameController.text.trim(); - - if (newName != _player.name) { - final fetchedPlayerNameCount = await db.playerDao - .getNameCount(name: newName); - await db.playerDao.updatePlayerName( - playerId: _player.id, - name: newName, - ); - widget.callback.call(); - setState(() { - _player = Player( - name: newName, - createdAt: _player.createdAt, - id: _player.id, - nameCount: _player.nameCount, - description: _player.description, - ); - - // If there is already a player with the same name, - // the count of that player is 0, so we start counting from 2 to get the correct count for this player. If there are no players with the same name, we just show the name without a count. - playerNameCount = fetchedPlayerNameCount == 0 - ? '' - : ' #${fetchedPlayerNameCount + 1}'; - }); - } - } - }); - }, - ), - ), - ], - ), - ), - ); - } - - /// Loads statistics for this player - Future _loadData() async { - isLoading = true; - final fetchedMatches = await db.matchDao.getMatchesByPlayer( - playerId: _player.id, - ); - final fetchedGroups = await db.groupDao.getGroupsByPlayer( - playerId: _player.id, - ); - - if (!mounted) return; - - setState(() { - playerMatches = fetchedMatches; - totalMatches = fetchedMatches.length; - matchesWon = fetchedMatches - .where((match) => match.mvp.any((mvp) => mvp.id == _player.id)) - .length; - playerGroups = fetchedGroups; - totalGroups = fetchedGroups.length; - isLoading = false; - }); - } - - /// Builds a single statistic row with a label and value - /// - [label]: The label of the statistic - /// - [value]: The value of the statistic - Widget _buildStatRow(String label, String value) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Text( - label, - style: const TextStyle( - fontSize: 16, - color: CustomTheme.textColor, - ), - ), - ], - ), - Text( - value, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ], - ), - ); - } - - bool isConfirmButtonEnabled() { - return nameController.text.trim().isNotEmpty; - } -} diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view.dart new file mode 100644 index 0000000..8659a2e --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view.dart @@ -0,0 +1,311 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/constants.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/app_skeleton.dart'; +import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; +import 'package:tallee/presentation/widgets/top_centered_message.dart'; + +class StatisticsView extends StatefulWidget { + /// A view that displays player statistics + const StatisticsView({super.key}); + + @override + State createState() => _StatisticsViewState(); +} + +class _StatisticsViewState extends State { + int matchCount = 0; + int groupCount = 0; + + List<(Player, int)> winCounts = List.filled(6, ( + Player(name: 'Skeleton Player'), + 1, + )); + List<(Player, int)> matchCounts = List.filled(6, ( + Player(name: 'Skeleton Player'), + 1, + )); + List<(Player, double)> winRates = List.filled(6, ( + Player(name: 'Skeleton Player'), + 1, + )); + bool isLoading = true; + + @override + void initState() { + super.initState(); + loadStatisticData(); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return SingleChildScrollView( + child: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: ConstrainedBox( + constraints: BoxConstraints(minWidth: constraints.maxWidth), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + QuickInfoTile( + width: constraints.maxWidth * 0.45, + height: constraints.maxHeight * 0.13, + title: loc.matches, + icon: Icons.groups_rounded, + value: matchCount, + ), + SizedBox(width: constraints.maxWidth * 0.05), + QuickInfoTile( + width: constraints.maxWidth * 0.45, + height: constraints.maxHeight * 0.13, + title: loc.groups, + icon: Icons.groups_rounded, + value: groupCount, + ), + ], + ), + SizedBox(height: constraints.maxHeight * 0.02), + Visibility( + visible: + winCounts.isEmpty && + matchCounts.isEmpty && + winRates.isEmpty, + replacement: Column( + children: [ + StatisticsTile( + icon: Icons.sports_score, + title: loc.wins, + width: constraints.maxWidth * 0.95, + values: winCounts, + itemCount: 3, + barColor: Colors.green, + ), + SizedBox(height: constraints.maxHeight * 0.02), + StatisticsTile( + icon: Icons.percent, + title: loc.winrate, + width: constraints.maxWidth * 0.95, + values: winRates, + itemCount: 5, + barColor: Colors.orange[700]!, + ), + SizedBox(height: constraints.maxHeight * 0.02), + StatisticsTile( + icon: Icons.casino, + title: loc.amount_of_matches, + width: constraints.maxWidth * 0.95, + values: matchCounts, + itemCount: 10, + barColor: Colors.blue, + ), + ], + ), + child: TopCenteredMessage( + icon: Icons.info, + title: loc.info, + message: AppLocalizations.of( + context, + ).no_statistics_available, + ), + ), + SizedBox(height: MediaQuery.paddingOf(context).bottom), + ], + ), + ), + ), + ); + }, + ); + } + + /// Loads matches and players from the database + /// and calculates statistics for each player + void loadStatisticData() { + final db = Provider.of(context, listen: false); + + Future.wait([ + db.matchDao.getAllMatches(), + db.playerDao.getAllPlayers(), + db.matchDao.getMatchCount(), + db.groupDao.getGroupCount(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]).then((results) async { + if (!mounted) return; + + final matches = results[0] as List; + final players = results[1] as List; + matchCount = results[2] as int; + groupCount = results[3] as int; + + winCounts = _calculateWinsForAllPlayers( + matches: matches, + players: players, + context: context, + ); + matchCounts = _calculateMatchAmountsForAllPlayers( + matches: matches, + players: players, + context: context, + ); + winRates = computeWinRatePercent( + winCounts: winCounts, + matchCounts: matchCounts, + ); + + setState(() { + isLoading = false; + }); + }); + } + + /// Calculates the number of wins for each player + /// and returns a sorted list of tuples (playerName, winCount) + List<(Player, int)> _calculateWinsForAllPlayers({ + required List matches, + required List players, + required BuildContext context, + }) { + List<(Player, int)> winCounts = []; + final loc = AppLocalizations.of(context); + + // Getting the winners + for (var match in matches) { + final mvps = match.mvp; + for (var winner in mvps) { + final index = winCounts.indexWhere((entry) => entry.$1.id == winner.id); + // -1 means winner not found in winCounts + if (index != -1) { + final current = winCounts[index].$2; + winCounts[index] = (winner, current + 1); + } else { + winCounts.add((winner, 1)); + } + } + } + + // Adding all players with zero wins + for (var player in players) { + final index = winCounts.indexWhere((entry) => entry.$1.id == player.id); + // -1 means player not found in winCounts + if (index == -1) { + winCounts.add((player, 0)); + } + } + + // Replace player IDs with names + for (int i = 0; i < winCounts.length; i++) { + final playerId = winCounts[i].$1.id; + final player = players.firstWhere( + (p) => p.id == playerId, + orElse: () => Player(id: playerId, name: loc.not_available), + ); + winCounts[i] = (player, winCounts[i].$2); + } + + winCounts.sort((a, b) => b.$2.compareTo(a.$2)); + + return winCounts; + } + + /// Calculates the number of matches played for each player + /// and returns a sorted list of tuples (playerName, matchCount) + List<(Player, int)> _calculateMatchAmountsForAllPlayers({ + required List matches, + required List players, + required BuildContext context, + }) { + List<(Player, int)> matchCounts = []; + final loc = AppLocalizations.of(context); + + // Counting matches for each player + for (var match in matches) { + for (Player player in match.players) { + // Check if the player is already in matchCounts + final index = matchCounts.indexWhere( + (entry) => entry.$1.id == player.id, + ); + + // -1 -> not found + if (index == -1) { + // Add new entry + matchCounts.add((player, 1)); + } else { + // Update existing entry + final currentMatchAmount = matchCounts[index].$2; + matchCounts[index] = (player, currentMatchAmount + 1); + } + } + } + + // Adding all players with zero matches + for (var player in players) { + final index = matchCounts.indexWhere((entry) => entry.$1.id == player.id); + // -1 means player not found in matchCounts + if (index == -1) { + matchCounts.add((player, 0)); + } + } + + // Replace player IDs with names + for (int i = 0; i < matchCounts.length; i++) { + final playerId = matchCounts[i].$1.id; + final player = players.firstWhere( + (p) => p.id == playerId, + orElse: () => Player(id: playerId, name: loc.not_available), + ); + matchCounts[i] = (player, matchCounts[i].$2); + } + + matchCounts.sort((a, b) => b.$2.compareTo(a.$2)); + + return matchCounts; + } + + List<(Player, double)> computeWinRatePercent({ + required List<(Player, int)> winCounts, + required List<(Player, int)> matchCounts, + }) { + final Map winsMap = {for (var e in winCounts) e.$1: e.$2}; + final Map matchesMap = {for (var e in matchCounts) e.$1: e.$2}; + + // Get all unique player names + final player = {...matchesMap.keys}; + + // Calculate win rates + final result = player.map((name) { + final int w = winsMap[name] ?? 0; + final int m = matchesMap[name] ?? 0; + // Calculate percentage and round to 2 decimal places + // Avoid division by zero + final double percent = (m > 0) + ? double.parse(((w / m)).toStringAsFixed(2)) + : 0; + return (name, percent); + }).toList(); + + // Sort the result: first by winrate descending, + // then by wins descending in case of a tie + result.sort((a, b) { + final cmp = b.$2.compareTo(a.$2); + if (cmp != 0) return cmp; + final wa = winsMap[a.$1] ?? 0; + final wb = winsMap[b.$1] ?? 0; + return wb.compareTo(wa); + }); + + return result; + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart deleted file mode 100644 index a88432a..0000000 --- a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart +++ /dev/null @@ -1,635 +0,0 @@ -import 'package:animated_custom_dropdown/custom_dropdown.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/core/common.dart'; -import 'package:tallee/core/constants.dart'; -import 'package:tallee/core/custom_theme.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; - -class CreateStatisticView extends StatefulWidget { - const CreateStatisticView({super.key, required this.onStatisticCreated}); - - final void Function() onStatisticCreated; - - @override - State createState() => _CreateStatisticViewState(); -} - -class _CreateStatisticViewState extends State { - bool isLoading = false; - - /* Data loaded from the database */ - List players = []; - List games = []; - List groups = []; - - /* User selections */ - StatisticType? selectedType; - List selectedScope = []; - List selectedGames = []; - List selectedPlayers = []; - List selectedGroups = []; - Timeframe? selectedTimeframe; - - @override - void initState() { - loadAllData(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - var loc = AppLocalizations.of(context); - - return ScaffoldMessenger( - child: Scaffold( - appBar: AppBar(title: Text(loc.create_statistic)), - body: Stack( - alignment: AlignmentDirectional.center, - children: [ - SingleChildScrollView( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 80, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Classifier title - Padding( - padding: const EdgeInsetsGeometry.only(left: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - loc.classifier, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - const Text( - 'description', - textAlign: TextAlign.start, - style: TextStyle( - color: CustomTheme.textColor, - fontSize: 12, - ), - softWrap: true, - ), - ], - ), - ), - - // Classifier selection - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: CustomDropdown( - closedHeaderPadding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 16, - ), - listItemBuilder: - (context, item, isSelected, onItemSelect) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - translateStatisticTypeToString(item, context), - style: itemStyle, - ), - if (isSelected) - const Icon( - Icons.check, - color: CustomTheme.textColor, - ), - ], - ), - headerBuilder: (context, selectedType, enabled) => Text( - translateStatisticTypeToString(selectedType, context), - style: headerStyle, - ), - hintText: loc.select_a_classifier, - items: StatisticType.values, - decoration: decoration, - onChanged: (value) { - setState(() { - selectedType = value; - }); - }, - ), - ), - - const SizedBox(height: 10), - - // Scope title - Padding( - padding: const EdgeInsetsGeometry.only(left: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - loc.scope, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - const Text( - 'description', - textAlign: TextAlign.start, - style: TextStyle( - color: CustomTheme.textColor, - fontSize: 12, - ), - ), - ], - ), - ), - - // Scope selection - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: CustomDropdown.multiSelect( - closedHeaderPadding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 16, - ), - hintText: loc.select_a_scope, - items: StatisticScope.values, - decoration: decoration, - listItemBuilder: - (context, scope, isSelected, onItemSelect) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - translateScopeToString(scope, context), - style: itemStyle, - ), - if (isSelected) - const Icon( - Icons.check, - color: CustomTheme.textColor, - ), - ], - ), - headerListBuilder: (context, selectedItems, enabled) => - Text( - selectedItems - .map((s) => translateScopeToString(s, context)) - .join(', '), - style: headerStyle, - overflow: TextOverflow.ellipsis, - ), - onListChanged: (List values) { - setState(() { - selectedScope = values; - }); - }, - ), - ), - - if (selectedScope.contains(StatisticScope.selectedGames)) ...[ - const SizedBox(height: 10), - - // games title - Padding( - padding: const EdgeInsetsGeometry.only(left: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - loc.games, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - Text( - loc.select_the_filtered_games, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 12, - ), - ), - ], - ), - ), - - // game selection - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: CustomDropdown.multiSelect( - enabled: !isLoading, - disabledDecoration: disabledDecoration, - closedHeaderPadding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 16, - ), - hintText: isLoading ? loc.loading : loc.select_a_game, - items: games, - decoration: decoration, - listItemBuilder: - (context, item, isSelected, onItemSelect) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // Name - Text(item.name, style: itemStyle), - const SizedBox(width: 12), - - // Ruleset - Text( - translateRulesetToString( - item.ruleset, - context, - ), - style: hintStyle.copyWith(fontSize: 12), - ), - ], - ), - - // Check icon - if (isSelected) - const Icon( - Icons.check, - color: CustomTheme.textColor, - ), - ], - ), - headerListBuilder: (context, selectedItems, enabled) => - Text( - selectedItems.map((g) => g.name).join(', '), - style: const TextStyle( - color: CustomTheme.textColor, - fontWeight: FontWeight.bold, - ), - overflow: TextOverflow.ellipsis, - ), - onListChanged: (List values) { - setState(() { - selectedGames = values; - }); - }, - ), - ), - ], - - if (selectedScope.contains( - StatisticScope.selectedGroups, - )) ...[ - const SizedBox(height: 10), - - // groups title - Padding( - padding: const EdgeInsetsGeometry.only(left: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - loc.groups, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - Text( - loc.select_the_filtered_groups, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 12, - ), - ), - ], - ), - ), - - // groups selection - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: CustomDropdown.multiSelect( - enabled: !isLoading, - disabledDecoration: disabledDecoration, - closedHeaderPadding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 16, - ), - hintText: isLoading ? loc.loading : loc.select_a_group, - items: groups, - decoration: decoration, - listItemBuilder: - (context, item, isSelected, onItemSelect) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // Name - Text(item.name, style: itemStyle), - const SizedBox(width: 12), - - // Ruleset - Text( - ' ${item.members.length.toString()} ${loc.members}', - style: hintStyle.copyWith(fontSize: 12), - ), - ], - ), - if (isSelected) - const Icon( - Icons.check, - color: CustomTheme.textColor, - ), - ], - ), - headerListBuilder: (context, selectedItems, enabled) => - Text( - selectedItems.map((g) => g.name).join(', '), - style: headerStyle, - overflow: TextOverflow.ellipsis, - ), - onListChanged: (List groups) { - setState(() { - selectedGroups = groups; - }); - }, - ), - ), - ], - - if (selectedScope.contains(StatisticScope.timeframe)) ...[ - const SizedBox(height: 10), - - // timeframe title - Padding( - padding: const EdgeInsetsGeometry.only(left: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - loc.timeframe, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - Text( - loc.select_the_filtered_timeframe, - textAlign: TextAlign.start, - style: const TextStyle( - color: CustomTheme.textColor, - fontSize: 12, - ), - ), - ], - ), - ), - - // groups selection - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, - horizontal: 16, - ), - child: CustomDropdown( - enabled: !isLoading, - excludeSelected: false, - disabledDecoration: disabledDecoration, - closedHeaderPadding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 16, - ), - hintText: isLoading - ? loc.loading - : loc.select_a_timeframe, - items: Timeframe.values, - decoration: decoration, - listItemBuilder: - (context, timeframe, isSelected, onItemSelect) => - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - translateTimeframeToString( - timeframe, - context, - ), - style: itemStyle, - ), - if (isSelected) - const Icon( - Icons.check, - color: CustomTheme.textColor, - ), - ], - ), - headerBuilder: (context, selectedTimeframe, enabled) => - Text( - translateTimeframeToString( - selectedTimeframe, - context, - ), - style: headerStyle, - overflow: TextOverflow.ellipsis, - ), - onChanged: (Timeframe? timeframe) { - setState(() { - selectedTimeframe = timeframe; - }); - }, - ), - ), - ], - ], - ), - ), - - // Create statistic button - Positioned( - bottom: MediaQuery.of(context).padding.bottom, - child: AnimatedDialogButton( - buttonConstraints: const BoxConstraints(minWidth: 350), - buttonText: loc.create_statistic, - onPressed: selectedType != null && selectedScope.isNotEmpty - ? () => submitStatistic() - : null, - ), - ), - ], - ), - ), - ); - } - - CustomDropdownDecoration get decoration => CustomDropdownDecoration( - listItemDecoration: const ListItemDecoration( - selectedIconBorder: BorderSide(color: CustomTheme.primaryColor, width: 1), - selectedIconColor: CustomTheme.primaryColor, - highlightColor: CustomTheme.secondaryColor, - splashColor: Colors.transparent, - selectedColor: CustomTheme.onBoxColor, - ), - listItemStyle: itemStyle, - headerStyle: headerStyle, - hintStyle: hintStyle, - closedFillColor: CustomTheme.boxColor, - closedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), - expandedFillColor: CustomTheme.boxColor, - expandedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), - ); - - CustomDropdownDisabledDecoration get disabledDecoration => - CustomDropdownDisabledDecoration( - fillColor: CustomTheme.boxColor.withAlpha(125), - border: Border.all( - color: CustomTheme.boxBorderColor.withAlpha(125), - width: 1, - ), - headerStyle: disabledHeaderStyle, - hintStyle: disabledHintStyle, - ); - - TextStyle get headerStyle => const TextStyle( - color: CustomTheme.textColor, - fontSize: 14, - fontWeight: FontWeight.bold, - ); - - TextStyle get itemStyle => - const TextStyle(color: CustomTheme.textColor, fontSize: 14); - - TextStyle get hintStyle => - const TextStyle(color: CustomTheme.hintColor, fontSize: 14); - - TextStyle get disabledHeaderStyle => const TextStyle( - color: CustomTheme.hintColor, - fontSize: 14, - fontWeight: FontWeight.bold, - ); - - TextStyle get disabledHintStyle => - const TextStyle(color: CustomTheme.hintColor, fontSize: 14); - - Future loadAllData() async { - isLoading = true; - final db = Provider.of(context, listen: false); - - Future.wait([ - db.playerDao.getAllPlayers(), - db.groupDao.getAllGroups(), - db.gameDao.getAllGames(), - Future.delayed(Constants.MINIMUM_SKELETON_DURATION), - ]) - .then((results) async { - players = results[0]; - groups = results[1]; - games = results[2]; - isLoading = false; - }) - .catchError((error) { - print('Error loading data: $error'); - }); - } - - void submitStatistic() { - final newStatistic = Statistic( - type: selectedType!, - scopes: selectedScope, - timeframe: selectedTimeframe, - selectedGroups: selectedGroups, - selectedGames: selectedGames, - ); - final db = Provider.of(context, listen: false); - db.statisticDao.addStatistic(statistic: newStatistic); - Navigator.of(context).pop(newStatistic); - } -} - -String translateTimeframeToString(Timeframe timeframe, BuildContext context) { - final loc = AppLocalizations.of(context); - switch (timeframe) { - case Timeframe.last7Days: - return loc.last_7_days; - case Timeframe.last30Days: - return loc.last_30_days; - case Timeframe.last90Days: - return loc.last_90_days; - case Timeframe.last180Days: - return loc.last_180_days; - case Timeframe.lastYear: - return loc.last_year; - case Timeframe.allTime: - return loc.all_time; - } -} - -String translateScopeToString(StatisticScope scope, BuildContext context) { - final loc = AppLocalizations.of(context); - switch (scope) { - case StatisticScope.allPlayers: - return loc.all_players; - case StatisticScope.selectedGroups: - return loc.selected_groups; - case StatisticScope.selectedGames: - return loc.selected_games; - case StatisticScope.timeframe: - return loc.timeframe; - } -} - -String translateStatisticTypeToString( - StatisticType type, - BuildContext context, -) { - final loc = AppLocalizations.of(context); - switch (type) { - case StatisticType.totalMatches: - return loc.total_matches; - case StatisticType.totalWins: - return loc.total_wins; - case StatisticType.totalScore: - return loc.total_score; - case StatisticType.totalLosses: - return loc.total_losses; - case StatisticType.averageScore: - return loc.average_score; - case StatisticType.bestScore: - return loc.best_score; - case StatisticType.worstScore: - return loc.worst_score; - case StatisticType.winrate: - return loc.winrate; - } -} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart deleted file mode 100644 index f1bc209..0000000 --- a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; -import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; -import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; -import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; - -class StatisticDetailView extends StatefulWidget { - const StatisticDetailView({ - super.key, - required this.statistic, - required this.values, - required this.icon, - required this.barColor, - }); - - final Statistic statistic; - final List<(Player, num)> values; - final IconData icon; - final Color barColor; - - @override - State createState() => _StatisticDetailViewState(); -} - -class _StatisticDetailViewState extends State { - late int displayCount; - - @override - void initState() { - super.initState(); - displayCount = widget.statistic.displayCount; - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context); - final title = translateStatisticTypeToString( - widget.statistic.type, - context, - ); - const style = TextStyle(fontWeight: FontWeight.bold); - - return Scaffold( - appBar: AppBar( - title: Text(title), - leading: HapticIconButton( - icon: const Icon(Icons.arrow_back_ios_new), - onPressed: () => handleBack(context), - ), - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(12.0), - child: Column( - children: [ - StatisticsTile( - icon: widget.icon, - title: title, - width: MediaQuery.sizeOf(context).width * 0.95, - values: widget.values, - barColor: widget.barColor, - selectedGroups: widget.statistic.selectedGroups, - selectedGames: widget.statistic.selectedGames, - displayCount: displayCount, - showAllValues: true, - ), - const SizedBox(height: 12), - - InfoTile( - icon: Icons.filter_alt, - title: loc.filter, - content: Column( - spacing: 12, - - children: [ - // Scopes - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(loc.scope, style: style), - Text( - widget.statistic.scopes - .map( - (scope) => translateScopeToString(scope, context), - ) - .join('\n'), - textAlign: TextAlign.end, - ), - ], - ), - - // Timeframe - if (widget.statistic.timeframe != null) - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(loc.timeframe, style: style), - Text( - translateTimeframeToString( - widget.statistic.timeframe!, - context, - ), - textAlign: TextAlign.end, - ), - ], - ), - - // Groups - if (widget.statistic.selectedGroups != null) - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(loc.groups, style: style), - Text( - widget.statistic.selectedGroups! - .map((group) => group.name) - .join('\n'), - textAlign: TextAlign.end, - ), - ], - ), - - // Games - if (widget.statistic.selectedGames != null) - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(loc.games, style: style), - Text( - widget.statistic.selectedGames! - .map((game) => game.name) - .join('\n'), - textAlign: TextAlign.end, - ), - ], - ), - - if (widget.values.isNotEmpty) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(loc.displayed_entries, style: style), - Row( - children: [ - HapticIconButton( - icon: const Icon(Icons.remove), - onPressed: displayCount <= 1 - ? null - : () => setState(() => displayCount -= 1), - ), - SizedBox( - width: 30, - child: Text( - '$displayCount', - textAlign: TextAlign.center, - ), - ), - HapticIconButton( - icon: const Icon(Icons.add), - onPressed: displayCount >= widget.values.length - ? null - : () => setState(() => displayCount += 1), - ), - ], - ), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } - - // Handles saving the display count and giving it to statistics view - Future handleBack(BuildContext context) async { - final db = Provider.of(context, listen: false); - await db.statisticDao.updateDisplayCount(widget.statistic.id, displayCount); - if (context.mounted) Navigator.of(context).pop(displayCount); - } -} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart deleted file mode 100644 index 807358e..0000000 --- a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart +++ /dev/null @@ -1,322 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:tallee/core/common.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; -import 'package:tallee/data/models/match.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' - show translateStatisticTypeToString; -import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; - -List _colorPalette = AppColor.values - .map((c) => getColorFromAppColor(c)) - .toList(); - -/// Returns the icon for the given statistic type. -IconData getStatisticIconForType(StatisticType type) => - _getStatisticIcon(type: type); - -/// Returns a color from the palette based on the statistic's ID. -Color getStatisticColorForStatistic(Statistic stat) => _getStatisticColor(stat); - -/// Computes the statistic values for a given [Statistic]. -List<(Player, num)> computeStatisticValues({ - required Statistic statistic, - required List matches, - required List players, -}) { - final filteredMatches = _getFilterMatches(statistic, matches); - final filteredPlayers = _getFilteredPlayers( - statistic, - players, - filteredMatches, - ); - - return _computeValuesForType( - type: statistic.type, - matches: filteredMatches, - players: filteredPlayers, - ); -} - -/// Build the [StatisticsTile] for a given [Statistic]. -Widget buildStatisticTile({ - required Statistic statistic, - required List matches, - required List players, - required BuildContext context, - double? width, -}) { - final values = computeStatisticValues( - statistic: statistic, - matches: matches, - players: players, - ); - - return StatisticsTile( - icon: _getStatisticIcon(type: statistic.type), - title: translateStatisticTypeToString(statistic.type, context), - width: width ?? MediaQuery.sizeOf(context).width * 0.95, - values: values, - barColor: _getStatisticColor(statistic), - displayCount: statistic.displayCount, - selectedGroups: statistic.selectedGroups, - selectedGames: statistic.selectedGames, - ); -} - -List _getFilterMatches(Statistic statistic, List matches) { - List filteredMatches = matches; - - // Filter timeframe - if (statistic.scopes.contains(StatisticScope.timeframe) && - statistic.timeframe != null) { - final minDate = _getMinimumDate(timeframe: statistic.timeframe!); - print( - 'Filtering matches by timeframe: ${statistic.timeframe}, minDate: $minDate', - ); - if (minDate != null) { - filteredMatches = matches - .where((m) => m.endedAt != null && m.endedAt!.isAfter(minDate)) - .toList(); - } - } - - // Filter games - if (statistic.scopes.contains(StatisticScope.selectedGames) && - (statistic.selectedGames?.isNotEmpty ?? false)) { - final gameIds = statistic.selectedGames!.map((g) => g.id).toSet(); - filteredMatches = filteredMatches - .where((match) => gameIds.contains(match.game.id)) - .toList(); - } - - // Filter groups - if (statistic.scopes.contains(StatisticScope.selectedGroups) && - (statistic.selectedGroups?.isNotEmpty ?? false)) { - final groupIds = statistic.selectedGroups!.map((g) => g.id).toSet(); - filteredMatches = filteredMatches - .where((m) => m.group != null && groupIds.contains(m.group!.id)) - .toList(); - } - - return filteredMatches; -} - -/// Returns a [Player] List with the selected players depending on -List _getFilteredPlayers( - Statistic statistic, - List allPlayers, - List filteredMatches, -) { - // allPlayers - if (statistic.scopes.contains(StatisticScope.allPlayers)) { - return allPlayers; - } - - // selectedGroups -> only members - if (statistic.scopes.contains(StatisticScope.selectedGroups) && - (statistic.selectedGroups?.isNotEmpty ?? false)) { - final Set ids = { - for (final g in statistic.selectedGroups!) - for (final p in g.members) p.id, - }; - return allPlayers.where((p) => ids.contains(p.id)).toList(); - } - - // Else -> all players from filtered matches - final Set ids = { - for (final m in filteredMatches) - for (final p in m.players) p.id, - }; - return allPlayers.where((p) => ids.contains(p.id)).toList(); -} - -/// Returns a [DateTime] with the minimum time and date the [timeframe] allows -DateTime? _getMinimumDate({required Timeframe timeframe}) { - final now = DateTime.now(); - switch (timeframe) { - case Timeframe.last7Days: - return now.subtract(const Duration(days: 7)); - case Timeframe.last30Days: - return now.subtract(const Duration(days: 30)); - case Timeframe.last90Days: - return now.subtract(const Duration(days: 90)); - case Timeframe.last180Days: - return now.subtract(const Duration(days: 180)); - case Timeframe.lastYear: - return now.subtract(const Duration(days: 365)); - case Timeframe.allTime: - return null; - } -} - -/// Computes the statistic values for each player based on the statistic type -/// and returns a list of (Player, value) tuples sorted descending by value. -List<(Player, num)> _computeValuesForType({ - required StatisticType type, - required List matches, - required List players, -}) { - switch (type) { - case StatisticType.totalMatches: - return _sortDesc( - players.map((p) => (p, _matchesPlayed(p, matches) as num)).toList(), - ); - - case StatisticType.totalWins: - return _sortDesc( - players.map((p) => (p, _wins(p, matches) as num)).toList(), - ); - - case StatisticType.totalLosses: - return _sortDesc( - players - .map( - (p) => - (p, (_matchesPlayed(p, matches) - _wins(p, matches)) as num), - ) - .toList(), - ); - - case StatisticType.totalScore: - return _sortDesc( - players.map((p) => (p, _totalScore(p, matches) as num)).toList(), - ); - - case StatisticType.averageScore: - return _sortDesc( - players.map((p) { - final scores = _scoresOf(p, matches); - final avg = scores.isEmpty - ? 0.0 - : double.parse( - (scores.reduce((a, b) => a + b) / scores.length) - .toStringAsFixed(2), - ); - return (p, avg as num); - }).toList(), - ); - - case StatisticType.bestScore: - return _sortDesc( - players.map((p) { - final scores = _scoresOf(p, matches); - final best = scores.isEmpty ? 0 : scores.reduce(max); - return (p, best as num); - }).toList(), - ); - - case StatisticType.worstScore: - // Ascending here is more meaningful for "worst", but keep the - // existing tile semantics (largest bar = top entry) by sorting - // descending on the inverse — i.e. show smallest score on top. - final entries = players.map((p) { - final scores = _scoresOf(p, matches); - final worst = scores.isEmpty ? 0 : scores.reduce(min); - return (p, worst as num); - }).toList(); - entries.sort((a, b) => a.$2.compareTo(b.$2)); - return entries; - - case StatisticType.winrate: - return _sortDesc( - players.map((p) { - final played = _matchesPlayed(p, matches); - final wins = _wins(p, matches); - final rate = played == 0 - ? 0.0 - : double.parse((wins / played).toStringAsFixed(2)); - return (p, rate as num); - }).toList(), - ); - } -} - -/* Helper functions for different statistic types */ - -/// Detemerines how many matches the player has played in the given list of matches. -int _matchesPlayed(Player p, List matches) => - matches.where((m) => m.players.any((mp) => mp.id == p.id)).length; - -/// Determines how many matches the player has won in the given list of matches. -int _wins(Player p, List matches) => - matches.where((m) => m.mvp.any((mp) => mp.id == p.id)).length; - -/// Determines the total score of the player in the given list of matches. -int _totalScore(Player p, List matches) { - var total = 0; - for (final m in matches) { - final s = m.scores[p.id]; - if (s != null) total += s.score; - } - return total; -} - -/// Returns a list of all scores the player has achieved in the given list of matches. -List _scoresOf(Player p, List matches) => [ - for (final m in matches) - if (m.scores[p.id] != null) m.scores[p.id]!.score, -]; - -/// Returns the list of entries sorted descending by the statistic value. -List<(Player, num)> _sortDesc(List<(Player, num)> entries) { - entries.sort((a, b) => b.$2.compareTo(a.$2)); - return entries; -} - -/* Icon and color */ - -/// Returns the icon for the given statistic type. -IconData _getStatisticIcon({required StatisticType type}) { - switch (type) { - case StatisticType.totalMatches: - return Icons.casino; - case StatisticType.totalWins: - return Icons.emoji_events; - case StatisticType.totalLosses: - return Icons.sentiment_dissatisfied; - case StatisticType.totalScore: - return Icons.scoreboard; - case StatisticType.averageScore: - return Icons.show_chart; - case StatisticType.bestScore: - return Icons.trending_up; - case StatisticType.worstScore: - return Icons.trending_down; - case StatisticType.winrate: - return Icons.percent; - } -} - -/// Returns a color from the palette based on the statistic's ID as random seed. -Color _getStatisticColor(Statistic stat) { - final seed = stat.id.hashCode; - return _colorPalette[seed.abs() % _colorPalette.length]; -} - -/* Skeleton data */ - -/// A placeholder tile with mock data for the loading state. -Widget buildSkeletonStatisticTile({required BuildContext context}) { - final count = 4 + Random().nextInt(5); // 4..8 - final values = <(Player, num)>[ - for (var i = 0; i < count; i++) - (Player(name: 'Player ${i + 1}'), count - i), - ]; - - return StatisticsTile( - icon: Icons.bar_chart, - title: 'Skeleton title', - width: MediaQuery.sizeOf(context).width * 0.95, - values: values, - barColor: _colorPalette[Random().nextInt(_colorPalette.length)], - selectedGames: [Game(name: 'Game 1', ruleset: Ruleset.highestScore)], - selectedGroups: [Group(name: 'Group 1', members: [])], - displayCount: 5, - ); -} diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart deleted file mode 100644 index 3381267..0000000 --- a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart +++ /dev/null @@ -1,190 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/core/adaptive_page_route.dart'; -import 'package:tallee/core/constants.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/match.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_detail_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart'; -import 'package:tallee/presentation/widgets/app_skeleton.dart'; -import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; -import 'package:tallee/presentation/widgets/top_centered_message.dart'; - -class StatisticsView extends StatefulWidget { - /// A view that displays player statistics - const StatisticsView({super.key}); - - @override - State createState() => _StatisticsViewState(); -} - -class _StatisticsViewState extends State { - bool isLoading = true; - List _allMatches = const []; - List _allPlayers = const []; - List _statistics = const []; - List statisticTiles = []; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addPostFrameCallback((_) { - if (!mounted) return; - loadStatistics(context); - }); - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context); - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Stack( - alignment: AlignmentDirectional.bottomCenter, - fit: StackFit.expand, - children: [ - Visibility( - visible: statisticTiles.isNotEmpty, - replacement: Center( - child: TopCenteredMessage( - icon: Icons.info, - title: loc.info, - message: loc.no_statistics_created_yet, - ), - ), - child: SingleChildScrollView( - child: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: Column( - spacing: 12, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ...statisticTiles, - SizedBox( - height: MediaQuery.paddingOf(context).bottom + 80, - ), - ], - ), - ), - ), - ), - Positioned( - bottom: MediaQuery.paddingOf(context).bottom + 20, - child: MainMenuButton( - text: loc.create_statistic, - icon: Icons.bar_chart, - onPressed: () async { - Statistic newStatistic = await Navigator.push( - context, - adaptivePageRoute( - builder: (context) => CreateStatisticView( - onStatisticCreated: () => loadStatistics(context), - ), - ), - ); - if (!context.mounted) return; - setState(() { - _statistics = [..._statistics, newStatistic]; - statisticTiles = _statistics - .map((stat) => _buildStatisticTile(context, stat)) - .toList(); - }); - }, - ), - ), - ], - ); - }, - ); - } - - Future loadStatistics(BuildContext context) async { - setState(() { - isLoading = true; - statisticTiles = List.generate( - 4, - (index) => Column( - children: [ - buildSkeletonStatisticTile(context: context), - const SizedBox(height: 12), - ], - ), - ); - }); - - final db = Provider.of(context, listen: false); - - final results = await Future.wait([ - db.statisticDao.getAllStatistics(), - db.matchDao.getAllMatches(), - db.playerDao.getAllPlayers(), - Future.delayed(Constants.MINIMUM_SKELETON_DURATION), - ]); - - if (!mounted) return; - - final statistics = results[0] as List; - _allMatches = results[1] as List; - _allPlayers = results[2] as List; - _statistics = statistics; - - setState(() { - statisticTiles = _statistics - .map((stat) => _buildStatisticTile(context, stat)) - .toList(); - isLoading = false; - }); - } - - Widget _buildStatisticTile(BuildContext context, Statistic statistic) { - final values = computeStatisticValues( - statistic: statistic, - matches: _allMatches, - players: _allPlayers, - ); - - return GestureDetector( - onTap: () async { - final newDisplayCount = await Navigator.push( - context, - adaptivePageRoute( - builder: (context) => StatisticDetailView( - statistic: statistic, - values: values, - icon: getStatisticIconForType(statistic.type), - barColor: getStatisticColorForStatistic(statistic), - ), - ), - ); - if (newDisplayCount != null && - newDisplayCount != statistic.displayCount) { - setState(() { - _statistics = _statistics - .map( - (stat) => stat.id == statistic.id - ? stat.copyWith(displayCount: newDisplayCount) - : stat, - ) - .toList(); - statisticTiles = _statistics - .map((stat) => _buildStatisticTile(context, stat)) - .toList(); - }); - } - }, - child: buildStatisticTile( - statistic: statistic, - matches: _allMatches, - players: _allPlayers, - context: context, - ), - ); - } -} diff --git a/lib/presentation/widgets/app_skeleton.dart b/lib/presentation/widgets/app_skeleton.dart index abdfb8d..8a21320 100644 --- a/lib/presentation/widgets/app_skeleton.dart +++ b/lib/presentation/widgets/app_skeleton.dart @@ -6,13 +6,11 @@ class AppSkeleton extends StatefulWidget { /// - [child]: The widget tree to apply the skeleton effect to. /// - [enabled]: A boolean to enable or disable the skeleton effect. /// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher. - /// - [alignment]: The alignment used for the custom layout builder and optional Align wrapper. Defaults to [Alignment.center]. const AppSkeleton({ super.key, required this.child, this.enabled = true, this.fixLayoutBuilder = false, - this.alignment = Alignment.center, }); /// The widget tree to apply the skeleton effect to. @@ -24,9 +22,6 @@ class AppSkeleton extends StatefulWidget { /// A boolean to fix the layout builder for AnimatedSwitcher. final bool fixLayoutBuilder; - /// The alignment used for the custom layout builder and optional Align wrapper - final Alignment alignment; - @override State createState() => _AppSkeletonState(); } @@ -50,14 +45,13 @@ class _AppSkeletonState extends State { layoutBuilder: !widget.fixLayoutBuilder ? AnimatedSwitcher.defaultLayoutBuilder : (Widget? currentChild, List previousChildren) { - final children = [...previousChildren]; - if (currentChild != null) children.add(currentChild); - return Stack(alignment: widget.alignment, children: children); + return Stack( + alignment: Alignment.topCenter, + children: [...previousChildren, ?currentChild], + ); }, ), - child: widget.fixLayoutBuilder - ? Align(alignment: widget.alignment, child: widget.child) - : widget.child, + child: widget.child, ); } } diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index 5062b23..8c8765e 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -11,7 +11,7 @@ class AnimatedDialogButton extends StatefulWidget { const AnimatedDialogButton({ super.key, required this.buttonText, - this.onPressed, + required this.onPressed, this.buttonConstraints, this.buttonType = ButtonType.primary, this.isDescructive = false, @@ -19,7 +19,7 @@ class AnimatedDialogButton extends StatefulWidget { final String buttonText; - final VoidCallback? onPressed; + final VoidCallback onPressed; final BoxConstraints? buttonConstraints; @@ -38,38 +38,28 @@ class _AnimatedDialogButtonState extends State { Widget build(BuildContext context) { final textStyling = _getTextStyling(); final buttonDecoration = _getButtonDecoration(); - bool isDisabled = widget.onPressed == null; - return IgnorePointer( - ignoring: isDisabled, - child: Opacity( - opacity: isDisabled ? 0.5 : 1.0, - child: GestureDetector( - onTapDown: (_) => setState(() => _isPressed = true), - onTapUp: (_) => setState(() => _isPressed = false), - onTapCancel: () => setState(() => _isPressed = false), - onTap: widget.onPressed, - child: AnimatedScale( - scale: _isPressed ? 0.95 : 1.0, - duration: const Duration(milliseconds: 100), - child: AnimatedOpacity( - opacity: _isPressed ? 0.6 : 1.0, - duration: const Duration(milliseconds: 100), - child: Center( - child: Container( - constraints: widget.buttonConstraints, - decoration: buttonDecoration, - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), - margin: const EdgeInsets.symmetric(vertical: 8), - child: Text( - widget.buttonText, - style: textStyling, - textAlign: TextAlign.center, - ), - ), + return GestureDetector( + onTapDown: (_) => setState(() => _isPressed = true), + onTapUp: (_) => setState(() => _isPressed = false), + onTapCancel: () => setState(() => _isPressed = false), + onTap: widget.onPressed, + child: AnimatedScale( + scale: _isPressed ? 0.95 : 1.0, + duration: const Duration(milliseconds: 100), + child: AnimatedOpacity( + opacity: _isPressed ? 0.6 : 1.0, + duration: const Duration(milliseconds: 100), + child: Center( + child: Container( + constraints: widget.buttonConstraints, + decoration: buttonDecoration, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + margin: const EdgeInsets.symmetric(vertical: 8), + child: Text( + widget.buttonText, + style: textStyling, + textAlign: TextAlign.center, ), ), ), diff --git a/lib/presentation/widgets/dialog/custom_alert_dialog.dart b/lib/presentation/widgets/dialog/custom_alert_dialog.dart index 7dfba98..606fc49 100644 --- a/lib/presentation/widgets/dialog/custom_alert_dialog.dart +++ b/lib/presentation/widgets/dialog/custom_alert_dialog.dart @@ -19,6 +19,7 @@ class CustomAlertDialog extends StatelessWidget { final String title; final Widget content; final List actions; + @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/presentation/widgets/dialog/custom_dialog_action.dart b/lib/presentation/widgets/dialog/custom_dialog_action.dart index 0c0b2e0..26dc40d 100644 --- a/lib/presentation/widgets/dialog/custom_dialog_action.dart +++ b/lib/presentation/widgets/dialog/custom_dialog_action.dart @@ -10,7 +10,7 @@ class CustomDialogAction extends StatelessWidget { /// - [onPressed]: Callback function that is triggered when the button is pressed. const CustomDialogAction({ super.key, - this.onPressed, + required this.onPressed, required this.text, this.buttonType = ButtonType.primary, this.isDestructive = false, @@ -20,18 +20,17 @@ class CustomDialogAction extends StatelessWidget { final ButtonType buttonType; - final VoidCallback? onPressed; + final VoidCallback onPressed; final bool isDestructive; + @override Widget build(BuildContext context) { return AnimatedDialogButton( - onPressed: onPressed != null - ? () async { - await HapticFeedback.selectionClick(); - onPressed?.call(); - } - : null, + onPressed: () async { + await HapticFeedback.selectionClick(); + onPressed.call(); + }, buttonText: text, buttonType: buttonType, isDescructive: isDestructive, diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index 2e6bf74..553e637 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -12,44 +12,41 @@ class GameLabel extends StatelessWidget { final String title; final String description; - final AppColor color; + final GameColor color; @override Widget build(BuildContext context) { - final backgroundColor = getColorFromAppColor(color); + final backgroundColor = getColorFromGameColor(color); final fontColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Title - Container( - decoration: BoxDecoration( - color: backgroundColor.withAlpha(230), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - bottomLeft: Radius.circular(8), + return IntrinsicHeight( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Title + Container( + decoration: BoxDecoration( + color: backgroundColor.withAlpha(230), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + bottomLeft: Radius.circular(8), + ), + ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: Text( + title, + style: TextStyle( + fontSize: 12, + color: fontColor, + fontWeight: FontWeight.bold, + ), ), ), - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - child: Text( - title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - softWrap: false, - style: TextStyle( - fontSize: 12, - color: fontColor, - fontWeight: FontWeight.bold, - ), - ), - ), - // Description - Flexible( - child: Container( + // Description + Container( decoration: BoxDecoration( color: backgroundColor.withAlpha(140), borderRadius: const BorderRadius.only( @@ -60,9 +57,6 @@ class GameLabel extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Text( description, - maxLines: 1, - overflow: TextOverflow.ellipsis, - softWrap: false, style: TextStyle( fontSize: 12, color: fontColor, @@ -70,8 +64,8 @@ class GameLabel extends StatelessWidget { ), ), ), - ), - ], + ], + ), ); } } diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 6a62c95..00d6c11 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -26,7 +26,6 @@ class PlayerSelection extends StatefulWidget { this.availablePlayers, this.initialSelectedPlayers, required this.onChanged, - this.onPlayerCreated, }); /// An optional list of players to choose from. If null, all players from the database are used. @@ -38,9 +37,6 @@ class PlayerSelection extends StatefulWidget { /// A callback function that is invoked whenever the selection changes, final Function(List value) onChanged; - /// A callback function that is invoked when a player was created in this widget - final VoidCallback? onPlayerCreated; - @override State createState() => _PlayerSelectionState(); } @@ -327,7 +323,6 @@ class _PlayerSelectionState extends State { /// Updates the state after successfully adding a new player. void _handleSuccessfulPlayerCreation(Player player) { - widget.onPlayerCreated?.call(); selectedPlayers.insert(0, player); widget.onChanged([...selectedPlayers]); allPlayers.add(player); diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/game_tile.dart index 4fb12d1..ee5acf0 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/game_tile.dart @@ -51,7 +51,7 @@ class GameTile extends StatelessWidget { ? (badgeColor!.computeLuminance() > 0.5 ? Colors.black : Colors.white) : Colors.white; - final gameColor = badgeColor ?? getColorFromAppColor(AppColor.orange); + final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange); return GestureDetector( onTap: () async { diff --git a/lib/presentation/widgets/tiles/group_tile.dart b/lib/presentation/widgets/tiles/group_tile.dart index c01dcbe..f6c406e 100644 --- a/lib/presentation/widgets/tiles/group_tile.dart +++ b/lib/presentation/widgets/tiles/group_tile.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/data/models/group.dart'; -import 'package:tallee/presentation/views/main_menu/player_detail_view.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; class GroupTile extends StatefulWidget { @@ -17,7 +15,6 @@ class GroupTile extends StatefulWidget { required this.group, this.isHighlighted = false, this.onTap, - this.onPlayerChanged, }); /// The group data to be displayed. @@ -29,9 +26,6 @@ class GroupTile extends StatefulWidget { /// Callback function to be executed when the tile is tapped. final VoidCallback? onTap; - /// Callback function to be executed when the players in the group are changed. - final VoidCallback? onPlayerChanged; - @override State createState() => _GroupTileState(); } @@ -98,19 +92,6 @@ class _GroupTileState extends State { text: member.name, suffixText: getNameCountText(member), iconEnabled: false, - onTileTap: () { - Navigator.push( - context, - adaptivePageRoute( - builder: (context) => PlayerDetailView( - player: member, - callback: () { - widget.onPlayerChanged?.call(); - }, - ), - ), - ); - }, ), ], ), diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index c3d0242..6a81dc3 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -3,13 +3,11 @@ import 'dart:core' hide Match; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; -import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/views/main_menu/player_detail_view.dart'; import 'package:tallee/presentation/widgets/game_label.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; @@ -26,7 +24,6 @@ class MatchTile extends StatefulWidget { required this.onTap, this.width, this.compact = false, - this.onPlayerEdited, }); /// The match data to be displayed. @@ -35,9 +32,6 @@ class MatchTile extends StatefulWidget { /// The callback invoked when the tile is tapped. final VoidCallback onTap; - /// The callback invoked when the players are edited - final VoidCallback? onPlayerEdited; - /// Optional width for the tile. final double? width; @@ -230,19 +224,6 @@ class _MatchTileState extends State { text: player.name, suffixText: getNameCountText(player), iconEnabled: false, - onTileTap: () { - Navigator.push( - context, - adaptivePageRoute( - builder: (context) => PlayerDetailView( - player: player, - callback: () { - widget.onPlayerEdited?.call(); - }, - ), - ), - ); - }, ); }).toList(), ), diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index 02cdb75..ea9cb49 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -1,12 +1,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; @@ -25,11 +21,8 @@ class StatisticsTile extends StatelessWidget { required this.title, required this.width, required this.values, + required this.itemCount, required this.barColor, - required this.displayCount, - this.selectedGroups, - this.selectedGames, - this.showAllValues = false, }); /// The icon displayed next to the title. @@ -44,16 +37,12 @@ class StatisticsTile extends StatelessWidget { /// A list of tuples containing labels and their corresponding numeric values. final List<(Player, num)> values; + /// The maximum number of items to display. + final int itemCount; + /// The color of the bars representing the values. final Color barColor; - // statistic data - final int displayCount; - final List? selectedGroups; - final List? selectedGames; - - final bool showAllValues; - @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -63,202 +52,91 @@ class StatisticsTile extends StatelessWidget { title: title, icon: icon, content: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Visibility( visible: values.isNotEmpty, - - // No data avaiable message replacement: Center( heightFactor: 4, child: Text(loc.no_data_available), ), - - // Bar chart child: LayoutBuilder( builder: (context, constraints) { - final maxBarWidth = constraints.maxWidth * 0.8; - - // If displayCount wasnt provided, take all values - final valuesShown = showAllValues - ? values.length - : min(values.length, displayCount); - final displayValues = values.take(valuesShown).toList(); - final maxVal = displayValues.isNotEmpty - ? displayValues.fold( - 0, - (currentMax, entry) => - entry.$2 > currentMax ? entry.$2 : currentMax, - ) - : 0; - + final maxBarWidth = constraints.maxWidth * 0.65; return Column( - children: [ - // Bars - ...List.generate(valuesShown, (index) { - /// Fraction of wins - final double fraction = (maxVal > 0) - ? (displayValues[index].$2 / maxVal) - : 0.0; + children: List.generate(min(values.length, itemCount), (index) { + /// The maximum wins among all players + final maxMatches = values.isNotEmpty ? values[0].$2 : 0; - /// Calculated width for current the bar - final double barWidth = (maxBarWidth * fraction).clamp( - 0.0, - maxBarWidth, - ); + /// Fraction of wins + final double fraction = (maxMatches > 0) + ? (values[index].$2 / maxMatches) + : 0.0; - final barClr = index >= displayCount - ? barColor.withAlpha(150) - : barColor; + /// Calculated width for current the bar + final double barWidth = maxBarWidth * fraction; - var textClr = barColor.computeLuminance() > 0.5 - ? const Color(0xFF101010) - : CustomTheme.textColor; - textClr = textClr.withAlpha( - index >= displayCount ? 220 : 255, - ); - - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SizedBox( - width: maxBarWidth, - child: Stack( - clipBehavior: Clip.hardEdge, - children: [ - // Bar - Container( - height: 24, - width: barWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: barClr, - ), - ), - - // Player - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: RichText( - maxLines: 1, - softWrap: false, - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: displayValues[index].$1.name, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: textClr, - ), - ), - TextSpan( - text: getNameCountText( - displayValues[index].$1, - ), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: - (barColor == - getColorFromAppColor( - AppColor.yellow, - ) - ? const Color( - 0xFF101010, - ) - : CustomTheme.textColor) - .withAlpha(150), - ), - ), - ], - ), - ), - ), - ], - ), - ), - const Spacer(), - - // Value - Center( - child: Text( - displayValues[index].$2 <= 1 && - displayValues[index].$2 is double - ? displayValues[index].$2.toStringAsFixed(2) - : displayValues[index].$2.toString(), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Stack( + children: [ + Container( + height: 24, + width: barWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: barColor, ), ), + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: RichText( + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: values[index].$1.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + TextSpan( + text: getNameCountText(values[index].$1), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor.withAlpha( + 150, + ), + ), + ), + ], + ), + ), + ), + ], + ), + const Spacer(), + Center( + child: Text( + values[index].$2 <= 1 && values[index].$2 is double + ? values[index].$2.toStringAsFixed(2) + : values[index].$2.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), - ], - ), - ); - }), - - // Group & Game info - if (hasGame || hasGroup) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Wrap( - alignment: WrapAlignment.start, - crossAxisAlignment: WrapCrossAlignment.center, - spacing: 4, - runSpacing: 4, - children: [ - // Game - if (hasGroup) - Row( - spacing: 8, - children: [ - const Icon( - RpgAwesome.clovers_card, - color: CustomTheme.hintColor, - size: 20, - ), - Text( - getGameText(selectedGames!), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.hintColor, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - if (hasGroup && hasGame) const SizedBox(width: 20), - - // Group - if (hasGroup) - Row( - spacing: 8, - children: [ - const Icon( - Icons.groups, - color: CustomTheme.hintColor, - ), - Text( - getGroupText(selectedGroups!), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.hintColor, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ], - ), + ), + ], ), - ], + ); + }), ); }, ), @@ -266,24 +144,4 @@ class StatisticsTile extends StatelessWidget { ), ); } - - String getGroupText(List groups) { - var text = groups[0].name; - if (groups.length > 1) { - return '$text + ${groups.length - 1}'; - } - return text; - } - - String getGameText(List games) { - var text = games[0].name; - if (games.length > 1) { - return '$text + ${games.length - 1}'; - } - return text; - } - - bool get hasGroup => selectedGroups != null && selectedGroups!.isNotEmpty; - - bool get hasGame => selectedGames != null && selectedGames!.isNotEmpty; } diff --git a/lib/presentation/widgets/tiles/text_icon_tile.dart b/lib/presentation/widgets/tiles/text_icon_tile.dart index a5ef399..541b6ae 100644 --- a/lib/presentation/widgets/tiles/text_icon_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_tile.dart @@ -12,7 +12,6 @@ class TextIconTile extends StatelessWidget { this.suffixText = '', this.iconEnabled = true, this.onIconTap, - this.onTileTap, }); /// The text to display in the tile. @@ -26,58 +25,52 @@ class TextIconTile extends StatelessWidget { /// The callback to be invoked when the icon is tapped. final VoidCallback? onIconTap; - /// The callback to be invoked when the tile is tapped. - final VoidCallback? onTileTap; - @override Widget build(BuildContext context) { - return GestureDetector( - onTap: onTileTap, - child: Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: CustomTheme.onBoxColor, - borderRadius: BorderRadius.circular(12), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - if (iconEnabled) const SizedBox(width: 3), - Flexible( - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: text, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), + return Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: CustomTheme.onBoxColor, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + if (iconEnabled) const SizedBox(width: 3), + Flexible( + child: RichText( + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: text, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, ), - TextSpan( - text: suffixText, - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: CustomTheme.textColor.withAlpha(120), - ), + ), + TextSpan( + text: suffixText, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: CustomTheme.textColor.withAlpha(120), ), - ], - ), + ), + ], ), ), - if (iconEnabled) ...[ - const SizedBox(width: 3), - GestureDetector( - onTap: onIconTap, - child: const Icon(Icons.close, size: 20), - ), - ], + ), + if (iconEnabled) ...[ + const SizedBox(width: 3), + GestureDetector( + onTap: onIconTap, + child: const Icon(Icons.close, size: 20), + ), ], - ), + ], ), ); } diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 9dbf955..29199f8 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -20,7 +20,6 @@ class DataTransferService { static Future deleteAllData(BuildContext context) async { final db = Provider.of(context, listen: false); - await db.statisticDao.deleteAllStatistics(); await db.matchDao.deleteAllMatches(); await db.teamDao.deleteAllTeams(); await db.groupDao.deleteAllGroups(); @@ -279,7 +278,7 @@ class DataTransferService { name: 'Unknown', ruleset: Ruleset.singleWinner, description: '', - color: AppColor.blue, + color: GameColor.blue, icon: '', ); } diff --git a/pubspec.lock b/pubspec.lock index fc30e57..2ec2197 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" - animated_custom_dropdown: - dependency: "direct main" - description: - name: animated_custom_dropdown - sha256: "5a72dc209041bb53f6c7164bc2e366552d5197cdb032b1c9b2c36e3013024486" - url: "https://pub.dev" - source: hosted - version: "3.1.1" arb_utils: dependency: "direct dev" description: @@ -93,10 +85,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" + sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.15.0" built_collection: dependency: transitive description: @@ -185,14 +177,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" collection: dependency: "direct main" description: @@ -341,34 +325,26 @@ packages: dependency: "direct main" description: name: drift - sha256: "970cd188fddb111b26ea6a9b07a62bf5c2432d74147b8122c67044ae3b97e99e" + sha256: "8033500116b24398fba0cca0369cc31678cd627c01e41753a61186911cea743e" url: "https://pub.dev" source: hosted - version: "2.31.0" + version: "2.33.0" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: "917184b2fb867b70a548a83bf0d36268423b38d39968c06cce4905683da49587" + sha256: b3dd5b75e30522a91da8abda9f5bb17230cb038097f6d15fa75d42bb563428aa url: "https://pub.dev" source: hosted - version: "2.31.0" + version: "2.33.0" drift_flutter: dependency: "direct main" description: name: drift_flutter - sha256: c07120854742a0cae2f7501a0da02493addde550db6641d284983c08762e60a7 + sha256: "887fdec622174dc7eaefd0048403e34ee07cc18626ac8a7544cc3b8a4a172166" url: "https://pub.dev" source: hosted - version: "0.2.8" - dropdown_flutter: - dependency: "direct main" - description: - name: dropdown_flutter - sha256: "5ae3d05d768d0bb6030ff735e6b4b93f7b29be3cf3bec7c86cd4f444c8f067ff" - url: "https://pub.dev" - source: hosted - version: "1.0.3" + version: "0.3.0" equatable: dependency: transitive description: @@ -413,10 +389,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "68c9a085d9bb4546e0a31d1e583a48d7c17a6987d538788ea064f0043b1fc02d" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.4.0" fixnum: dependency: transitive description: @@ -1138,30 +1114,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.2" + sqlcipher_flutter_libs: + dependency: transitive + description: + name: sqlcipher_flutter_libs + sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929" + url: "https://pub.dev" + source: hosted + version: "0.7.0+eol" sqlite3: dependency: transitive description: name: sqlite3 - sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" + sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5" url: "https://pub.dev" source: hosted - version: "2.9.4" + version: "3.3.1" sqlite3_flutter_libs: dependency: transitive description: name: sqlite3_flutter_libs - sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad + sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" url: "https://pub.dev" source: hosted - version: "0.5.42" + version: "0.6.0+eol" sqlparser: dependency: transitive description: name: sqlparser - sha256: "337e9997f7141ffdd054259128553c348635fa318f7ca492f07a4ab76f850d19" + sha256: ecdc06d4a7d79dcbc928d99afd2f7f5b0f98a637c46f89be83d911617f759978 url: "https://pub.dev" source: hosted - version: "0.43.1" + version: "0.44.4" stack_trace: dependency: transitive description: @@ -1443,5 +1427,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.10.3 <4.0.0" - flutter: ">=3.38.4" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.41.0" diff --git a/pubspec.yaml b/pubspec.yaml index e90b0ec..9f1dc59 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,10 +7,8 @@ environment: sdk: ^3.12.0 dependencies: - animated_custom_dropdown: ^3.1.1 clock: ^1.1.2 collection: ^1.19.1 - dropdown_flutter: ^1.0.3 cupertino_icons: ^1.0.9 drift: ^2.33.0 drift_flutter: ^0.3.0 diff --git a/test/db_tests/aggregates/group_test.dart b/test/db_tests/aggregates/group_test.dart index 3ea625f..1498523 100644 --- a/test/db_tests/aggregates/group_test.dart +++ b/test/db_tests/aggregates/group_test.dart @@ -194,31 +194,6 @@ void main() { expect(allGroups, isEmpty); }); - test('getGroupsByPlayer() works correctly', () async { - await database.groupDao.addGroupsAsList( - groups: [testGroup1, testGroup2], - ); - - final groups = await database.groupDao.getGroupsByPlayer( - playerId: testPlayer2.id, - ); - - expect(groups, hasLength(2)); - expect(groups.any((group) => group.id == testGroup1.id), isTrue); - expect(groups.any((group) => group.id == testGroup2.id), isTrue); - }); - - test( - 'getGroupsByPlayer() returns empty list for non-existent player', - () async { - final groups = await database.groupDao.getGroupsByPlayer( - playerId: 'non-existent-player-id', - ); - - expect(groups, isEmpty); - }, - ); - test('addGroupsAsList() with duplicate groups only adds once', () async { await database.groupDao.addGroupsAsList( groups: [testGroup1, testGroup1, testGroup1], diff --git a/test/db_tests/aggregates/match_test.dart b/test/db_tests/aggregates/match_test.dart index 1144a73..37c1cd0 100644 --- a/test/db_tests/aggregates/match_test.dart +++ b/test/db_tests/aggregates/match_test.dart @@ -56,7 +56,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: AppColor.blue, + color: GameColor.blue, icon: '', ); testMatch1 = Match( @@ -260,34 +260,6 @@ void main() { expect(match.group!.id, testGroup1.id); }); - test('getMatchesByPlayer() works correctly', () async { - await database.matchDao.addMatchesAsList( - matches: [testMatch1, testMatch2], - ); - - final matches = await database.matchDao.getMatchesByPlayer( - playerId: testPlayer1.id, - ); - - expect(matches, hasLength(1)); - expect(matches.first.id, testMatch2.id); - expect( - matches.first.players.any((p) => p.id == testPlayer1.id), - isTrue, - ); - }); - - test( - 'getMatchesByPlayer() returns empty list for non-existent player', - () async { - final matches = await database.matchDao.getMatchesByPlayer( - playerId: 'non-existing-player-id', - ); - - expect(matches, isEmpty); - }, - ); - test('getMatchCount() works correctly', () async { var count = await database.matchDao.getMatchCount(); expect(count, 0); diff --git a/test/db_tests/aggregates/team_test.dart b/test/db_tests/aggregates/team_test.dart index 381d22b..fefdcc5 100644 --- a/test/db_tests/aggregates/team_test.dart +++ b/test/db_tests/aggregates/team_test.dart @@ -49,7 +49,7 @@ void main() { testGame = Game( name: 'Test Game', ruleset: Ruleset.highestScore, - color: AppColor.blue, + color: GameColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/entities/game_test.dart b/test/db_tests/entities/game_test.dart index f7e7dcd..778d43b 100644 --- a/test/db_tests/entities/game_test.dart +++ b/test/db_tests/entities/game_test.dart @@ -28,7 +28,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'A classic strategy game', - color: AppColor.blue, + color: GameColor.blue, icon: 'chess_icon', ); testGame2 = Game( @@ -36,7 +36,7 @@ void main() { name: 'Poker', ruleset: Ruleset.multipleWinners, description: 'Card game with multiple winners', - color: AppColor.red, + color: GameColor.red, icon: 'poker_icon', ); testGame3 = Game( @@ -44,7 +44,7 @@ void main() { name: 'Monopoly', ruleset: Ruleset.highestScore, description: 'A board game about real estate', - color: AppColor.orange, + color: GameColor.orange, icon: '', ); }); @@ -124,7 +124,7 @@ void main() { name: 'Game\'s & "Special" ', ruleset: Ruleset.multipleWinners, description: 'Description with émojis 🎮🎲', - color: AppColor.purple, + color: GameColor.purple, icon: '', ); await database.gameDao.addGame(game: specialGame); @@ -280,19 +280,19 @@ void main() { await database.gameDao.updateGameColor( gameId: testGame1.id, - color: AppColor.green, + color: GameColor.green, ); final updatedGame = await database.gameDao.getGameById( gameId: testGame1.id, ); - expect(updatedGame.color, AppColor.green); + expect(updatedGame.color, GameColor.green); }); test('updateGameColor() does nothing for non-existent game', () async { final updated = await database.gameDao.updateGameColor( gameId: 'non-existent-id', - color: AppColor.green, + color: GameColor.green, ); expect(updated, isFalse); @@ -336,7 +336,7 @@ void main() { name: newName, ); - const newGameColor = AppColor.teal; + const newGameColor = GameColor.teal; await database.gameDao.updateGameColor( gameId: testGame1.id, color: newGameColor, diff --git a/test/db_tests/entities/player_test.dart b/test/db_tests/entities/player_test.dart index 4105df1..bfcced4 100644 --- a/test/db_tests/entities/player_test.dart +++ b/test/db_tests/entities/player_test.dart @@ -233,95 +233,6 @@ void main() { expect(allPlayers, isEmpty); }); - test('updatePlayerName() sets correct nameCount with 2 player', () async { - await database.playerDao.addPlayer(player: testPlayer1); - await database.playerDao.addPlayer(player: testPlayer2); - - final newName = testPlayer1.name; - await database.playerDao.updatePlayerName( - playerId: testPlayer2.id, - name: newName, - ); - - var player = await database.playerDao.getPlayerById( - playerId: testPlayer1.id, - ); - expect(player.nameCount, 1); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer2.id, - ); - expect(player.nameCount, 2); - - await database.playerDao.updatePlayerName( - playerId: testPlayer1.id, - name: 'different name', - ); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer1.id, - ); - expect(player.nameCount, 0); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer2.id, - ); - expect(player.nameCount, 0); - }); - - test('updatePlayerName() sets correct nameCount with 3 player', () async { - await database.playerDao.addPlayersAsList( - players: [testPlayer1, testPlayer2, testPlayer3], - ); - - // Changing both names to player 1's name - final newName = testPlayer1.name; - await database.playerDao.updatePlayerName( - playerId: testPlayer2.id, - name: newName, - ); - await database.playerDao.updatePlayerName( - playerId: testPlayer3.id, - name: newName, - ); - - var player = await database.playerDao.getPlayerById( - playerId: testPlayer1.id, - ); - expect(player.nameCount, 1); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer2.id, - ); - expect(player.nameCount, 2); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer3.id, - ); - expect(player.nameCount, 3); - - // Changing the middle players name - await database.playerDao.updatePlayerName( - playerId: testPlayer2.id, - name: 'different name', - ); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer1.id, - ); - expect(player.nameCount, 1); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer2.id, - ); - expect(player.nameCount, 0); - - player = await database.playerDao.getPlayerById( - playerId: testPlayer3.id, - ); - expect(player.nameCount, 2); - }); - test('updatePlayerDescription() works correctly', () async { await database.playerDao.addPlayer(player: testPlayer1); @@ -461,22 +372,14 @@ void main() { final player1 = Player(name: testPlayer1.name, description: ''); await database.playerDao.addPlayer(player: player1); - final player2 = Player(name: testPlayer1.name, description: ''); - await database.playerDao.addPlayer(player: player2); - var players = await database.playerDao.getAllPlayers(); - expect(players.length, 3); + expect(players.length, 2); players.sort((a, b) => a.nameCount.compareTo(b.nameCount)); for (int i = 0; i < players.length - 1; i++) { expect(players[i].nameCount, i + 1); } - - // ids are correct in the right order - expect(players[0].id, testPlayer1.id); - expect(players[1].id, player1.id); - expect(players[2].id, player2.id); }, ); @@ -501,62 +404,24 @@ void main() { for (int i = 0; i < players.length - 1; i++) { expect(players[i].nameCount, i + 1); } - - // ids are correct in the right order - expect(players[0].id, testPlayer1.id); - expect(players[1].id, player1.id); - expect(players[2].id, player2.id); - expect(players[3].id, player3.id); }, ); - test('getNameCount works correctly', () async { - final player1 = Player(name: testPlayer1.name); + test('getNameCount works correctly', () async { final player2 = Player(name: testPlayer1.name); + final player3 = Player(name: testPlayer1.name); - await database.playerDao.addPlayer(player: testPlayer1); - - var nameCount = await database.playerDao.getNameCount( - name: testPlayer1.name, + await database.playerDao.addPlayersAsList( + players: [testPlayer1, player2, player3], ); - expect(nameCount, 1); - - await database.playerDao.addPlayersAsList(players: [player1, player2]); - - nameCount = await database.playerDao.getNameCount( + final nameCount = await database.playerDao.getNameCount( name: testPlayer1.name, ); expect(nameCount, 3); }); - test('calculateNameCount works correctly', () async { - final player1 = Player(name: testPlayer1.name); - final player2 = Player(name: testPlayer1.name); - - // Case 1: No existing players with the name - var nameCount = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(nameCount, 0); - - // Case 2: One existing player with the name. Should return 2 for - // the new player - await database.playerDao.addPlayer(player: testPlayer1); - nameCount = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(nameCount, 2); - - // Case 3: Multiple existing players with the name. Should return count + 1 - await database.playerDao.addPlayersAsList(players: [player1, player2]); - nameCount = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(nameCount, 4); - }); - test('updateNameCount works correctly', () async { await database.playerDao.addPlayer(player: testPlayer1); @@ -576,24 +441,14 @@ void main() { final player2 = Player(name: testPlayer1.name, description: ''); final player3 = Player(name: testPlayer1.name, description: ''); - await database.playerDao.addPlayer(player: testPlayer1); - var player = await database.playerDao.getPlayerWithHighestNameCount( - name: testPlayer1.name, + await database.playerDao.addPlayersAsList( + players: [testPlayer1, player2, player3], ); - expect(player, isNotNull); - expect(player!.nameCount, 0); - await database.playerDao.addPlayer(player: player2); - player = await database.playerDao.getPlayerWithHighestNameCount( + final player = await database.playerDao.getPlayerWithHighestNameCount( name: testPlayer1.name, ); - expect(player, isNotNull); - expect(player!.nameCount, 2); - await database.playerDao.addPlayer(player: player3); - player = await database.playerDao.getPlayerWithHighestNameCount( - name: testPlayer1.name, - ); expect(player, isNotNull); expect(player!.nameCount, 3); }); @@ -605,6 +460,32 @@ void main() { expect(player, isNull); }); + test('calculateNameCount works correctly', () async { + // Case 1: No existing players with the name + var count = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(count, 0); + + // Case 2: One existing player with the name. Should update that + // player's nameCount to 1 and return 2 for the new player + await database.playerDao.addPlayer(player: testPlayer1); + + count = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(count, 2); + + // Case 3: Multiple existing players with the name. + final player2 = Player(name: testPlayer1.name, nameCount: count); + await database.playerDao.addPlayer(player: player2); + + count = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(count, 3); + }); + test('getPlayerWithHighestNameCount with non existing player', () async { await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.initializeNameCount(name: testPlayer1.name); diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart index fa7ec21..6d879c3 100644 --- a/test/db_tests/relationships/player_match_test.dart +++ b/test/db_tests/relationships/player_match_test.dart @@ -42,7 +42,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: AppColor.blue, + color: GameColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart deleted file mode 100644 index bd590c7..0000000 --- a/test/db_tests/statistics/statistic_test.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'dart:core'; - -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart' hide isNotNull; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/core/enums.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/game.dart'; -import 'package:tallee/data/models/group.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/data/models/statistic.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - late Player testPlayer5; - late Group testGroup1; - late Group testGroup2; - late Game testGame; - /*late Match testMatch1; - late Match testMatch2; - late Match testMatchOnlyPlayers; - late Match testMatchOnlyGroup;*/ - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() async { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testPlayer5 = Player(name: 'Eve'); - testGroup1 = Group( - name: 'Test Group 1', - description: '', - members: [testPlayer1, testPlayer2, testPlayer3], - ); - testGroup2 = Group( - name: 'Test Group 2', - description: '', - members: [testPlayer4, testPlayer5], - ); - testGame = Game( - name: 'Test Game', - ruleset: Ruleset.singleWinner, - description: 'A test game', - color: AppColor.blue, - icon: '', - ); - /*testMatch1 = Match( - name: 'First Test Match', - game: testGame, - group: testGroup1, - players: [testPlayer4, testPlayer5], - scores: {testPlayer4.id: ScoreEntry(score: 1)}, - ); - testMatch2 = Match( - name: 'Second Test Match', - game: testGame, - group: testGroup2, - players: [testPlayer1, testPlayer2, testPlayer3], - ); - testMatchOnlyPlayers = Match( - name: 'Test Match with Players', - game: testGame, - players: [testPlayer1, testPlayer2, testPlayer3], - ); - testMatchOnlyGroup = Match( - name: 'Test Match with Group', - game: testGame, - group: testGroup2, - players: testGroup2.members, - );*/ - }); - await database.playerDao.addPlayersAsList( - players: [ - testPlayer1, - testPlayer2, - testPlayer3, - testPlayer4, - testPlayer5, - ], - ); - await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); - await database.gameDao.addGame(game: testGame); - }); - tearDown(() async { - await database.close(); - }); - - test('Adding/fetching statistic works correclty', () async { - final statistic = Statistic( - type: StatisticType.averageScore, - scopes: [StatisticScope.allPlayers, StatisticScope.selectedGames], - timeframe: Timeframe.allTime, - selectedGames: [testGame], - selectedGroups: [testGroup1], - ); - - final added = await database.statisticDao.addStatistic( - statistic: statistic, - ); - expect(added, isTrue); - - final fetched = await database.statisticDao.getStatisticById(statistic.id); - expect(fetched, isNotNull); - expect(fetched!.type, statistic.type); - }); -} diff --git a/test/db_tests/values/score_entry_test.dart b/test/db_tests/values/score_entry_test.dart index 593d194..f6cc292 100644 --- a/test/db_tests/values/score_entry_test.dart +++ b/test/db_tests/values/score_entry_test.dart @@ -40,7 +40,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: AppColor.blue, + color: GameColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/services/data_transfer_service_test.dart b/test/services/data_transfer_service_test.dart index 94ed977..586138a 100644 --- a/test/services/data_transfer_service_test.dart +++ b/test/services/data_transfer_service_test.dart @@ -45,7 +45,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'Strategic board game', - color: AppColor.blue, + color: GameColor.blue, icon: 'chess_icon', ); @@ -448,19 +448,19 @@ void main() { Game( name: 'Red Game', ruleset: Ruleset.singleWinner, - color: AppColor.red, + color: GameColor.red, icon: 'icon', ), Game( name: 'Blue Game', ruleset: Ruleset.singleWinner, - color: AppColor.blue, + color: GameColor.blue, icon: 'icon', ), Game( name: 'Green Game', ruleset: Ruleset.singleWinner, - color: AppColor.green, + color: GameColor.green, icon: 'icon', ), ]; @@ -484,19 +484,19 @@ void main() { Game( name: 'Highest Score Game', ruleset: Ruleset.highestScore, - color: AppColor.blue, + color: GameColor.blue, icon: 'icon', ), Game( name: 'Lowest Score Game', ruleset: Ruleset.lowestScore, - color: AppColor.blue, + color: GameColor.blue, icon: 'icon', ), Game( name: 'Single Winner', ruleset: Ruleset.singleWinner, - color: AppColor.blue, + color: GameColor.blue, icon: 'icon', ), ]; -- 2.49.1 From bf6c352d54683135cfd651e55acf8aaa400efa50 Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:55:35 +0000 Subject: [PATCH 37/39] Updated version number [skip ci] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 9f1dc59..d8b07ed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.34+282 +version: 0.0.35+283 environment: sdk: ^3.12.0 -- 2.49.1 From 6679a0f942a98a79667caa17131b6a148baf11ad Mon Sep 17 00:00:00 2001 From: "Gitea Actions [bot]" Date: Mon, 25 May 2026 12:56:12 +0000 Subject: [PATCH 38/39] Updated licenses [skip ci] --- .../settings_view/licenses/oss_licenses.dart | 86 +------------------ 1 file changed, 3 insertions(+), 83 deletions(-) diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 7614949..9130ba5 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -14,7 +14,6 @@ const thisPackage = _tallee; const allDependencies = [ __fe_analyzer_shared, _analyzer, - _animated_custom_dropdown, _arb_utils, _archive, _args, @@ -56,7 +55,6 @@ const allDependencies = [ _drift, _drift_dev, _drift_flutter, - _dropdown_flutter, _equatable, _fake_async, _ffi, @@ -198,10 +196,8 @@ const allDependencies = [ /// Direct `dependencies`. const dependencies = [ - _animated_custom_dropdown, _clock, _collection, - _dropdown_flutter, _cupertino_icons, _drift, _drift_flutter, @@ -366,47 +362,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// animated_custom_dropdown 3.1.1 -const _animated_custom_dropdown = Package( - name: 'animated_custom_dropdown', - description: 'Custom dropdown widget allows to add highly customizable dropdown widget in your projects. Features includes Search on list data, Network search, Multi-selection and many more.', - homepage: 'https://github.com/AbdullahChauhan/custom-dropdown', - authors: [], - version: '3.1.1', - spdxIdentifiers: ['BSD-3-Clause'], - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('flutter')], - devDependencies: [PackageRef('flutter_test'), PackageRef('flutter_lints')], - license: '''Copyright (c) 2024 Abdullah Chauhan - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - ); - /// arb_utils 0.11.0 const _arb_utils = Package( name: 'arb_utils', @@ -2684,41 +2639,6 @@ furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.''', - ); - -/// dropdown_flutter 1.0.3 -const _dropdown_flutter = Package( - name: 'dropdown_flutter', - description: 'A Flutter package designed to enhance your app with customizable dropdowns, featuring list data search, network search, and multi-selection.', - homepage: 'https://github.com/farhansadikgalib/dropdown_flutter', - authors: [], - version: '1.0.3', - spdxIdentifiers: ['MIT'], - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('flutter')], - devDependencies: [PackageRef('flutter_test'), PackageRef('flutter_lints')], - license: '''MIT License - -Copyright (c) 2024 Farhan Sadik Galib - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39700,16 +39620,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.34+282 +/// tallee 0.0.35+283 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.34+282', + version: '0.0.35+283', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('animated_custom_dropdown'), PackageRef('clock'), PackageRef('collection'), PackageRef('dropdown_flutter'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_numeric_text'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')], + dependencies: [PackageRef('clock'), PackageRef('collection'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('flutter_numeric_text'), PackageRef('flutter_popup'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')], devDependencies: [PackageRef('arb_utils'), PackageRef('flutter_test'), PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')], license: '''GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 -- 2.49.1 From eaf78227325fbdd6f425b409b63acf5306c5b28d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 25 May 2026 19:22:59 +0200 Subject: [PATCH 39/39] Revert to 4bd2f97 --- lib/core/common.dart | 44 +- lib/core/enums.dart | 30 +- lib/data/dao/game_dao.dart | 36 +- lib/data/dao/group_dao.dart | 70 +- lib/data/dao/match_dao.dart | 96 +- lib/data/dao/player_dao.dart | 155 +- lib/data/dao/player_group_dao.dart | 39 +- lib/data/dao/player_match_dao.dart | 23 +- lib/data/dao/score_entry_dao.dart | 42 +- lib/data/dao/statistic_dao.dart | 127 + lib/data/dao/statistic_dao.g.dart | 19 + lib/data/dao/statistic_game_dao.dart | 61 + lib/data/dao/statistic_game_dao.g.dart | 29 + lib/data/dao/statistic_group_dao.dart | 67 + lib/data/dao/statistic_group_dao.g.dart | 29 + lib/data/dao/statistic_scope_dao.dart | 55 + lib/data/dao/statistic_scope_dao.g.dart | 26 + lib/data/dao/team_dao.dart | 22 +- lib/data/db/database.dart | 16 + lib/data/db/database.g.dart | 2912 ++++++++++++++++- lib/data/db/tables/statistic_game_table.dart | 13 + lib/data/db/tables/statistic_group_table.dart | 13 + lib/data/db/tables/statistic_scope_table.dart | 11 + lib/data/db/tables/statistic_table.dart | 11 + lib/data/models/game.dart | 8 +- lib/data/models/group.dart | 8 +- lib/data/models/match.dart | 2 +- lib/data/models/statistic.dart | 48 + lib/l10n/arb/app_de.arb | 70 + lib/l10n/arb/app_en.arb | 53 +- lib/l10n/generated/app_localizations.dart | 268 +- lib/l10n/generated/app_localizations_de.dart | 137 +- lib/l10n/generated/app_localizations_en.dart | 139 +- .../main_menu/custom_navigation_bar.dart | 2 +- .../group_view/create_group_view.dart | 3 +- .../main_menu/group_view/group_view.dart | 6 +- .../create_match/choose_game_view.dart | 2 +- .../create_match/create_game_view.dart | 17 +- .../create_match/create_match_view.dart | 1 + .../main_menu/match_view/match_view.dart | 5 +- .../views/main_menu/player_detail_view.dart | 394 +++ .../settings_view/licenses/oss_licenses.dart | 4 +- .../views/main_menu/statistics_view.dart | 311 -- .../create_statistic_view.dart | 635 ++++ .../statistic_detail_view.dart | 191 ++ .../statistic_tile_factory.dart | 322 ++ .../statistics_view/statistics_view.dart | 190 ++ lib/presentation/widgets/app_skeleton.dart | 16 +- .../buttons/animated_dialog_button.dart | 56 +- .../widgets/dialog/custom_alert_dialog.dart | 1 - .../widgets/dialog/custom_dialog_action.dart | 15 +- lib/presentation/widgets/game_label.dart | 60 +- .../widgets/player_selection.dart | 5 + lib/presentation/widgets/tiles/game_tile.dart | 2 +- .../widgets/tiles/group_tile.dart | 19 + .../widgets/tiles/match_tile.dart | 19 + .../widgets/tiles/statistics_tile.dart | 288 +- .../widgets/tiles/text_icon_tile.dart | 85 +- lib/services/data_transfer_service.dart | 3 +- pubspec.lock | 68 +- pubspec.yaml | 4 +- test/db_tests/aggregates/group_test.dart | 25 + test/db_tests/aggregates/match_test.dart | 30 +- test/db_tests/aggregates/team_test.dart | 2 +- test/db_tests/entities/game_test.dart | 16 +- test/db_tests/entities/player_test.dart | 193 +- .../relationships/player_match_test.dart | 2 +- test/db_tests/statistics/statistic_test.dart | 122 + test/db_tests/values/score_entry_test.dart | 2 +- test/services/data_transfer_service_test.dart | 14 +- 70 files changed, 6970 insertions(+), 839 deletions(-) create mode 100644 lib/data/dao/statistic_dao.dart create mode 100644 lib/data/dao/statistic_dao.g.dart create mode 100644 lib/data/dao/statistic_game_dao.dart create mode 100644 lib/data/dao/statistic_game_dao.g.dart create mode 100644 lib/data/dao/statistic_group_dao.dart create mode 100644 lib/data/dao/statistic_group_dao.g.dart create mode 100644 lib/data/dao/statistic_scope_dao.dart create mode 100644 lib/data/dao/statistic_scope_dao.g.dart create mode 100644 lib/data/db/tables/statistic_game_table.dart create mode 100644 lib/data/db/tables/statistic_group_table.dart create mode 100644 lib/data/db/tables/statistic_scope_table.dart create mode 100644 lib/data/db/tables/statistic_table.dart create mode 100644 lib/data/models/statistic.dart create mode 100644 lib/presentation/views/main_menu/player_detail_view.dart delete mode 100644 lib/presentation/views/main_menu/statistics_view.dart create mode 100644 lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart create mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart create mode 100644 lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart create mode 100644 lib/presentation/views/main_menu/statistics_view/statistics_view.dart create mode 100644 test/db_tests/statistics/statistic_test.dart diff --git a/lib/core/common.dart b/lib/core/common.dart index 312e3fa..df88ea3 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -24,48 +24,48 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } } -/// Translates a [GameColor] enum value to its corresponding localized string. -String translateGameColorToString(GameColor color, BuildContext context) { +/// Translates a [AppColor] enum value to its corresponding localized string. +String translateAppColorToString(AppColor color, BuildContext context) { final loc = AppLocalizations.of(context); switch (color) { - case GameColor.red: + case AppColor.red: return loc.color_red; - case GameColor.blue: + case AppColor.blue: return loc.color_blue; - case GameColor.green: + case AppColor.green: return loc.color_green; - case GameColor.yellow: + case AppColor.yellow: return loc.color_yellow; - case GameColor.purple: + case AppColor.purple: return loc.color_purple; - case GameColor.orange: + case AppColor.orange: return loc.color_orange; - case GameColor.pink: + case AppColor.pink: return loc.color_pink; - case GameColor.teal: + case AppColor.teal: return loc.color_teal; } } -/// Returns the [Color] object corresponding to a [GameColor] enum value. -Color getColorFromGameColor(GameColor color) { +/// Returns the [Color] object corresponding to a [AppColor] enum value. +Color getColorFromAppColor(AppColor color) { switch (color) { - case GameColor.red: + case AppColor.red: return Colors.red; - case GameColor.blue: + case AppColor.blue: return Colors.blue; - case GameColor.green: + case AppColor.green: return Colors.green; - case GameColor.yellow: + case AppColor.yellow: return const Color(0xFFF7CA28); - case GameColor.purple: + case AppColor.purple: return Colors.purple; - case GameColor.orange: + case AppColor.orange: return const Color(0xFFef681f); - case GameColor.pink: - return Colors.pink; - case GameColor.teal: - return Colors.teal; + case AppColor.pink: + return const Color(0xFFE91E63); + case AppColor.teal: + return const Color(0xFF00BCD4); } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 99141e4..073fd7a 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -43,4 +43,32 @@ enum Ruleset { } /// Different colors for highlighting games -enum GameColor { red, orange, yellow, green, teal, blue, purple, pink } +enum AppColor { red, orange, yellow, green, teal, blue, purple, pink } + +enum StatisticType { + totalMatches, + totalWins, + totalScore, + totalLosses, + averageScore, + bestScore, + worstScore, + winrate, +} + +enum StatisticScope { + allPlayers, + //selectedPlayer, + selectedGroups, + selectedGames, + timeframe, +} + +enum Timeframe { + last7Days, + last30Days, + last90Days, + last180Days, + lastYear, + allTime, +} diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index a4c2300..3ee8ebd 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -77,8 +77,8 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Returns `true` if the game exists, `false` otherwise. Future gameExists({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all games from the database. @@ -92,7 +92,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { name: row.name, ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), description: row.description, - color: GameColor.values.firstWhere((e) => e.name == row.color), + color: AppColor.values.firstWhere((e) => e.name == row.color), icon: row.icon, createdAt: row.createdAt, ), @@ -103,15 +103,15 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Retrieves a [Game] by its [gameId]. Future getGameById({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); - final result = await query.getSingle(); + final row = await query.getSingle(); return Game( - id: result.id, - name: result.name, - ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), - description: result.description, - color: GameColor.values.firstWhere((e) => e.name == result.color), - icon: result.icon, - createdAt: result.createdAt, + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, ); } @@ -123,7 +123,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String name, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -135,7 +135,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required Ruleset ruleset, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(ruleset: Value(ruleset.name)), ); return rowsAffected > 0; @@ -147,7 +147,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String description, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(description: Value(description)), ); return rowsAffected > 0; @@ -156,10 +156,10 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Updates the color of the game with the given [gameId]. Future updateGameColor({ required String gameId, - required GameColor color, + required AppColor color, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(color: Value(color.name)), ); return rowsAffected > 0; @@ -171,7 +171,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String icon, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(icon: Value(icon)), ); return rowsAffected > 0; @@ -182,7 +182,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Deletes the game with the given [gameId] from the database. /// Returns `true` if the game was deleted, `false` if the game did not exist. Future deleteGame({required String gameId}) async { - final query = delete(gameTable)..where((g) => g.id.equals(gameId)); + final query = delete(gameTable)..where((tbl) => tbl.id.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index bffe5a4..8d1c0a2 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -143,16 +143,16 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final query = select(groupTable); final result = await query.get(); return Future.wait( - result.map((groupData) async { + result.map((row) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: groupData.id, + groupId: row.id, ); return Group( - id: groupData.id, - name: groupData.name, - description: groupData.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: groupData.createdAt, + createdAt: row.createdAt, ); }), ); @@ -161,18 +161,18 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Retrieves a [Group] by its [groupId], including its members. Future getGroupById({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingle(); + final row = await query.getSingle(); List members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupId, ); return Group( - id: result.id, - name: result.name, - description: result.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: result.createdAt, + createdAt: row.createdAt, ); } @@ -180,17 +180,49 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future getGroupCount() async { final count = await (selectOnly(groupTable)..addColumns([groupTable.id.count()])) - .map((row) => row.read(groupTable.id.count())) + .map((tbl) => tbl.read(groupTable.id.count())) .getSingle(); return count ?? 0; } + /// Retrieves all groups a specific player belongs to. + /// Returns an empty list if the player is not part of any group. + Future> getGroupsByPlayer({required String playerId}) async { + final playerGroups = await (select( + playerGroupTable, + )..where((tbl) => tbl.playerId.equals(playerId))).get(); + + if (playerGroups.isEmpty) return []; + + final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList(); + final result = + await (select(groupTable) + ..where((tbl) => tbl.id.isIn(groupIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) + .get(); + + return Future.wait( + result.map((row) async { + final members = await db.playerGroupDao.getPlayersOfGroup( + groupId: row.id, + ); + return Group( + id: row.id, + name: row.name, + description: row.description, + members: members, + createdAt: row.createdAt, + ); + }), + ); + } + /// Checks if a group with the given [groupId] exists in the database. /// Returns `true` if the group exists, `false` otherwise. Future groupExists({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /* Delete */ @@ -220,9 +252,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String name, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(name: Value(name)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(name: Value(name))); return rowsAffected > 0; } @@ -233,9 +264,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String description, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(description: Value(description)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(description: Value(description))); return rowsAffected > 0; } } diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index 88cca35..74611b6 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -258,15 +258,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Returns `true` if the match exists, otherwise `false`. Future matchExists({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves the number of matches in the database. Future getMatchCount() async { final count = await (selectOnly(matchTable)..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -279,10 +279,12 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return Future.wait( result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); + Group? group; if (row.groupId != null) { group = await db.groupDao.getGroupById(groupId: row.groupId!); } + final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); @@ -312,13 +314,13 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Retrieves a [Match] by its [matchId]. Future getMatchById({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingle(); + final row = await query.getSingle(); - final game = await db.gameDao.getGameById(gameId: result.gameId); + final game = await db.gameDao.getGameById(gameId: row.gameId); Group? group; - if (result.groupId != null) { - group = await db.groupDao.getGroupById(groupId: result.groupId!); + if (row.groupId != null) { + group = await db.groupDao.getGroupById(groupId: row.groupId!); } final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); @@ -328,15 +330,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { final teams = await _getMatchTeams(matchId: matchId); return Match( - id: result.id, - name: result.name, + id: row.id, + name: row.name, game: game, group: group, players: players, teams: teams.isEmpty ? null : teams, - notes: result.notes, - createdAt: result.createdAt, - endedAt: result.endedAt, + notes: row.notes, + createdAt: row.createdAt, + endedAt: row.endedAt, scores: scores, ); } @@ -347,25 +349,73 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { await (selectOnly(matchTable) ..where(matchTable.gameId.equals(gameId)) ..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } + Future> getMatchesByPlayer({required String playerId}) async { + final playerMatches = await (select( + playerMatchTable, + )..where((tbl) => tbl.playerId.equals(playerId))).get(); + + if (playerMatches.isEmpty) return []; + + final matchIds = playerMatches.map((tbl) => tbl.matchId).toSet().toList(); + final result = + await (select(matchTable) + ..where((tbl) => tbl.id.isIn(matchIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) + .get(); + + return Future.wait( + result.map((row) async { + final game = await db.gameDao.getGameById(gameId: row.gameId); + + Group? group; + if (row.groupId != null) { + group = await db.groupDao.getGroupById(groupId: row.groupId!); + } + + final players = await db.playerMatchDao.getPlayersOfMatch( + matchId: row.id, + ); + final scores = await db.scoreEntryDao.getAllMatchScores( + matchId: row.id, + ); + final teams = await _getMatchTeams(matchId: row.id); + + return Match( + id: row.id, + name: row.name, + game: game, + group: group, + players: players, + teams: teams.isEmpty ? null : teams, + notes: row.notes, + createdAt: row.createdAt, + endedAt: row.endedAt, + scores: scores, + ); + }), + ); + } + /// Retrieves all matches associated with the given [groupId]. /// Queries the database directly, filtering by [groupId]. Future> getMatchesByGroup({required String groupId}) async { final query = select(matchTable)..where((m) => m.groupId.equals(groupId)); - final rows = await query.get(); + final result = await query.get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); final group = await db.groupDao.getGroupById(groupId: groupId); final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); final teams = await _getMatchTeams(matchId: row.id); + return Match( id: row.id, name: row.name, @@ -385,7 +435,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> _getMatchTeams({required String matchId}) async { // Get all unique team IDs from PlayerMatchTable for this match final playerMatchQuery = select(db.playerMatchTable) - ..where((pm) => pm.matchId.equals(matchId) & pm.teamId.isNotNull()); + ..where((tbl) => tbl.matchId.equals(matchId) & tbl.teamId.isNotNull()); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; @@ -412,7 +462,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String name, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(name: Value(name)), ); @@ -427,7 +477,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String? groupId, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(groupId: Value(groupId)), ); @@ -440,7 +490,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String notes, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(notes: Value(notes)), ); @@ -451,7 +501,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Sets the groupId to null. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future removeMatchGroup({required String matchId}) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( const MatchTableCompanion(groupId: Value(null)), ); @@ -465,7 +515,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required DateTime endedAt, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(endedAt: Value(endedAt)), ); @@ -477,7 +527,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes the match with the given [matchId] from the database. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future deleteMatch({required String matchId}) async { - final query = delete(matchTable)..where((g) => g.id.equals(matchId)); + final query = delete(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -493,7 +543,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes all matches associated with a specific game. /// Returns the number of matches deleted. Future deleteMatchesByGame({required String gameId}) async { - final query = delete(matchTable)..where((m) => m.gameId.equals(gameId)); + final query = delete(matchTable)..where((tbl) => tbl.gameId.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected; } diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index 51e5845..1a60243 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -17,7 +17,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// the new one. Future addPlayer({required Player player}) async { if (!await playerExists(playerId: player.id)) { - final int nameCount = await calculateNameCount(name: player.name); + final int nameCount = await _processNameCount(name: player.name); await into(playerTable).insert( PlayerTableCompanion.insert( @@ -64,7 +64,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final playersWithName = entry.value; // Get the current nameCount - var nameCount = await calculateNameCount(name: name); + var nameCount = await _processNameCount(name: name); // One player with the same name if (playersWithName.length == 1) { @@ -113,7 +113,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { Future getPlayerCount() async { final count = await (selectOnly(playerTable)..addColumns([playerTable.id.count()])) - .map((row) => row.read(playerTable.id.count())) + .map((tbl) => tbl.read(playerTable.id.count())) .getSingle(); return count ?? 0; } @@ -122,8 +122,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns `true` if the player exists, `false` otherwise. Future playerExists({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all players from the database. @@ -146,57 +146,76 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Retrieves a [Player] by their [id]. Future getPlayerById({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingle(); + final row = await query.getSingle(); return Player( - id: result.id, - name: result.name, - description: result.description, - createdAt: result.createdAt, - nameCount: result.nameCount, + id: row.id, + name: row.name, + description: row.description, + createdAt: row.createdAt, + nameCount: row.nameCount, ); } /* Update */ /// Updates the name of the player with the given [playerId] to [name]. + /// + /// Keeps the `nameCount` values of the affected name groups consistent: + /// - The renamed player gets a fresh `nameCount` for the new name group. + /// - All players in the previous name group whose `nameCount` was greater + /// than the removed one get decremented by 1, so the numbering stays + /// contiguous (1..N) in `createdAt` order. + /// - If only one player remains in the previous name group, their + /// `nameCount` is reset to 0. Future updatePlayerName({ required String playerId, required String name, }) async { - // Get previous name and name count for the player before updating - final previousPlayerName = - await (select(playerTable)..where((p) => p.id.equals(playerId))) - .map((row) => row.name) - .getSingleOrNull() ?? - ''; - final previousNameCount = await getNameCount(name: previousPlayerName); + return transaction(() async { + final previousPlayer = await (select( + playerTable, + )..where((tbl) => tbl.id.equals(playerId))).getSingleOrNull(); + if (previousPlayer == null) return false; - final rowsAffected = - await (update(playerTable)..where((p) => p.id.equals(playerId))).write( - PlayerTableCompanion(name: Value(name)), - ); + final previousName = previousPlayer.name; + final previousCount = previousPlayer.nameCount; - // Update name count for the new name - final count = await calculateNameCount(name: name); - if (count > 0) { - await (update(playerTable)..where((p) => p.name.equals(name))).write( - PlayerTableCompanion(nameCount: Value(count)), - ); - } + // Determine the nameCount for the renamed player in the new group. + final newNameCount = await _processNameCount(name: name); - if (previousNameCount > 0) { - // Get the player with that name and the hightest nameCount, and update their nameCount to previousNameCount - final player = await getPlayerWithHighestNameCount( - name: previousPlayerName, - ); - if (player != null) { - await updateNameCount( - playerId: player.id, - nameCount: previousNameCount, - ); + final rowsAffected = + await (update( + playerTable, + )..where((tbl) => tbl.id.equals(playerId))).write( + PlayerTableCompanion( + name: Value(name), + nameCount: Value(newNameCount), + ), + ); + + // Consolidate the previous name group. + final remainingCount = await getNameCount(name: previousName); + + if (remainingCount == 1) { + // Only one player left + await (update(playerTable)..where((p) => p.name.equals(previousName))) + .write(const PlayerTableCompanion(nameCount: Value(0))); + } else if (remainingCount > 1 && previousCount > 0) { + // Shift every player above the gap down by one to keep numbering in order. + await (update(playerTable)..where( + (tbl) => + tbl.name.equals(previousName) & + tbl.nameCount.isBiggerThanValue(previousCount), + )) + .write( + PlayerTableCompanion.custom( + nameCount: playerTable.nameCount - const Constant(1), + ), + ); } - } - return rowsAffected > 0; + + return rowsAffected > 0; + }); } /// Updates the description of the player with the given [playerId] to @@ -207,9 +226,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String description, }) async { final rowsAffected = - await (update(playerTable)..where((g) => g.id.equals(playerId))).write( - PlayerTableCompanion(description: Value(description)), - ); + await (update(playerTable)..where((tbl) => tbl.id.equals(playerId))) + .write(PlayerTableCompanion(description: Value(description))); return rowsAffected > 0; } @@ -218,7 +236,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Deletes the player with the given [id] from the database. /// Returns `true` if the player was deleted, `false` if the player did not exist. Future deletePlayer({required String playerId}) async { - final query = delete(playerTable)..where((p) => p.id.equals(playerId)); + final query = delete(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -226,8 +244,10 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /* Name count management */ /// Retrieves the count of players with the given [name]. + /// Returns the highest name count if players with the same name exist, + /// otherwise `null`. Future getNameCount({required String name}) async { - final query = select(playerTable)..where((p) => p.name.equals(name)); + final query = select(playerTable)..where((tbl) => tbl.name.equals(name)); final result = await query.get(); return result.length; } @@ -238,7 +258,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String playerId, required int nameCount, }) async { - final query = update(playerTable)..where((p) => p.id.equals(playerId)); + final query = update(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.write( PlayerTableCompanion(nameCount: Value(nameCount)), ); @@ -248,8 +268,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future getPlayerWithHighestNameCount({required String name}) async { final query = select(playerTable) - ..where((p) => p.name.equals(name)) - ..orderBy([(p) => OrderingTerm.desc(p.nameCount)]) + ..where((tbl) => tbl.name.equals(name)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.nameCount)]) ..limit(1); final result = await query.getSingleOrNull(); if (result != null) { @@ -264,34 +284,47 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return null; } + /// Processes the name count for a new player with the given [name]. + ///- 0 Player: returning 0 + ///- 1 Player: returning 2, and initializes the nameCount for the existing player to 1 + ///- Other: returning the existing count + 1 + Future _processNameCount({required String name}) async { + final nameCount = await calculateNameCount(name: name); + if (nameCount == 2) { + // If one other player exists with the same name, initialize the nameCount + await initializeNameCount(name: name); + } + return nameCount; + } + @visibleForTesting + /// Calculates the name count for a new player with the given [name]. + /// - 0 Players: Name count is 0 + /// - 1 Player: Name count is 2 (since the existing player will be 1) + /// - Other: Name count is the existing count + 1 Future calculateNameCount({required String name}) async { final count = await getNameCount(name: name); final int nameCount; - if (count == 1) { - // If one other player exists with the same name, initialize the nameCount - await initializeNameCount(name: name); - // And for the new player, set nameCount to 2 + if (count == 0) { + // If no other players exist with the same name, the returned nameCount is 0 + nameCount = 0; + } else if (count == 1) { + // If one other player with the name count exists, the returned name count is 2 nameCount = 2; - } else if (count > 1) { + } else { // If more than one player exists with the same name, just increment // the nameCount for the new player nameCount = count + 1; - } else { - // If no other players exist with the same name, set nameCount to 0 - nameCount = 0; } - return nameCount; } @visibleForTesting Future initializeNameCount({required String name}) async { final rowsAffected = - await (update(playerTable)..where((p) => p.name.equals(name))).write( - const PlayerTableCompanion(nameCount: Value(1)), - ); + await (update(playerTable)..where((tbl) => tbl.name.equals(name))) + .write(const PlayerTableCompanion(nameCount: Value(1))); return rowsAffected > 0; } diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index 48c5653..b48dc23 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -39,18 +39,25 @@ class PlayerGroupDao extends DatabaseAccessor /// Retrieves all players belonging to a specific group by [groupId]. Future> getPlayersOfGroup({required String groupId}) async { - final query = select(playerGroupTable) - ..where((pG) => pG.groupId.equals(groupId)); - final result = await query.get(); + final query = select(playerGroupTable).join([ + innerJoin( + playerTable, + playerTable.id.equalsExp(playerGroupTable.playerId), + ), + ])..where(playerGroupTable.groupId.equals(groupId)); - List groupMembers = List.empty(growable: true); - - for (var entry in result) { - final player = await db.playerDao.getPlayerById(playerId: entry.playerId); - groupMembers.add(player); - } - - return groupMembers; + final result = await query.map((row) => row.readTable(playerTable)).get(); + return result + .map( + (row) => Player( + id: row.id, + createdAt: row.createdAt, + name: row.name, + nameCount: row.nameCount, + description: row.description, + ), + ) + .toList(); } /// Checks if a player with [playerId] is in the group with [groupId]. @@ -60,7 +67,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = select(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final result = await query.getSingleOrNull(); return result != null; } @@ -81,7 +90,7 @@ class PlayerGroupDao extends DatabaseAccessor await db.transaction(() async { // Remove all existing players from the group final deleteQuery = delete(db.playerGroupTable) - ..where((p) => p.groupId.equals(groupId)); + ..where((tbl) => tbl.groupId.equals(groupId)); await deleteQuery.go(); // Add new players to the player table if they don't exist @@ -121,7 +130,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = delete(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index d119468..912cfcc 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -40,7 +40,7 @@ class PlayerMatchDao extends DatabaseAccessor await (selectOnly(playerMatchTable) ..where(playerMatchTable.matchId.equals(matchId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -56,7 +56,7 @@ class PlayerMatchDao extends DatabaseAccessor ..where(playerMatchTable.matchId.equals(matchId)) ..where(playerMatchTable.playerId.equals(playerId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -66,7 +66,7 @@ class PlayerMatchDao extends DatabaseAccessor Future> getPlayersOfMatch({required String matchId}) async { final result = await (select( playerMatchTable, - )..where((p) => p.matchId.equals(matchId))).get(); + )..where((tbl) => tbl.matchId.equals(matchId))).get(); if (result.isEmpty) return []; @@ -85,8 +85,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final result = await (select(playerMatchTable) - ..where((p) => p.matchId.equals(matchId)) - ..where((p) => p.teamId.equals(teamId))) + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.teamId.equals(teamId))) .get(); if (result.isEmpty) return []; @@ -109,7 +109,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final rowsAffected = await (update(playerMatchTable)..where( - (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + (tbl) => + tbl.matchId.equals(matchId) & tbl.playerId.equals(playerId), )) .write(PlayerMatchTableCompanion(teamId: Value(teamId))); return rowsAffected > 0; @@ -143,9 +144,9 @@ class PlayerMatchDao extends DatabaseAccessor // Remove old players if (playersToRemove.isNotEmpty) { await (delete(playerMatchTable)..where( - (pg) => - pg.matchId.equals(matchId) & - pg.playerId.isIn(playersToRemove.toList()), + (tbl) => + tbl.matchId.equals(matchId) & + tbl.playerId.isIn(playersToRemove.toList()), )) .go(); } @@ -182,8 +183,8 @@ class PlayerMatchDao extends DatabaseAccessor required String playerId, }) async { final query = delete(playerMatchTable) - ..where((pg) => pg.matchId.equals(matchId)) - ..where((pg) => pg.playerId.equals(playerId)); + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.playerId.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/score_entry_dao.dart b/lib/data/dao/score_entry_dao.dart index 830135d..276e8fd 100644 --- a/lib/data/dao/score_entry_dao.dart +++ b/lib/data/dao/score_entry_dao.dart @@ -70,10 +70,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = select(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final result = await query.getSingleOrNull(); @@ -91,7 +91,7 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final result = await query.get(); final Map scoresByPlayer = {}; @@ -113,8 +113,10 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)) - ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ) + ..orderBy([(tbl) => OrderingTerm.asc(tbl.roundNumber)]); final result = await query.get(); return result .map( @@ -136,8 +138,8 @@ class ScoreEntryDao extends DatabaseAccessor final query = selectOnly(scoreEntryTable) ..where(scoreEntryTable.matchId.equals(matchId)) ..addColumns([scoreEntryTable.roundNumber.max()]); - final result = await query.getSingle(); - return result.read(scoreEntryTable.roundNumber.max()); + final row = await query.getSingle(); + return row.read(scoreEntryTable.roundNumber.max()); } /// Aggregates the total score for a player in a match by summing all their @@ -166,10 +168,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final rowsAffected = await (update(scoreEntryTable)..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(entry.roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(entry.roundNumber), )) .write( ScoreEntryTableCompanion( @@ -190,10 +192,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = delete(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final rowsAffected = await query.go(); return rowsAffected > 0; @@ -201,7 +203,7 @@ class ScoreEntryDao extends DatabaseAccessor Future deleteAllScoresForMatch({required String matchId}) async { final query = delete(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -211,7 +213,9 @@ class ScoreEntryDao extends DatabaseAccessor required String playerId, }) async { final query = delete(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart new file mode 100644 index 0000000..092ceb0 --- /dev/null +++ b/lib/data/dao/statistic_dao.dart @@ -0,0 +1,127 @@ +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; +import 'package:tallee/data/models/statistic.dart'; + +part 'statistic_dao.g.dart'; + +@DriftAccessor(tables: [StatisticTable]) +class StatisticDao extends DatabaseAccessor + with _$StatisticDaoMixin { + StatisticDao(super.db); + + /* Create */ + + Future addStatistic({required Statistic statistic}) async { + await into(statisticTable).insert( + StatisticTableCompanion.insert( + id: statistic.id, + type: statistic.type.name, + timeframe: Value(statistic.timeframe?.name), + displayCount: Value(statistic.displayCount), + ), + mode: InsertMode.insertOrReplace, + ); + + await db.statisticScopeDao.addStatisticScopes( + statisticId: statistic.id, + scopes: statistic.scopes, + ); + + if (statistic.selectedGroups != null) { + await db.statisticGroupDao.addStatisticGroups( + statisticId: statistic.id, + groups: statistic.selectedGroups!, + ); + } + + if (statistic.selectedGames != null) { + await db.statisticGameDao.addStatisticGames( + statisticId: statistic.id, + games: statistic.selectedGames!, + ); + } + + return true; + } + + /* Read */ + + Future getStatisticById(String statisticId) async { + final query = select(statisticTable); + final row = await query.getSingleOrNull(); + if (row != null) { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere((type) => type.name == row.type), + scopes: scopes, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + displayCount: row.displayCount, + id: row.id, + ); + } + return null; + } + + /// Retrieves all statistics from the database, including their associated groups and games. + Future> getAllStatistics() async { + final query = select(statisticTable); + final result = await query.get(); + return Future.wait( + result.map((row) async { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere( + (type) => type.name == row.type, + ), + scopes: scopes, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + displayCount: row.displayCount, + id: row.id, + ); + }), + ); + } + + /* Update */ + + Future updateDisplayCount(String statisticId, int displayCount) async { + final rowsUpdated = + await (update(statisticTable) + ..where((tbl) => tbl.id.equals(statisticId))) + .write(StatisticTableCompanion(displayCount: Value(displayCount))); + + return rowsUpdated > 0; + } + + /* Delete */ + + Future deleteStatistic(String statisticId) async { + final rowsDeleted = await (delete( + statisticTable, + )..where((tbl) => tbl.id.equals(statisticId))).go(); + + return rowsDeleted > 0; + } + + Future deleteAllStatistics() async { + final rowsDeleted = await delete(statisticTable).go(); + return rowsDeleted > 0; + } +} diff --git a/lib/data/dao/statistic_dao.g.dart b/lib/data/dao/statistic_dao.g.dart new file mode 100644 index 0000000..0ce36e1 --- /dev/null +++ b/lib/data/dao/statistic_dao.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + StatisticDaoManager get managers => StatisticDaoManager(this); +} + +class StatisticDaoManager { + final _$StatisticDaoMixin _db; + StatisticDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); +} diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart new file mode 100644 index 0000000..d546ce0 --- /dev/null +++ b/lib/data/dao/statistic_game_dao.dart @@ -0,0 +1,61 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/models/game.dart'; + +part 'statistic_game_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGameTable]) +class StatisticGameDao extends DatabaseAccessor + with _$StatisticGameDaoMixin { + StatisticGameDao(super.db); + + /// Retrieves a list of games associated with a specific statistic. + Future?> getGamesForStatistic(String statisticId) async { + final query = select(statisticGameTable).join([ + innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)), + ])..where(statisticGameTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(gameTable)).get(); + if (results.isEmpty) return null; + return results + .map( + (row) => Game( + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, + ), + ) + .toList(); + } + + Future addStatisticGames({ + required String statisticId, + required List games, + }) { + final entries = games + .map( + (game) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: game.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGameTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic games: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_game_dao.g.dart b/lib/data/dao/statistic_game_dao.g.dart new file mode 100644 index 0000000..d6ee984 --- /dev/null +++ b/lib/data/dao/statistic_game_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_game_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGameDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GameTableTable get gameTable => attachedDatabase.gameTable; + $StatisticGameTableTable get statisticGameTable => + attachedDatabase.statisticGameTable; + StatisticGameDaoManager get managers => StatisticGameDaoManager(this); +} + +class StatisticGameDaoManager { + final _$StatisticGameDaoMixin _db; + StatisticGameDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GameTableTableTableManager get gameTable => + $$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager( + _db.attachedDatabase, + _db.statisticGameTable, + ); +} diff --git a/lib/data/dao/statistic_group_dao.dart b/lib/data/dao/statistic_group_dao.dart new file mode 100644 index 0000000..449b6a8 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/models/group.dart'; + +part 'statistic_group_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGroupTable, GroupTable]) +class StatisticGroupDao extends DatabaseAccessor + with _$StatisticGroupDaoMixin { + StatisticGroupDao(super.db); + + /// Retrieves a list of groups associated with a specific statistic. + Future?> getGroupsForStatistic(String statisticId) async { + final query = select(statisticGroupTable).join([ + innerJoin( + groupTable, + groupTable.id.equalsExp(statisticGroupTable.groupId), + ), + ])..where(statisticGroupTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(groupTable)).get(); + if (results.isEmpty) return null; + final groups = await Future.wait( + results.map((result) async { + final groupMembers = await db.playerGroupDao.getPlayersOfGroup( + groupId: result.id, + ); + return Group( + id: result.id, + createdAt: result.createdAt, + name: result.name, + description: result.description, + members: groupMembers, + ); + }), + ); + + return groups; + } + + Future addStatisticGroups({ + required String statisticId, + required List groups, + }) async { + final entries = groups + .map( + (group) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: group.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGroupTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic groups: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_group_dao.g.dart b/lib/data/dao/statistic_group_dao.g.dart new file mode 100644 index 0000000..57a83c5 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_group_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGroupDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; + $StatisticGroupTableTable get statisticGroupTable => + attachedDatabase.statisticGroupTable; + StatisticGroupDaoManager get managers => StatisticGroupDaoManager(this); +} + +class StatisticGroupDaoManager { + final _$StatisticGroupDaoMixin _db; + StatisticGroupDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GroupTableTableTableManager get groupTable => + $$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager( + _db.attachedDatabase, + _db.statisticGroupTable, + ); +} diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart new file mode 100644 index 0000000..eb286af --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.dart @@ -0,0 +1,55 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; + +part 'statistic_scope_dao.g.dart'; + +@DriftAccessor(tables: [StatisticScopeTable]) +class StatisticScopeDao extends DatabaseAccessor + with _$StatisticScopeDaoMixin { + StatisticScopeDao(super.db); + + /// Retrieves a list of statistic scopes associated with a specific statistic ID. + Future> getScopeForStatistic(String statisticId) async { + final query = select(statisticScopeTable) + ..where((tbl) => tbl.statisticId.equals(statisticId)); + + final result = await query.get(); + return result + .map( + (row) => StatisticScope.values.firstWhere( + (e) => e.name == row.scope, + orElse: () => throw Exception( + 'Invalid scope value: ${row.scope} for statistic ID: $statisticId', + ), + ), + ) + .toList(); + } + + Future addStatisticScopes({ + required String statisticId, + required List scopes, + }) async { + final entries = scopes + .map( + (scope) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope.name, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticScopeTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic scopes: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_scope_dao.g.dart b/lib/data/dao/statistic_scope_dao.g.dart new file mode 100644 index 0000000..adaa171 --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_scope_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticScopeDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $StatisticScopeTableTable get statisticScopeTable => + attachedDatabase.statisticScopeTable; + StatisticScopeDaoManager get managers => StatisticScopeDaoManager(this); +} + +class StatisticScopeDaoManager { + final _$StatisticScopeDaoMixin _db; + StatisticScopeDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager( + _db.attachedDatabase, + _db.statisticScopeTable, + ); +} diff --git a/lib/data/dao/team_dao.dart b/lib/data/dao/team_dao.dart index cba68fb..333db68 100644 --- a/lib/data/dao/team_dao.dart +++ b/lib/data/dao/team_dao.dart @@ -86,7 +86,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future getTeamCount() async { final count = await (selectOnly(teamTable)..addColumns([teamTable.id.count()])) - .map((row) => row.read(teamTable.id.count())) + .map((tbl) => tbl.read(teamTable.id.count())) .getSingle(); return count ?? 0; } @@ -95,8 +95,8 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Returns `true` if the team exists, `false` otherwise. Future teamExists({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all teams from the database. @@ -119,12 +119,12 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Retrieves a [Team] by its [teamId], including its members. Future getTeamById({required String teamId}) async { final query = select(teamTable)..where((t) => t.id.equals(teamId)); - final result = await query.getSingle(); + final row = await query.getSingle(); final members = await _getTeamMembers(teamId: teamId); return Team( - id: result.id, - name: result.name, - createdAt: result.createdAt, + id: row.id, + name: row.name, + createdAt: row.createdAt, members: members, ); } @@ -133,13 +133,13 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future> _getTeamMembers({required String teamId}) async { // Get all player_match entries with this teamId final playerMatchQuery = select(db.playerMatchTable) - ..where((pm) => pm.teamId.equals(teamId)); + ..where((tbl) => tbl.teamId.equals(teamId)); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; // Get unique player IDs - final playerIds = playerMatches.map((pm) => pm.playerId).toSet(); + final playerIds = playerMatches.map((tbl) => tbl.playerId).toSet(); // Fetch all players final players = await Future.wait( @@ -156,7 +156,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { required String name, }) async { final rowsAffected = - await (update(teamTable)..where((t) => t.id.equals(teamId))).write( + await (update(teamTable)..where((tbl) => tbl.id.equals(teamId))).write( TeamTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -175,7 +175,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// Deletes the team with the given [teamId] from the database. /// Returns `true` if the team was deleted, `false` otherwise. Future deleteTeam({required String teamId}) async { - final query = delete(teamTable)..where((t) => t.id.equals(teamId)); + final query = delete(teamTable)..where((tbl) => tbl.id.equals(teamId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index a7e9c1d..792da0e 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -8,6 +8,10 @@ import 'package:tallee/data/dao/player_dao.dart'; import 'package:tallee/data/dao/player_group_dao.dart'; import 'package:tallee/data/dao/player_match_dao.dart'; import 'package:tallee/data/dao/score_entry_dao.dart'; +import 'package:tallee/data/dao/statistic_dao.dart'; +import 'package:tallee/data/dao/statistic_game_dao.dart'; +import 'package:tallee/data/dao/statistic_group_dao.dart'; +import 'package:tallee/data/dao/statistic_scope_dao.dart'; import 'package:tallee/data/dao/team_dao.dart'; import 'package:tallee/data/db/tables/game_table.dart'; import 'package:tallee/data/db/tables/group_table.dart'; @@ -16,6 +20,10 @@ import 'package:tallee/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_match_table.dart'; import 'package:tallee/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/score_entry_table.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; import 'package:tallee/data/db/tables/team_table.dart'; part 'database.g.dart'; @@ -30,6 +38,10 @@ part 'database.g.dart'; GameTable, TeamTable, ScoreEntryTable, + StatisticTable, + StatisticScopeTable, + StatisticGameTable, + StatisticGroupTable, ], daos: [ PlayerDao, @@ -40,6 +52,10 @@ part 'database.g.dart'; GameDao, ScoreEntryDao, TeamDao, + StatisticDao, + StatisticScopeDao, + StatisticGameDao, + StatisticGroupDao, ], ) class AppDatabase extends _$AppDatabase { diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index c8d0faa..3a9d277 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -2732,6 +2732,1020 @@ class ScoreEntryTableCompanion extends UpdateCompanion { } } +class $StatisticTableTable extends StatisticTable + with TableInfo<$StatisticTableTable, StatisticTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _typeMeta = const VerificationMeta('type'); + @override + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _timeframeMeta = const VerificationMeta( + 'timeframe', + ); + @override + late final GeneratedColumn timeframe = GeneratedColumn( + 'timeframe', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _displayCountMeta = const VerificationMeta( + 'displayCount', + ); + @override + late final GeneratedColumn displayCount = GeneratedColumn( + 'display_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(5), + ); + @override + List get $columns => [id, type, timeframe, displayCount]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, + type.isAcceptableOrUnknown(data['type']!, _typeMeta), + ); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('timeframe')) { + context.handle( + _timeframeMeta, + timeframe.isAcceptableOrUnknown(data['timeframe']!, _timeframeMeta), + ); + } + if (data.containsKey('display_count')) { + context.handle( + _displayCountMeta, + displayCount.isAcceptableOrUnknown( + data['display_count']!, + _displayCountMeta, + ), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + StatisticTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticTableData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + timeframe: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}timeframe'], + ), + displayCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}display_count'], + )!, + ); + } + + @override + $StatisticTableTable createAlias(String alias) { + return $StatisticTableTable(attachedDatabase, alias); + } +} + +class StatisticTableData extends DataClass + implements Insertable { + final String id; + final String type; + final String? timeframe; + final int displayCount; + const StatisticTableData({ + required this.id, + required this.type, + this.timeframe, + required this.displayCount, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['type'] = Variable(type); + if (!nullToAbsent || timeframe != null) { + map['timeframe'] = Variable(timeframe); + } + map['display_count'] = Variable(displayCount); + return map; + } + + StatisticTableCompanion toCompanion(bool nullToAbsent) { + return StatisticTableCompanion( + id: Value(id), + type: Value(type), + timeframe: timeframe == null && nullToAbsent + ? const Value.absent() + : Value(timeframe), + displayCount: Value(displayCount), + ); + } + + factory StatisticTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticTableData( + id: serializer.fromJson(json['id']), + type: serializer.fromJson(json['type']), + timeframe: serializer.fromJson(json['timeframe']), + displayCount: serializer.fromJson(json['displayCount']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'type': serializer.toJson(type), + 'timeframe': serializer.toJson(timeframe), + 'displayCount': serializer.toJson(displayCount), + }; + } + + StatisticTableData copyWith({ + String? id, + String? type, + Value timeframe = const Value.absent(), + int? displayCount, + }) => StatisticTableData( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe.present ? timeframe.value : this.timeframe, + displayCount: displayCount ?? this.displayCount, + ); + StatisticTableData copyWithCompanion(StatisticTableCompanion data) { + return StatisticTableData( + id: data.id.present ? data.id.value : this.id, + type: data.type.present ? data.type.value : this.type, + timeframe: data.timeframe.present ? data.timeframe.value : this.timeframe, + displayCount: data.displayCount.present + ? data.displayCount.value + : this.displayCount, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticTableData(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, type, timeframe, displayCount); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticTableData && + other.id == this.id && + other.type == this.type && + other.timeframe == this.timeframe && + other.displayCount == this.displayCount); +} + +class StatisticTableCompanion extends UpdateCompanion { + final Value id; + final Value type; + final Value timeframe; + final Value displayCount; + final Value rowid; + const StatisticTableCompanion({ + this.id = const Value.absent(), + this.type = const Value.absent(), + this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticTableCompanion.insert({ + required String id, + required String type, + this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), + this.rowid = const Value.absent(), + }) : id = Value(id), + type = Value(type); + static Insertable custom({ + Expression? id, + Expression? type, + Expression? timeframe, + Expression? displayCount, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (type != null) 'type': type, + if (timeframe != null) 'timeframe': timeframe, + if (displayCount != null) 'display_count': displayCount, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticTableCompanion copyWith({ + Value? id, + Value? type, + Value? timeframe, + Value? displayCount, + Value? rowid, + }) { + return StatisticTableCompanion( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe ?? this.timeframe, + displayCount: displayCount ?? this.displayCount, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (timeframe.present) { + map['timeframe'] = Variable(timeframe.value); + } + if (displayCount.present) { + map['display_count'] = Variable(displayCount.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticTableCompanion(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticScopeTableTable extends StatisticScopeTable + with TableInfo<$StatisticScopeTableTable, StatisticScopeTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticScopeTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _scopeMeta = const VerificationMeta('scope'); + @override + late final GeneratedColumn scope = GeneratedColumn( + 'scope', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [statisticId, scope]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_scope_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('scope')) { + context.handle( + _scopeMeta, + scope.isAcceptableOrUnknown(data['scope']!, _scopeMeta), + ); + } else if (isInserting) { + context.missing(_scopeMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, scope}; + @override + StatisticScopeTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticScopeTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + scope: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}scope'], + )!, + ); + } + + @override + $StatisticScopeTableTable createAlias(String alias) { + return $StatisticScopeTableTable(attachedDatabase, alias); + } +} + +class StatisticScopeTableData extends DataClass + implements Insertable { + final String statisticId; + final String scope; + const StatisticScopeTableData({ + required this.statisticId, + required this.scope, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['scope'] = Variable(scope); + return map; + } + + StatisticScopeTableCompanion toCompanion(bool nullToAbsent) { + return StatisticScopeTableCompanion( + statisticId: Value(statisticId), + scope: Value(scope), + ); + } + + factory StatisticScopeTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticScopeTableData( + statisticId: serializer.fromJson(json['statisticId']), + scope: serializer.fromJson(json['scope']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'scope': serializer.toJson(scope), + }; + } + + StatisticScopeTableData copyWith({String? statisticId, String? scope}) => + StatisticScopeTableData( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + ); + StatisticScopeTableData copyWithCompanion(StatisticScopeTableCompanion data) { + return StatisticScopeTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + scope: data.scope.present ? data.scope.value : this.scope, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableData(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, scope); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticScopeTableData && + other.statisticId == this.statisticId && + other.scope == this.scope); +} + +class StatisticScopeTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value scope; + final Value rowid; + const StatisticScopeTableCompanion({ + this.statisticId = const Value.absent(), + this.scope = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticScopeTableCompanion.insert({ + required String statisticId, + required String scope, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + scope = Value(scope); + static Insertable custom({ + Expression? statisticId, + Expression? scope, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (scope != null) 'scope': scope, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticScopeTableCompanion copyWith({ + Value? statisticId, + Value? scope, + Value? rowid, + }) { + return StatisticScopeTableCompanion( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (scope.present) { + map['scope'] = Variable(scope.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGameTableTable extends StatisticGameTable + with TableInfo<$StatisticGameTableTable, StatisticGameTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGameTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _gameIdMeta = const VerificationMeta('gameId'); + @override + late final GeneratedColumn gameId = GeneratedColumn( + 'game_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES game_table (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [statisticId, gameId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_game_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('game_id')) { + context.handle( + _gameIdMeta, + gameId.isAcceptableOrUnknown(data['game_id']!, _gameIdMeta), + ); + } else if (isInserting) { + context.missing(_gameIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, gameId}; + @override + StatisticGameTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGameTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + gameId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}game_id'], + )!, + ); + } + + @override + $StatisticGameTableTable createAlias(String alias) { + return $StatisticGameTableTable(attachedDatabase, alias); + } +} + +class StatisticGameTableData extends DataClass + implements Insertable { + final String statisticId; + final String gameId; + const StatisticGameTableData({ + required this.statisticId, + required this.gameId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['game_id'] = Variable(gameId); + return map; + } + + StatisticGameTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGameTableCompanion( + statisticId: Value(statisticId), + gameId: Value(gameId), + ); + } + + factory StatisticGameTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGameTableData( + statisticId: serializer.fromJson(json['statisticId']), + gameId: serializer.fromJson(json['gameId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'gameId': serializer.toJson(gameId), + }; + } + + StatisticGameTableData copyWith({String? statisticId, String? gameId}) => + StatisticGameTableData( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + ); + StatisticGameTableData copyWithCompanion(StatisticGameTableCompanion data) { + return StatisticGameTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + gameId: data.gameId.present ? data.gameId.value : this.gameId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableData(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, gameId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGameTableData && + other.statisticId == this.statisticId && + other.gameId == this.gameId); +} + +class StatisticGameTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value gameId; + final Value rowid; + const StatisticGameTableCompanion({ + this.statisticId = const Value.absent(), + this.gameId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGameTableCompanion.insert({ + required String statisticId, + required String gameId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + gameId = Value(gameId); + static Insertable custom({ + Expression? statisticId, + Expression? gameId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (gameId != null) 'game_id': gameId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGameTableCompanion copyWith({ + Value? statisticId, + Value? gameId, + Value? rowid, + }) { + return StatisticGameTableCompanion( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (gameId.present) { + map['game_id'] = Variable(gameId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGroupTableTable extends StatisticGroupTable + with TableInfo<$StatisticGroupTableTable, StatisticGroupTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGroupTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _groupIdMeta = const VerificationMeta( + 'groupId', + ); + @override + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES group_table (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [statisticId, groupId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_group_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('group_id')) { + context.handle( + _groupIdMeta, + groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), + ); + } else if (isInserting) { + context.missing(_groupIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, groupId}; + @override + StatisticGroupTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGroupTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + ); + } + + @override + $StatisticGroupTableTable createAlias(String alias) { + return $StatisticGroupTableTable(attachedDatabase, alias); + } +} + +class StatisticGroupTableData extends DataClass + implements Insertable { + final String statisticId; + final String groupId; + const StatisticGroupTableData({ + required this.statisticId, + required this.groupId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['group_id'] = Variable(groupId); + return map; + } + + StatisticGroupTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGroupTableCompanion( + statisticId: Value(statisticId), + groupId: Value(groupId), + ); + } + + factory StatisticGroupTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGroupTableData( + statisticId: serializer.fromJson(json['statisticId']), + groupId: serializer.fromJson(json['groupId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'groupId': serializer.toJson(groupId), + }; + } + + StatisticGroupTableData copyWith({String? statisticId, String? groupId}) => + StatisticGroupTableData( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + ); + StatisticGroupTableData copyWithCompanion(StatisticGroupTableCompanion data) { + return StatisticGroupTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableData(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, groupId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGroupTableData && + other.statisticId == this.statisticId && + other.groupId == this.groupId); +} + +class StatisticGroupTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value groupId; + final Value rowid; + const StatisticGroupTableCompanion({ + this.statisticId = const Value.absent(), + this.groupId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGroupTableCompanion.insert({ + required String statisticId, + required String groupId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + groupId = Value(groupId); + static Insertable custom({ + Expression? statisticId, + Expression? groupId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (groupId != null) 'group_id': groupId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGroupTableCompanion copyWith({ + Value? statisticId, + Value? groupId, + Value? rowid, + }) { + return StatisticGroupTableCompanion( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); @@ -2749,6 +3763,13 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final $ScoreEntryTableTable scoreEntryTable = $ScoreEntryTableTable( this, ); + late final $StatisticTableTable statisticTable = $StatisticTableTable(this); + late final $StatisticScopeTableTable statisticScopeTable = + $StatisticScopeTableTable(this); + late final $StatisticGameTableTable statisticGameTable = + $StatisticGameTableTable(this); + late final $StatisticGroupTableTable statisticGroupTable = + $StatisticGroupTableTable(this); late final PlayerDao playerDao = PlayerDao(this as AppDatabase); late final GroupDao groupDao = GroupDao(this as AppDatabase); late final MatchDao matchDao = MatchDao(this as AppDatabase); @@ -2761,6 +3782,16 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final GameDao gameDao = GameDao(this as AppDatabase); late final ScoreEntryDao scoreEntryDao = ScoreEntryDao(this as AppDatabase); late final TeamDao teamDao = TeamDao(this as AppDatabase); + late final StatisticDao statisticDao = StatisticDao(this as AppDatabase); + late final StatisticScopeDao statisticScopeDao = StatisticScopeDao( + this as AppDatabase, + ); + late final StatisticGameDao statisticGameDao = StatisticGameDao( + this as AppDatabase, + ); + late final StatisticGroupDao statisticGroupDao = StatisticGroupDao( + this as AppDatabase, + ); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -2774,6 +3805,10 @@ abstract class _$AppDatabase extends GeneratedDatabase { teamTable, playerMatchTable, scoreEntryTable, + statisticTable, + statisticScopeTable, + statisticGameTable, + statisticGroupTable, ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ @@ -2840,6 +3875,41 @@ abstract class _$AppDatabase extends GeneratedDatabase { ), result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)], ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_scope_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'game_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'group_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), ]); } @@ -3419,6 +4489,33 @@ final class $$GroupTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.groupTable.id, + db.statisticGroupTable.groupId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GroupTableTableFilterComposer @@ -3499,6 +4596,31 @@ class $$GroupTableTableFilterComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableOrderingComposer @@ -3603,6 +4725,32 @@ class $$GroupTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableTableManager @@ -3621,6 +4769,7 @@ class $$GroupTableTableTableManager PrefetchHooks Function({ bool matchTableRefs, bool playerGroupTableRefs, + bool statisticGroupTableRefs, }) > { $$GroupTableTableTableManager(_$AppDatabase db, $GroupTableTable table) @@ -3671,12 +4820,17 @@ class $$GroupTableTableTableManager ) .toList(), prefetchHooksCallback: - ({matchTableRefs = false, playerGroupTableRefs = false}) { + ({ + matchTableRefs = false, + playerGroupTableRefs = false, + statisticGroupTableRefs = false, + }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (matchTableRefs) db.matchTable, if (playerGroupTableRefs) db.playerGroupTable, + if (statisticGroupTableRefs) db.statisticGroupTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -3723,6 +4877,27 @@ class $$GroupTableTableTableManager ), typedResults: items, ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + GroupTableData, + $GroupTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$GroupTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.groupId == item.id, + ), + typedResults: items, + ), ]; }, ); @@ -3743,7 +4918,11 @@ typedef $$GroupTableTableProcessedTableManager = $$GroupTableTableUpdateCompanionBuilder, (GroupTableData, $$GroupTableTableReferences), GroupTableData, - PrefetchHooks Function({bool matchTableRefs, bool playerGroupTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool playerGroupTableRefs, + bool statisticGroupTableRefs, + }) >; typedef $$GameTableTableCreateCompanionBuilder = GameTableCompanion Function({ @@ -3789,6 +4968,33 @@ final class $$GameTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.gameTable.id, + db.statisticGameTable.gameId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.gameId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GameTableTableFilterComposer @@ -3859,6 +5065,31 @@ class $$GameTableTableFilterComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableOrderingComposer @@ -3962,6 +5193,32 @@ class $$GameTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableTableManager @@ -3977,7 +5234,10 @@ class $$GameTableTableTableManager $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool statisticGameTableRefs, + }) > { $$GameTableTableTableManager(_$AppDatabase db, $GameTableTable table) : super( @@ -4038,36 +5298,63 @@ class $$GameTableTableTableManager ), ) .toList(), - prefetchHooksCallback: ({matchTableRefs = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [if (matchTableRefs) db.matchTable], - addJoins: null, - getPrefetchedDataCallback: (items) async { - return [ - if (matchTableRefs) - await $_getPrefetchedData< - GameTableData, - $GameTableTable, - MatchTableData - >( - currentTable: table, - referencedTable: $$GameTableTableReferences - ._matchTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GameTableTableReferences( - db, - table, - p0, - ).matchTableRefs, - referencedItemsForCurrentItem: (item, referencedItems) => - referencedItems.where((e) => e.gameId == item.id), - typedResults: items, - ), - ]; + prefetchHooksCallback: + ({matchTableRefs = false, statisticGameTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (matchTableRefs) db.matchTable, + if (statisticGameTableRefs) db.statisticGameTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (matchTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + MatchTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._matchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).matchTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.gameId == item.id, + ), + typedResults: items, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.gameId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); }, - ); - }, ), ); } @@ -4084,7 +5371,7 @@ typedef $$GameTableTableProcessedTableManager = $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({bool matchTableRefs, bool statisticGameTableRefs}) >; typedef $$MatchTableTableCreateCompanionBuilder = MatchTableCompanion Function({ @@ -6273,6 +7560,1557 @@ typedef $$ScoreEntryTableTableProcessedTableManager = ScoreEntryTableData, PrefetchHooks Function({bool playerId, bool matchId}) >; +typedef $$StatisticTableTableCreateCompanionBuilder = + StatisticTableCompanion Function({ + required String id, + required String type, + Value timeframe, + Value displayCount, + Value rowid, + }); +typedef $$StatisticTableTableUpdateCompanionBuilder = + StatisticTableCompanion Function({ + Value id, + Value type, + Value timeframe, + Value displayCount, + Value rowid, + }); + +final class $$StatisticTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData + > { + $$StatisticTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static MultiTypedResultKey< + $StatisticScopeTableTable, + List + > + _statisticScopeTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticScopeTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticScopeTable.statisticId, + ), + ); + + $$StatisticScopeTableTableProcessedTableManager get statisticScopeTableRefs { + final manager = $$StatisticScopeTableTableTableManager( + $_db, + $_db.statisticScopeTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticScopeTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGameTable.statisticId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGroupTable.statisticId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$StatisticTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnFilters(column), + ); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableFilterComposer f) f, + ) { + final $$StatisticScopeTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableFilterComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$StatisticTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + GeneratedColumn get timeframe => + $composableBuilder(column: $table.timeframe, builder: (column) => column); + + GeneratedColumn get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => column, + ); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableAnnotationComposer a) f, + ) { + final $$StatisticScopeTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + > { + $$StatisticTableTableTableManager( + _$AppDatabase db, + $StatisticTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value type = const Value.absent(), + Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion( + id: id, + type: type, + timeframe: timeframe, + displayCount: displayCount, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required String type, + Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion.insert( + id: id, + type: type, + timeframe: timeframe, + displayCount: displayCount, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: + ({ + statisticScopeTableRefs = false, + statisticGameTableRefs = false, + statisticGroupTableRefs = false, + }) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (statisticScopeTableRefs) db.statisticScopeTable, + if (statisticGameTableRefs) db.statisticGameTable, + if (statisticGroupTableRefs) db.statisticGroupTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (statisticScopeTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticScopeTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticScopeTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticScopeTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$StatisticTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + >; +typedef $$StatisticScopeTableTableCreateCompanionBuilder = + StatisticScopeTableCompanion Function({ + required String statisticId, + required String scope, + Value rowid, + }); +typedef $$StatisticScopeTableTableUpdateCompanionBuilder = + StatisticScopeTableCompanion Function({ + Value statisticId, + Value scope, + Value rowid, + }); + +final class $$StatisticScopeTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData + > { + $$StatisticScopeTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticScopeTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticScopeTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnFilters(column), + ); + + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnOrderings(column), + ); + + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get scope => + $composableBuilder(column: $table.scope, builder: (column) => column); + + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + > { + $$StatisticScopeTableTableTableManager( + _$AppDatabase db, + $StatisticScopeTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticScopeTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticScopeTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticScopeTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value scope = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String scope, + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticScopeTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticScopeTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + >; +typedef $$StatisticGameTableTableCreateCompanionBuilder = + StatisticGameTableCompanion Function({ + required String statisticId, + required String gameId, + Value rowid, + }); +typedef $$StatisticGameTableTableUpdateCompanionBuilder = + StatisticGameTableCompanion Function({ + Value statisticId, + Value gameId, + Value rowid, + }); + +final class $$StatisticGameTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData + > { + $$StatisticGameTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGameTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GameTableTable _gameIdTable(_$AppDatabase db) => + db.gameTable.createAlias( + $_aliasNameGenerator(db.statisticGameTable.gameId, db.gameTable.id), + ); + + $$GameTableTableProcessedTableManager get gameId { + final $_column = $_itemColumn('game_id')!; + + final manager = $$GameTableTableTableManager( + $_db, + $_db.gameTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_gameIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticGameTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableFilterComposer get gameId { + final $$GameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableFilterComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableOrderingComposer get gameId { + final $$GameTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableOrderingComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GameTableTableAnnotationComposer get gameId { + final $$GameTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableAnnotationComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGameTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + > { + $$StatisticGameTableTableTableManager( + _$AppDatabase db, + $StatisticGameTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGameTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGameTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticGameTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value gameId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String gameId, + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGameTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, gameId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGameTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (gameId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.gameId, + referencedTable: + $$StatisticGameTableTableReferences + ._gameIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._gameIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGameTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + >; +typedef $$StatisticGroupTableTableCreateCompanionBuilder = + StatisticGroupTableCompanion Function({ + required String statisticId, + required String groupId, + Value rowid, + }); +typedef $$StatisticGroupTableTableUpdateCompanionBuilder = + StatisticGroupTableCompanion Function({ + Value statisticId, + Value groupId, + Value rowid, + }); + +final class $$StatisticGroupTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData + > { + $$StatisticGroupTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGroupTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GroupTableTable _groupIdTable(_$AppDatabase db) => + db.groupTable.createAlias( + $_aliasNameGenerator(db.statisticGroupTable.groupId, db.groupTable.id), + ); + + $$GroupTableTableProcessedTableManager get groupId { + final $_column = $_itemColumn('group_id')!; + + final manager = $$GroupTableTableTableManager( + $_db, + $_db.groupTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticGroupTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableFilterComposer get groupId { + final $$GroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableFilterComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableOrderingComposer get groupId { + final $$GroupTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableOrderingComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableAnnotationComposer get groupId { + final $$GroupTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableAnnotationComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticGroupTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + > { + $$StatisticGroupTableTableTableManager( + _$AppDatabase db, + $StatisticGroupTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGroupTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGroupTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticGroupTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value groupId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String groupId, + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGroupTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, groupId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (groupId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: + $$StatisticGroupTableTableReferences + ._groupIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._groupIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGroupTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + >; class $AppDatabaseManager { final _$AppDatabase _db; @@ -6293,4 +9131,12 @@ class $AppDatabaseManager { $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); $$ScoreEntryTableTableTableManager get scoreEntryTable => $$ScoreEntryTableTableTableManager(_db, _db.scoreEntryTable); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager(_db, _db.statisticTable); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager(_db, _db.statisticScopeTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager(_db, _db.statisticGameTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager(_db, _db.statisticGroupTable); } diff --git a/lib/data/db/tables/statistic_game_table.dart b/lib/data/db/tables/statistic_game_table.dart new file mode 100644 index 0000000..e1cc7d4 --- /dev/null +++ b/lib/data/db/tables/statistic_game_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGameTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get gameId => + text().references(GameTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, gameId}; +} diff --git a/lib/data/db/tables/statistic_group_table.dart b/lib/data/db/tables/statistic_group_table.dart new file mode 100644 index 0000000..cd642ad --- /dev/null +++ b/lib/data/db/tables/statistic_group_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGroupTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get groupId => + text().references(GroupTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, groupId}; +} diff --git a/lib/data/db/tables/statistic_scope_table.dart b/lib/data/db/tables/statistic_scope_table.dart new file mode 100644 index 0000000..3a9bcdc --- /dev/null +++ b/lib/data/db/tables/statistic_scope_table.dart @@ -0,0 +1,11 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticScopeTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get scope => text()(); + + @override + Set> get primaryKey => {statisticId, scope}; +} diff --git a/lib/data/db/tables/statistic_table.dart b/lib/data/db/tables/statistic_table.dart new file mode 100644 index 0000000..ef368a5 --- /dev/null +++ b/lib/data/db/tables/statistic_table.dart @@ -0,0 +1,11 @@ +import 'package:drift/drift.dart'; + +class StatisticTable extends Table { + TextColumn get id => text()(); + TextColumn get type => text()(); + TextColumn get timeframe => text().nullable()(); + IntColumn get displayCount => integer().withDefault(const Constant(5))(); + + @override + Set> get primaryKey => {id}; +} diff --git a/lib/data/models/game.dart b/lib/data/models/game.dart index 89bbd30..ec69204 100644 --- a/lib/data/models/game.dart +++ b/lib/data/models/game.dart @@ -8,13 +8,13 @@ class Game { final String name; final Ruleset ruleset; final String description; - final GameColor color; + final AppColor color; final String icon; Game({ required this.name, required this.ruleset, - this.color = GameColor.orange, + this.color = AppColor.orange, this.description = '', this.icon = '', String? id, @@ -33,7 +33,7 @@ class Game { String? name, Ruleset? ruleset, String? description, - GameColor? color, + AppColor? color, String? icon, }) { return Game( @@ -73,7 +73,7 @@ class Game { orElse: () => Ruleset.singleWinner, ), description = json['description'], - color = GameColor.values.firstWhere((e) => e.name == json['color']), + color = AppColor.values.firstWhere((e) => e.name == json['color']), icon = json['icon']; Map toJson() => { diff --git a/lib/data/models/group.dart b/lib/data/models/group.dart index 5c1515c..8b1fe92 100644 --- a/lib/data/models/group.dart +++ b/lib/data/models/group.dart @@ -5,17 +5,17 @@ import 'package:uuid/uuid.dart'; class Group { final String id; - final String name; - final String description; final DateTime createdAt; + final String name; final List members; + final String description; Group({ + required this.name, + required this.members, String? id, DateTime? createdAt, - required this.name, String? description, - required this.members, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? clock.now(), description = description ?? ''; diff --git a/lib/data/models/match.dart b/lib/data/models/match.dart index 2c43fe3..601a01c 100644 --- a/lib/data/models/match.dart +++ b/lib/data/models/match.dart @@ -107,7 +107,7 @@ class Match { name: '', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group = null, diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart new file mode 100644 index 0000000..e995d93 --- /dev/null +++ b/lib/data/models/statistic.dart @@ -0,0 +1,48 @@ +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:uuid/uuid.dart'; + +class Statistic { + final String id; + final StatisticType type; + final List scopes; + final Timeframe? timeframe; + final List? selectedGroups; + final List? selectedGames; + final int displayCount; + + Statistic({ + required this.type, + required this.scopes, + this.timeframe, + this.selectedGroups, + this.selectedGames, + this.displayCount = 5, + String? id, + }) : id = id ?? const Uuid().v4(); + + @override + String toString() { + return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)'; + } + + Statistic copyWith({ + StatisticType? type, + List? scopes, + Timeframe? timeframe, + List? selectedGroups, + List? selectedGames, + int? displayCount, + }) { + return Statistic( + id: id, + type: type ?? this.type, + scopes: scopes ?? this.scopes, + timeframe: timeframe ?? this.timeframe, + selectedGroups: selectedGroups ?? this.selectedGroups, + selectedGames: selectedGames ?? this.selectedGames, + displayCount: displayCount ?? this.displayCount, + ); + } +} diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index a1ed4af..7dc32f6 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -2,14 +2,18 @@ "@@locale": "de", "all_players": "Alle Spieler:innen", "all_players_selected": "Alle Spieler:innen ausgewählt", + "all_time": "Gesamter Zeitraum", "amount_of_matches": "Anzahl der Spiele", "app_name": "Tallee", + "average_score": "Durchschnittliche Punktzahl", "best_player": "Beste:r Spieler:in", + "best_score": "Beste Punktzahl", "cancel": "Abbrechen", "choose_color": "Farbe wählen", "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", + "classifier": "Klassifikator", "color": "Farbe", "color_blue": "Blau", "color_green": "Grün", @@ -19,12 +23,31 @@ "color_red": "Rot", "color_teal": "Türkis", "color_yellow": "Gelb", + "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Spielvorlage erstellen", "create_group": "Gruppe erstellen", "create_match": "Spiel erstellen", "create_new_group": "Neue Gruppe erstellen", "create_new_match": "Neues Spiel erstellen", + "create_statistic": "Statistik erstellen", + "create_statistic_classifier_subtitle": "Wähle die anzuzeigende Hauptmetrik aus", + "create_statistic_classifier_title": "Klassifikator", + "create_statistic_games_subtitle": "Wähle die gefilterten Spielvorlagen", + "create_statistic_games_title": "Spielvorlagen", + "create_statistic_groups_subtitle": "Wähle die gefilterten Gruppen", + "create_statistic_groups_title": "Gruppen", + "create_statistic_scope_subtitle": "Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.", + "create_statistic_scope_title": "Bereich", + "create_statistic_timeframe_subtitle": "Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.", + "create_statistic_timeframe_title": "Zeitraum", "created_on": "Erstellt am", "data": "Daten", "data_successfully_deleted": "Daten erfolgreich gelöscht", @@ -44,11 +67,15 @@ }, "delete_group": "Gruppe löschen", "delete_match": "Spiel löschen", + "delete_player": "Spieler:in löschen", "description": "Beschreibung", + "displayed_entries": "Angezeigte Einträge", "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_game": "Spielvorlage bearbeiten", "edit_group": "Gruppe bearbeiten", "edit_match": "Gruppe bearbeiten", + "edit_name": "Name ändern", + "edit_player": "Spieler bearbeiten", "enter_points": "Punkte eingeben", "enter_results": "Ergebnisse eintragen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", @@ -59,30 +86,42 @@ "exit_view": "Ansicht verlassen", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", + "filter": "Filter", "format_exception": "Formatfehler (siehe Konsole)", "game": "Spielvorlage", "game_name": "Spielvorlagenname", + "games": "Spielvorlagen", "group": "Gruppe", "group_name": "Gruppenname", "group_profile": "Gruppenprofil", "groups": "Gruppen", + "groups_part_of": "Gruppen Teil von", "highest_score": "Höchste Punkte", "home": "Startseite", "import_canceled": "Import abgebrochen", "import_data": "Daten importieren", "info": "Info", "invalid_schema": "Ungültiges Schema", + "last_180_days": "Letzte 180 Tage", + "last_30_days": "Letzte 30 Tage", + "last_7_days": "Letzte 7 Tage", + "last_90_days": "Letzte 90 Tage", + "last_year": "Letztes Jahr", "least_points": "Niedrigste Punkte", "legal": "Rechtliches", "legal_notice": "Impressum", "licenses": "Lizenzen", "live_edit_mode": "Live-Bearbeitungsmodus", + "loading": "Lädt...", "loser": "Verlierer:in", "lowest_score": "Niedrigste Punkte", "match_in_progress": "Spiel läuft...", "match_name": "Spieltitel", "match_profile": "Spielprofil", "matches": "Spiele", + "matches_part_of": "Spiele Teil von", + "matches_played": "Spiele gespielt", + "matches_won": "Spiele gewonnen", "members": "Mitglieder", "most_points": "Höchste Punkte", "multiple_winners": "Mehrere Gewinner:innen", @@ -92,6 +131,7 @@ "no_license_text_available": "Kein Lizenztext verfügbar", "no_licenses_found": "Keine Lizenzen gefunden", "no_matches_created_yet": "Noch keine Spiele erstellt", + "no_matches_played_yet": "Noch kein Spiel gespielt", "no_players_created_yet": "Noch keine Spieler:in erstellt", "no_players_found_with_that_name": "Keine Spieler:in mit diesem Namen gefunden", "no_players_selected": "Keine Spieler:innen ausgewählt", @@ -99,13 +139,16 @@ "no_results_entered_yet": "Noch keine Ergebnisse eingetragen", "no_second_match_available": "Kein zweites Spiel verfügbar", "no_statistics_available": "Keine Statistiken verfügbar", + "no_statistics_created_yet": "Noch keine Statistiken erstellt", "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", + "not_part_of_any_group": "Noch keiner Gruppe hinzugefügt", "place": "Platz", "placement": "Platzierung", "played_matches": "Gespielte Spiele", "player_name": "Spieler:innenname", + "player_profile": "Spieler:in-Profil", "players": "Spieler:innen", "point": "Punkt", "points": "Punkte", @@ -121,15 +164,36 @@ "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.", "save_changes": "Änderungen speichern", + "scope": "Bereich", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", + "select_a_classifier": "Klassifikator auswählen", + "select_a_game": "Spielvorlage auswählen", + "select_a_group": "Gruppe auswählen", + "select_a_scope": "Bereich auswählen", + "select_a_timeframe": "Zeitraum auswählen", + "select_a_timeframe_for_which_data_will_be_filtered": "Wähle einen Zeitraum, für den die Daten gefiltert werden sollen", "select_loser": "Verlierer:in wählen", + "select_the_filtered_games": "Wähle Spiele, nach denen gefiltert werden soll.", + "select_the_filtered_groups": "Wähle Gruppen, nach denen gefiltert werden soll.", + "select_the_filtered_timeframe": "Wähle einen Zeitraum, nach dem gefiltert werden soll.", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", + "selected_games": "Ausgewählte Spielvorlagen", + "selected_groups": "Ausgewählte Gruppen", "selected_players": "Ausgewählte Spieler:innen", + "set_name": "Name setzen", "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", + "statistic_type_average_score": "Durchschnittliche Punktzahl", + "statistic_type_best_score": "Beste Punktzahl", + "statistic_type_total_losses": "Niederlagen insgesamt", + "statistic_type_total_matches": "Spiele insgesamt", + "statistic_type_total_score": "Punktzahl insgesamt", + "statistic_type_total_wins": "Siege insgesamt", + "statistic_type_winrate": "Siegquote", + "statistic_type_worst_score": "Schlechteste Punktzahl", "statistics": "Statistiken", "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", @@ -137,12 +201,18 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", + "timeframe": "Zeitraum", "today_at": "Heute um", + "total_losses": "Niederlagen insgesamt", + "total_matches": "Spiele insgesamt", + "total_score": "Punktzahl insgesamt", + "total_wins": "Siege insgesamt", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", "winner": "Gewinner:in", "winners": "Gewinner:innen", "winrate": "Siegquote", "wins": "Siege", + "worst_score": "Schlechteste Punktzahl", "yesterday_at": "Gestern um" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 5ae94cd..988433b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -18,13 +18,30 @@ "color_purple": "Purple", "color_red": "Red", "color_teal": "Teal", + "displayed_entries": "Displayed entries", "color_yellow": "Yellow", - "could_not_add_player": "Could not add player", + "confirm": "Confirm", + "could_not_add_player": "Could not add player {playerName}", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Create Game", "create_group": "Create Group", "create_match": "Create match", "create_new_group": "Create new group", "create_new_match": "Create new match", + "create_statistic": "Create statistic", + "classifier": "Classifier", + "select_the_filtered_timeframe": "Select the timeframe you want to filter by.", + "select_the_filtered_games": "Select the games you want to filter by.", + "games": "Games", + "select_the_filtered_groups": "Select the groups you want to filter by.", + "scope": "Scope", + "timeframe": "Timeframe", "created_on": "Created on", "data": "Data", "data_successfully_deleted": "Data successfully deleted", @@ -42,13 +59,17 @@ } } }, + "filter": "Filter", "delete_group": "Delete Group", "delete_match": "Delete Match", + "delete_player": "Delete player?", "description": "Description", "drag_to_set_placement": "Drag to set placement", "edit_game": "Edit Game", "edit_group": "Edit Group", "edit_match": "Edit Match", + "edit_name": "Edit name", + "edit_player": "Edit player", "enter_points": "Enter points", "enter_results": "Enter Results", "error_creating_group": "Error while creating group, please try again", @@ -66,6 +87,7 @@ "group_name": "Group name", "group_profile": "Group Profile", "groups": "Groups", + "groups_part_of": "Groups part of", "highest_score": "Highest Score", "home": "Home", "import_canceled": "Import canceled", @@ -77,12 +99,16 @@ "legal_notice": "Legal Notice", "licenses": "Licenses", "live_edit_mode": "Live Edit Mode", + "loading": "Loading...", "loser": "Loser", "lowest_score": "Lowest Score", "match_in_progress": "Match in progress...", "match_name": "Match name", "match_profile": "Match Profile", "matches": "Matches", + "matches_part_of": "Matches part of", + "matches_played": "Matches played", + "matches_won": "Matches won", "members": "Members", "most_points": "Most Points", "multiple_winners": "Multiple Winners", @@ -92,6 +118,7 @@ "no_license_text_available": "No license text available", "no_licenses_found": "No licenses found", "no_matches_created_yet": "No matches created yet", + "no_matches_played_yet": "No games played yet", "no_players_created_yet": "No players created yet", "no_players_found_with_that_name": "No players found with that name", "no_players_selected": "No players selected", @@ -99,13 +126,16 @@ "no_results_entered_yet": "No results entered yet", "no_second_match_available": "No second match available", "no_statistics_available": "No statistics available", + "no_statistics_created_yet": "No statistics created yet", "none": "None", "none_group": "None", "not_available": "Not available", + "not_part_of_any_group": "Not part of any group yet", "place": "place", "placement": "Placement", "played_matches": "Played Matches", "player_name": "Player name", + "player_profile": "Player Profile", "players": "Players", "point": "Point", "points": "Points", @@ -126,11 +156,26 @@ "select_winner": "Select Winner", "select_winners": "Select Winners", "selected_players": "Selected players", + "set_name": "Set name", "settings": "Settings", + "select_a_classifier": "Select a classifier", + "select_a_game": "Select a game", + "select_a_group": "Select a group", + "select_a_scope": "Select a scope", + "select_a_timeframe": "Select a timeframe", "single_loser": "Single Loser", "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", + "selected_games": "Selected games", + "selected_groups": "Selected groups", + "average_score": "Average score", + "best_score": "Best score", + "total_losses": "Total losses", + "total_matches": "Total matches", + "total_score": "Total score", + "total_wins": "Total wins", + "worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -145,6 +190,12 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", + "all_time": "All time", + "last_180_days": "Last 180 days", + "last_30_days": "Last 30 days", + "last_7_days": "Last 7 days", + "last_90_days": "Last 90 days", + "last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index dd538d5..30e1b33 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -206,17 +206,29 @@ abstract class AppLocalizations { /// **'Teal'** String get color_teal; + /// No description provided for @displayed_entries. + /// + /// In en, this message translates to: + /// **'Displayed entries'** + String get displayed_entries; + /// No description provided for @color_yellow. /// /// In en, this message translates to: /// **'Yellow'** String get color_yellow; + /// No description provided for @confirm. + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + /// No description provided for @could_not_add_player. /// /// In en, this message translates to: - /// **'Could not add player'** - String could_not_add_player(Object playerName); + /// **'Could not add player {playerName}'** + String could_not_add_player(String playerName); /// No description provided for @create_game. /// @@ -248,6 +260,54 @@ abstract class AppLocalizations { /// **'Create new match'** String get create_new_match; + /// No description provided for @create_statistic. + /// + /// In en, this message translates to: + /// **'Create statistic'** + String get create_statistic; + + /// No description provided for @classifier. + /// + /// In en, this message translates to: + /// **'Classifier'** + String get classifier; + + /// No description provided for @select_the_filtered_timeframe. + /// + /// In en, this message translates to: + /// **'Select the timeframe you want to filter by.'** + String get select_the_filtered_timeframe; + + /// No description provided for @select_the_filtered_games. + /// + /// In en, this message translates to: + /// **'Select the games you want to filter by.'** + String get select_the_filtered_games; + + /// No description provided for @games. + /// + /// In en, this message translates to: + /// **'Games'** + String get games; + + /// No description provided for @select_the_filtered_groups. + /// + /// In en, this message translates to: + /// **'Select the groups you want to filter by.'** + String get select_the_filtered_groups; + + /// No description provided for @scope. + /// + /// In en, this message translates to: + /// **'Scope'** + String get scope; + + /// No description provided for @timeframe. + /// + /// In en, this message translates to: + /// **'Timeframe'** + String get timeframe; + /// No description provided for @created_on. /// /// In en, this message translates to: @@ -308,6 +368,12 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); + /// No description provided for @filter. + /// + /// In en, this message translates to: + /// **'Filter'** + String get filter; + /// No description provided for @delete_group. /// /// In en, this message translates to: @@ -320,6 +386,12 @@ abstract class AppLocalizations { /// **'Delete Match'** String get delete_match; + /// No description provided for @delete_player. + /// + /// In en, this message translates to: + /// **'Delete player?'** + String get delete_player; + /// No description provided for @description. /// /// In en, this message translates to: @@ -350,6 +422,18 @@ abstract class AppLocalizations { /// **'Edit Match'** String get edit_match; + /// No description provided for @edit_name. + /// + /// In en, this message translates to: + /// **'Edit name'** + String get edit_name; + + /// No description provided for @edit_player. + /// + /// In en, this message translates to: + /// **'Edit player'** + String get edit_player; + /// No description provided for @enter_points. /// /// In en, this message translates to: @@ -452,6 +536,12 @@ abstract class AppLocalizations { /// **'Groups'** String get groups; + /// No description provided for @groups_part_of. + /// + /// In en, this message translates to: + /// **'Groups part of'** + String get groups_part_of; + /// No description provided for @highest_score. /// /// In en, this message translates to: @@ -518,6 +608,12 @@ abstract class AppLocalizations { /// **'Live Edit Mode'** String get live_edit_mode; + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loading; + /// No description provided for @loser. /// /// In en, this message translates to: @@ -554,6 +650,24 @@ abstract class AppLocalizations { /// **'Matches'** String get matches; + /// No description provided for @matches_part_of. + /// + /// In en, this message translates to: + /// **'Matches part of'** + String get matches_part_of; + + /// No description provided for @matches_played. + /// + /// In en, this message translates to: + /// **'Matches played'** + String get matches_played; + + /// No description provided for @matches_won. + /// + /// In en, this message translates to: + /// **'Matches won'** + String get matches_won; + /// No description provided for @members. /// /// In en, this message translates to: @@ -608,6 +722,12 @@ abstract class AppLocalizations { /// **'No matches created yet'** String get no_matches_created_yet; + /// No description provided for @no_matches_played_yet. + /// + /// In en, this message translates to: + /// **'No games played yet'** + String get no_matches_played_yet; + /// No description provided for @no_players_created_yet. /// /// In en, this message translates to: @@ -650,6 +770,12 @@ abstract class AppLocalizations { /// **'No statistics available'** String get no_statistics_available; + /// No description provided for @no_statistics_created_yet. + /// + /// In en, this message translates to: + /// **'No statistics created yet'** + String get no_statistics_created_yet; + /// No description provided for @none. /// /// In en, this message translates to: @@ -668,6 +794,12 @@ abstract class AppLocalizations { /// **'Not available'** String get not_available; + /// No description provided for @not_part_of_any_group. + /// + /// In en, this message translates to: + /// **'Not part of any group yet'** + String get not_part_of_any_group; + /// No description provided for @place. /// /// In en, this message translates to: @@ -692,6 +824,12 @@ abstract class AppLocalizations { /// **'Player name'** String get player_name; + /// No description provided for @player_profile. + /// + /// In en, this message translates to: + /// **'Player Profile'** + String get player_profile; + /// No description provided for @players. /// /// In en, this message translates to: @@ -812,12 +950,48 @@ abstract class AppLocalizations { /// **'Selected players'** String get selected_players; + /// No description provided for @set_name. + /// + /// In en, this message translates to: + /// **'Set name'** + String get set_name; + /// No description provided for @settings. /// /// In en, this message translates to: /// **'Settings'** String get settings; + /// No description provided for @select_a_classifier. + /// + /// In en, this message translates to: + /// **'Select a classifier'** + String get select_a_classifier; + + /// No description provided for @select_a_game. + /// + /// In en, this message translates to: + /// **'Select a game'** + String get select_a_game; + + /// No description provided for @select_a_group. + /// + /// In en, this message translates to: + /// **'Select a group'** + String get select_a_group; + + /// No description provided for @select_a_scope. + /// + /// In en, this message translates to: + /// **'Select a scope'** + String get select_a_scope; + + /// No description provided for @select_a_timeframe. + /// + /// In en, this message translates to: + /// **'Select a timeframe'** + String get select_a_timeframe; + /// No description provided for @single_loser. /// /// In en, this message translates to: @@ -842,6 +1016,60 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; + /// No description provided for @selected_games. + /// + /// In en, this message translates to: + /// **'Selected games'** + String get selected_games; + + /// No description provided for @selected_groups. + /// + /// In en, this message translates to: + /// **'Selected groups'** + String get selected_groups; + + /// No description provided for @average_score. + /// + /// In en, this message translates to: + /// **'Average score'** + String get average_score; + + /// No description provided for @best_score. + /// + /// In en, this message translates to: + /// **'Best score'** + String get best_score; + + /// No description provided for @total_losses. + /// + /// In en, this message translates to: + /// **'Total losses'** + String get total_losses; + + /// No description provided for @total_matches. + /// + /// In en, this message translates to: + /// **'Total matches'** + String get total_matches; + + /// No description provided for @total_score. + /// + /// In en, this message translates to: + /// **'Total score'** + String get total_score; + + /// No description provided for @total_wins. + /// + /// In en, this message translates to: + /// **'Total wins'** + String get total_wins; + + /// No description provided for @worst_score. + /// + /// In en, this message translates to: + /// **'Worst score'** + String get worst_score; + /// Success message when adding a player /// /// In en, this message translates to: @@ -872,6 +1100,42 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; + /// No description provided for @all_time. + /// + /// In en, this message translates to: + /// **'All time'** + String get all_time; + + /// No description provided for @last_180_days. + /// + /// In en, this message translates to: + /// **'Last 180 days'** + String get last_180_days; + + /// No description provided for @last_30_days. + /// + /// In en, this message translates to: + /// **'Last 30 days'** + String get last_30_days; + + /// No description provided for @last_7_days. + /// + /// In en, this message translates to: + /// **'Last 7 days'** + String get last_7_days; + + /// No description provided for @last_90_days. + /// + /// In en, this message translates to: + /// **'Last 90 days'** + String get last_90_days; + + /// No description provided for @last_year. + /// + /// In en, this message translates to: + /// **'Last year'** + String get last_year; + /// No description provided for @today_at. /// /// 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 7c5177a..1417bb7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -62,11 +62,17 @@ class AppLocalizationsDe extends AppLocalizations { @override String get color_teal => 'Türkis'; + @override + String get displayed_entries => 'Angezeigte Einträge'; + @override String get color_yellow => 'Gelb'; @override - String could_not_add_player(Object playerName) { + String get confirm => 'Bestätigen'; + + @override + String could_not_add_player(String playerName) { return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; } @@ -85,6 +91,33 @@ class AppLocalizationsDe extends AppLocalizations { @override String get create_new_match => 'Neues Spiel erstellen'; + @override + String get create_statistic => 'Statistik erstellen'; + + @override + String get classifier => 'Klassifikator'; + + @override + String get select_the_filtered_timeframe => + 'Wähle einen Zeitraum, nach dem gefiltert werden soll.'; + + @override + String get select_the_filtered_games => + 'Wähle Spiele, nach denen gefiltert werden soll.'; + + @override + String get games => 'Spielvorlagen'; + + @override + String get select_the_filtered_groups => + 'Wähle Gruppen, nach denen gefiltert werden soll.'; + + @override + String get scope => 'Bereich'; + + @override + String get timeframe => 'Zeitraum'; + @override String get created_on => 'Erstellt am'; @@ -125,12 +158,18 @@ class AppLocalizationsDe extends AppLocalizations { return 'Wenn du diese Spielvorlage löschst, $_temp0 mit dieser Spielvorlage ebenfalls gelöscht.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Gruppe löschen'; @override String get delete_match => 'Spiel löschen'; + @override + String get delete_player => 'Spieler:in löschen'; + @override String get description => 'Beschreibung'; @@ -146,6 +185,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get edit_match => 'Gruppe bearbeiten'; + @override + String get edit_name => 'Name ändern'; + + @override + String get edit_player => 'Spieler bearbeiten'; + @override String get enter_points => 'Punkte eingeben'; @@ -201,6 +246,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get groups => 'Gruppen'; + @override + String get groups_part_of => 'Gruppen Teil von'; + @override String get highest_score => 'Höchste Punkte'; @@ -234,6 +282,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get live_edit_mode => 'Live-Bearbeitungsmodus'; + @override + String get loading => 'Lädt...'; + @override String get loser => 'Verlierer:in'; @@ -252,6 +303,15 @@ class AppLocalizationsDe extends AppLocalizations { @override String get matches => 'Spiele'; + @override + String get matches_part_of => 'Spiele Teil von'; + + @override + String get matches_played => 'Spiele gespielt'; + + @override + String get matches_won => 'Spiele gewonnen'; + @override String get members => 'Mitglieder'; @@ -279,6 +339,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_matches_created_yet => 'Noch keine Spiele erstellt'; + @override + String get no_matches_played_yet => 'Noch kein Spiel gespielt'; + @override String get no_players_created_yet => 'Noch keine Spieler:in erstellt'; @@ -301,6 +364,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_statistics_available => 'Keine Statistiken verfügbar'; + @override + String get no_statistics_created_yet => 'Noch keine Statistiken erstellt'; + @override String get none => 'Kein'; @@ -310,6 +376,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get not_available => 'Nicht verfügbar'; + @override + String get not_part_of_any_group => 'Noch keiner Gruppe hinzugefügt'; + @override String get place => 'Platz'; @@ -322,6 +391,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get player_name => 'Spieler:innenname'; + @override + String get player_profile => 'Spieler:in-Profil'; + @override String get players => 'Spieler:innen'; @@ -387,9 +459,27 @@ class AppLocalizationsDe extends AppLocalizations { @override String get selected_players => 'Ausgewählte Spieler:innen'; + @override + String get set_name => 'Name setzen'; + @override String get settings => 'Einstellungen'; + @override + String get select_a_classifier => 'Klassifikator auswählen'; + + @override + String get select_a_game => 'Spielvorlage auswählen'; + + @override + String get select_a_group => 'Gruppe auswählen'; + + @override + String get select_a_scope => 'Bereich auswählen'; + + @override + String get select_a_timeframe => 'Zeitraum auswählen'; + @override String get single_loser => 'Ein:e Verlierer:in'; @@ -402,6 +492,33 @@ class AppLocalizationsDe extends AppLocalizations { @override String get stats => 'Statistiken'; + @override + String get selected_games => 'Ausgewählte Spielvorlagen'; + + @override + String get selected_groups => 'Ausgewählte Gruppen'; + + @override + String get average_score => 'Durchschnittliche Punktzahl'; + + @override + String get best_score => 'Beste Punktzahl'; + + @override + String get total_losses => 'Niederlagen insgesamt'; + + @override + String get total_matches => 'Spiele insgesamt'; + + @override + String get total_score => 'Punktzahl insgesamt'; + + @override + String get total_wins => 'Siege insgesamt'; + + @override + String get worst_score => 'Schlechteste Punktzahl'; + @override String successfully_added_player(String playerName) { return 'Spieler:in $playerName erfolgreich hinzugefügt'; @@ -422,6 +539,24 @@ class AppLocalizationsDe extends AppLocalizations { @override String get tie => 'Unentschieden'; + @override + String get all_time => 'Gesamter Zeitraum'; + + @override + String get last_180_days => 'Letzte 180 Tage'; + + @override + String get last_30_days => 'Letzte 30 Tage'; + + @override + String get last_7_days => 'Letzte 7 Tage'; + + @override + String get last_90_days => 'Letzte 90 Tage'; + + @override + String get last_year => 'Letztes Jahr'; + @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index bc083e5..2275b97 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -62,12 +62,18 @@ class AppLocalizationsEn extends AppLocalizations { @override String get color_teal => 'Teal'; + @override + String get displayed_entries => 'Displayed entries'; + @override String get color_yellow => 'Yellow'; @override - String could_not_add_player(Object playerName) { - return 'Could not add player'; + String get confirm => 'Confirm'; + + @override + String could_not_add_player(String playerName) { + return 'Could not add player $playerName'; } @override @@ -85,6 +91,33 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_new_match => 'Create new match'; + @override + String get create_statistic => 'Create statistic'; + + @override + String get classifier => 'Classifier'; + + @override + String get select_the_filtered_timeframe => + 'Select the timeframe you want to filter by.'; + + @override + String get select_the_filtered_games => + 'Select the games you want to filter by.'; + + @override + String get games => 'Games'; + + @override + String get select_the_filtered_groups => + 'Select the groups you want to filter by.'; + + @override + String get scope => 'Scope'; + + @override + String get timeframe => 'Timeframe'; + @override String get created_on => 'Created on'; @@ -125,12 +158,18 @@ class AppLocalizationsEn extends AppLocalizations { return 'If you delete this game template, $_temp0 using this game template will also be deleted.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Delete Group'; @override String get delete_match => 'Delete Match'; + @override + String get delete_player => 'Delete player?'; + @override String get description => 'Description'; @@ -146,6 +185,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get edit_match => 'Edit Match'; + @override + String get edit_name => 'Edit name'; + + @override + String get edit_player => 'Edit player'; + @override String get enter_points => 'Enter points'; @@ -201,6 +246,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get groups => 'Groups'; + @override + String get groups_part_of => 'Groups part of'; + @override String get highest_score => 'Highest Score'; @@ -234,6 +282,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get live_edit_mode => 'Live Edit Mode'; + @override + String get loading => 'Loading...'; + @override String get loser => 'Loser'; @@ -252,6 +303,15 @@ class AppLocalizationsEn extends AppLocalizations { @override String get matches => 'Matches'; + @override + String get matches_part_of => 'Matches part of'; + + @override + String get matches_played => 'Matches played'; + + @override + String get matches_won => 'Matches won'; + @override String get members => 'Members'; @@ -279,6 +339,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_matches_created_yet => 'No matches created yet'; + @override + String get no_matches_played_yet => 'No games played yet'; + @override String get no_players_created_yet => 'No players created yet'; @@ -301,6 +364,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_statistics_available => 'No statistics available'; + @override + String get no_statistics_created_yet => 'No statistics created yet'; + @override String get none => 'None'; @@ -310,6 +376,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get not_available => 'Not available'; + @override + String get not_part_of_any_group => 'Not part of any group yet'; + @override String get place => 'place'; @@ -322,6 +391,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get player_name => 'Player name'; + @override + String get player_profile => 'Player Profile'; + @override String get players => 'Players'; @@ -387,9 +459,27 @@ class AppLocalizationsEn extends AppLocalizations { @override String get selected_players => 'Selected players'; + @override + String get set_name => 'Set name'; + @override String get settings => 'Settings'; + @override + String get select_a_classifier => 'Select a classifier'; + + @override + String get select_a_game => 'Select a game'; + + @override + String get select_a_group => 'Select a group'; + + @override + String get select_a_scope => 'Select a scope'; + + @override + String get select_a_timeframe => 'Select a timeframe'; + @override String get single_loser => 'Single Loser'; @@ -402,6 +492,33 @@ class AppLocalizationsEn extends AppLocalizations { @override String get stats => 'Stats'; + @override + String get selected_games => 'Selected games'; + + @override + String get selected_groups => 'Selected groups'; + + @override + String get average_score => 'Average score'; + + @override + String get best_score => 'Best score'; + + @override + String get total_losses => 'Total losses'; + + @override + String get total_matches => 'Total matches'; + + @override + String get total_score => 'Total score'; + + @override + String get total_wins => 'Total wins'; + + @override + String get worst_score => 'Worst score'; + @override String successfully_added_player(String playerName) { return 'Successfully added player $playerName'; @@ -421,6 +538,24 @@ class AppLocalizationsEn extends AppLocalizations { @override String get tie => 'Tie'; + @override + String get all_time => 'All time'; + + @override + String get last_180_days => 'Last 180 days'; + + @override + String get last_30_days => 'Last 30 days'; + + @override + String get last_7_days => 'Last 7 days'; + + @override + String get last_90_days => 'Last 90 days'; + + @override + String get last_year => 'Last year'; + @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 7e5434b..07d66b4 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -6,7 +6,7 @@ import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistics_view.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart'; diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index 84efbe1..72e4c69 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -89,6 +89,7 @@ class _CreateGroupViewState extends State { Expanded( child: PlayerSelection( initialSelectedPlayers: initialSelectedPlayers, + onPlayerCreated: () => widget.onMembersChanged?.call(), onChanged: (value) { setState(() { selectedPlayers = [...value]; @@ -134,6 +135,7 @@ class _CreateGroupViewState extends State { if (!mounted) return; if (success) { + widget.onMembersChanged?.call(); await HapticFeedback.successNotification(); if (mounted) { Navigator.pop(context, updatedGroup); @@ -157,7 +159,6 @@ class _CreateGroupViewState extends State { final success = await db.groupDao.addGroup( group: Group(name: groupName, members: selectedPlayers), ); - return success; } diff --git a/lib/presentation/views/main_menu/group_view/group_view.dart b/lib/presentation/views/main_menu/group_view/group_view.dart index c8a9398..e53b661 100644 --- a/lib/presentation/views/main_menu/group_view/group_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_view.dart @@ -77,6 +77,7 @@ class _GroupViewState extends State { ); } return GroupTile( + onPlayerChanged: loadGroups, group: groups[index], onTap: () async { await Navigator.push( @@ -106,13 +107,10 @@ class _GroupViewState extends State { context, adaptivePageRoute( builder: (context) { - return const CreateGroupView(); + return CreateGroupView(onMembersChanged: loadGroups); }, ), ); - setState(() { - loadGroups(); - }); }, ), ), diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 3c51cab..4d085d7 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -164,7 +164,7 @@ class _ChooseGameViewState extends State { game.ruleset, context, ), - badgeColor: getColorFromGameColor(game.color), + badgeColor: getColorFromAppColor(game.color), isHighlighted: selectedGameId == game.id, onTap: () async { setState(() { diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 998f4e1..0671055 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -49,10 +49,10 @@ class _CreateGameViewState extends State { late final AppDatabase db; late List<(Ruleset, String)> _rulesets; - late List<(GameColor, String)> _colors; + late List<(AppColor, String)> _colors; Ruleset? selectedRuleset = Ruleset.singleWinner; - GameColor? selectedColor = GameColor.orange; + AppColor? selectedColor = AppColor.orange; /// Controller for the game name input field. final _gameNameController = TextEditingController(); @@ -87,10 +87,10 @@ class _CreateGameViewState extends State { ), ); _colors = List.generate( - GameColor.values.length, + AppColor.values.length, (index) => ( - GameColor.values[index], - translateGameColorToString(GameColor.values[index], context), + AppColor.values[index], + translateAppColorToString(AppColor.values[index], context), ), ); @@ -117,7 +117,6 @@ class _CreateGameViewState extends State { return ScaffoldMessenger( child: Scaffold( - backgroundColor: CustomTheme.backgroundColor, appBar: AppBar( title: Text(isEditing ? loc.edit_game : loc.create_game), actions: [ @@ -468,7 +467,7 @@ class _CreateGameViewState extends State { height: 16, margin: const EdgeInsets.only(left: 12), decoration: BoxDecoration( - color: getColorFromGameColor( + color: getColorFromAppColor( _colors[index].$1, ), shape: BoxShape.circle, @@ -502,13 +501,13 @@ class _CreateGameViewState extends State { width: 16, height: 16, decoration: BoxDecoration( - color: getColorFromGameColor(selectedColor!), + color: getColorFromAppColor(selectedColor!), shape: BoxShape.circle, ), ), Padding( padding: const EdgeInsets.only(right: 5), - child: Text(translateGameColorToString(selectedColor!, context)), + child: Text(translateAppColorToString(selectedColor!, context)), ), Transform.rotate( angle: pi / 2, diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index 85bb936..c8790be 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -196,6 +196,7 @@ class _CreateMatchViewState extends State { child: PlayerSelection( key: ValueKey(selectedGroup?.id ?? 'no_group'), initialSelectedPlayers: selectedPlayers, + onPlayerCreated: () => widget.onMatchesUpdated?.call(), onChanged: (value) { setState(() { selectedPlayers = value; diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index a7f60c6..1d30afb 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -39,7 +39,7 @@ class _MatchViewState extends State { game: Game( name: 'Game name', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group: Group( @@ -79,7 +79,7 @@ class _MatchViewState extends State { visible: matches.isNotEmpty, replacement: Center( child: TopCenteredMessage( - icon: Icons.report, + icon: Icons.info, title: loc.info, message: loc.no_matches_created_yet, ), @@ -97,6 +97,7 @@ class _MatchViewState extends State { child: Padding( padding: const EdgeInsets.only(bottom: 12.0), child: MatchTile( + onPlayerEdited: loadMatches, width: MediaQuery.sizeOf(context).width * 0.95, onTap: () async { Navigator.push( diff --git a/lib/presentation/views/main_menu/player_detail_view.dart b/lib/presentation/views/main_menu/player_detail_view.dart new file mode 100644 index 0000000..e26c327 --- /dev/null +++ b/lib/presentation/views/main_menu/player_detail_view.dart @@ -0,0 +1,394 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/app_skeleton.dart'; +import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; +import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; +import 'package:tallee/presentation/widgets/colored_icon_container.dart'; +import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; +import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; +import 'package:tallee/presentation/widgets/text_input/text_input_field.dart'; +import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; + +class PlayerDetailView extends StatefulWidget { + const PlayerDetailView({ + super.key, + required this.player, + required this.callback, + }); + + /// The player to display + final Player player; + + final VoidCallback callback; + + @override + State createState() => _PlayerDetailViewState(); +} + +class _PlayerDetailViewState extends State { + late final AppDatabase db; + late Player _player; + late String playerNameCount; + bool isLoading = true; + + /// Total matches played by this player + int totalMatches = 0; + + /// Total matches won by this player + int matchesWon = 0; + + /// Total groups this player belongs to + int totalGroups = 0; + + /// Full list of groups this player belongs to + List playerGroups = List.filled( + 4, + Group(name: 'Skeleton group', members: []), + ); + + /// Full list of matches this player played in + List playerMatches = List.filled( + 4, + Match( + name: 'Skeleton match', + game: Game(name: 'Game name', ruleset: Ruleset.singleWinner), + players: [], + ), + ); + + TextEditingController nameController = TextEditingController(); + + @override + void initState() { + super.initState(); + _player = widget.player; + db = Provider.of(context, listen: false); + playerNameCount = getNameCountText(_player); + _loadData(); + } + + @override + void dispose() { + nameController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + + return Scaffold( + appBar: AppBar( + title: Text(loc.player_profile), + actions: [ + HapticIconButton( + icon: const Icon(Icons.delete), + onPressed: () async { + showDialog( + context: context, + builder: (context) => CustomAlertDialog( + title: loc.delete_player, + content: Text(loc.this_cannot_be_undone), + actions: [ + CustomDialogAction( + onPressed: () => Navigator.of(context).pop(true), + text: loc.delete, + ), + CustomDialogAction( + onPressed: () => Navigator.of(context).pop(false), + buttonType: ButtonType.secondary, + text: loc.cancel, + ), + ], + ), + ).then((confirmed) async { + if (confirmed! && context.mounted) { + //TODO: implement player deletion in db + if (!context.mounted) return; + Navigator.pop(context); + widget.callback(); + } + }); + }, + ), + ], + ), + body: SafeArea( + child: Stack( + alignment: Alignment.center, + children: [ + ListView( + padding: const EdgeInsets.only( + left: 12, + right: 12, + top: 20, + bottom: 100, + ), + children: [ + const Center( + child: ColoredIconContainer( + icon: Icons.person, + containerSize: 55, + iconSize: 38, + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _player.name, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor, + ), + textAlign: TextAlign.center, + ), + Text( + playerNameCount, + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor.withAlpha(120), + ), + textAlign: TextAlign.center, + ), + ], + ), + const SizedBox(height: 5), + Text( + '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_player.createdAt)}', + style: const TextStyle( + fontSize: 12, + color: CustomTheme.textColor, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + InfoTile( + title: '${loc.matches_part_of} ($totalMatches)', + icon: Icons.sports_esports, + horizontalAlignment: CrossAxisAlignment.start, + content: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + alignment: Alignment.topLeft, + child: playerMatches.isNotEmpty + ? Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, + spacing: 12, + runSpacing: 8, + children: playerMatches.map((match) { + return TextIconTile( + text: match.name, + iconEnabled: false, + ); + }).toList(), + ) + : Text( + loc.no_matches_played_yet, + style: const TextStyle( + fontSize: 14, + color: CustomTheme.textColor, + ), + ), + ), + ), + const SizedBox(height: 15), + InfoTile( + title: '${loc.groups_part_of} ($totalGroups)', + icon: Icons.people, + horizontalAlignment: CrossAxisAlignment.start, + content: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + alignment: Alignment.topLeft, + child: playerGroups.isNotEmpty + ? Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, + spacing: 12, + runSpacing: 8, + children: playerGroups.map((group) { + return TextIconTile( + text: group.name, + iconEnabled: false, + ); + }).toList(), + ) + : Text( + loc.not_part_of_any_group, + style: const TextStyle( + fontSize: 14, + color: CustomTheme.textColor, + ), + ), + ), + ), + const SizedBox(height: 15), + InfoTile( + title: loc.statistics, + icon: Icons.bar_chart, + content: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: Column( + children: [ + _buildStatRow( + loc.matches_played, + totalMatches.toString(), + ), + _buildStatRow(loc.matches_won, matchesWon.toString()), + _buildStatRow( + loc.winrate, + '${totalMatches == 0 ? 0 : ((matchesWon / totalMatches) * 100).round()}%', + ), + ], + ), + ), + ), + ], + ), + Positioned( + bottom: MediaQuery.paddingOf(context).bottom, + child: MainMenuButton( + text: loc.edit_player, + icon: Icons.edit, + onPressed: () async { + nameController.text = _player.name; + showDialog( + context: context, + builder: (context) => StatefulBuilder( + builder: (context, setDialogState) { + return CustomAlertDialog( + title: loc.edit_name, + content: TextInputField( + controller: nameController, + hintText: loc.set_name, + onChanged: (_) => setDialogState(() {}), + ), + actions: [ + CustomDialogAction( + onPressed: isConfirmButtonEnabled() + ? () => Navigator.of(context).pop(true) + : null, + text: loc.confirm, + ), + CustomDialogAction( + onPressed: () => Navigator.of(context).pop(false), + buttonType: ButtonType.secondary, + text: loc.cancel, + ), + ], + ); + }, + ), + ).then((confirmed) async { + if (confirmed! && context.mounted) { + final newName = nameController.text.trim(); + + if (newName != _player.name) { + final fetchedPlayerNameCount = await db.playerDao + .getNameCount(name: newName); + await db.playerDao.updatePlayerName( + playerId: _player.id, + name: newName, + ); + widget.callback.call(); + setState(() { + _player = Player( + name: newName, + createdAt: _player.createdAt, + id: _player.id, + nameCount: _player.nameCount, + description: _player.description, + ); + + // If there is already a player with the same name, + // the count of that player is 0, so we start counting from 2 to get the correct count for this player. If there are no players with the same name, we just show the name without a count. + playerNameCount = fetchedPlayerNameCount == 0 + ? '' + : ' #${fetchedPlayerNameCount + 1}'; + }); + } + } + }); + }, + ), + ), + ], + ), + ), + ); + } + + /// Loads statistics for this player + Future _loadData() async { + isLoading = true; + final fetchedMatches = await db.matchDao.getMatchesByPlayer( + playerId: _player.id, + ); + final fetchedGroups = await db.groupDao.getGroupsByPlayer( + playerId: _player.id, + ); + + if (!mounted) return; + + setState(() { + playerMatches = fetchedMatches; + totalMatches = fetchedMatches.length; + matchesWon = fetchedMatches + .where((match) => match.mvp.any((mvp) => mvp.id == _player.id)) + .length; + playerGroups = fetchedGroups; + totalGroups = fetchedGroups.length; + isLoading = false; + }); + } + + /// Builds a single statistic row with a label and value + /// - [label]: The label of the statistic + /// - [value]: The value of the statistic + Widget _buildStatRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Text( + label, + style: const TextStyle( + fontSize: 16, + color: CustomTheme.textColor, + ), + ), + ], + ), + Text( + value, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ], + ), + ); + } + + bool isConfirmButtonEnabled() { + return nameController.text.trim().isNotEmpty; + } +} diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 9130ba5..9bd4e0b 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -39620,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.35+283 +/// tallee 0.0.35+277 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.35+283', + version: '0.0.35+277', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view.dart deleted file mode 100644 index 8659a2e..0000000 --- a/lib/presentation/views/main_menu/statistics_view.dart +++ /dev/null @@ -1,311 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/core/constants.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/match.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/widgets/app_skeleton.dart'; -import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart'; -import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; -import 'package:tallee/presentation/widgets/top_centered_message.dart'; - -class StatisticsView extends StatefulWidget { - /// A view that displays player statistics - const StatisticsView({super.key}); - - @override - State createState() => _StatisticsViewState(); -} - -class _StatisticsViewState extends State { - int matchCount = 0; - int groupCount = 0; - - List<(Player, int)> winCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, int)> matchCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, double)> winRates = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - bool isLoading = true; - - @override - void initState() { - super.initState(); - loadStatisticData(); - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context); - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return SingleChildScrollView( - child: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.matches, - icon: Icons.groups_rounded, - value: matchCount, - ), - SizedBox(width: constraints.maxWidth * 0.05), - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.groups, - icon: Icons.groups_rounded, - value: groupCount, - ), - ], - ), - SizedBox(height: constraints.maxHeight * 0.02), - Visibility( - visible: - winCounts.isEmpty && - matchCounts.isEmpty && - winRates.isEmpty, - replacement: Column( - children: [ - StatisticsTile( - icon: Icons.sports_score, - title: loc.wins, - width: constraints.maxWidth * 0.95, - values: winCounts, - itemCount: 3, - barColor: Colors.green, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.percent, - title: loc.winrate, - width: constraints.maxWidth * 0.95, - values: winRates, - itemCount: 5, - barColor: Colors.orange[700]!, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.casino, - title: loc.amount_of_matches, - width: constraints.maxWidth * 0.95, - values: matchCounts, - itemCount: 10, - barColor: Colors.blue, - ), - ], - ), - child: TopCenteredMessage( - icon: Icons.info, - title: loc.info, - message: AppLocalizations.of( - context, - ).no_statistics_available, - ), - ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), - ], - ), - ), - ), - ); - }, - ); - } - - /// Loads matches and players from the database - /// and calculates statistics for each player - void loadStatisticData() { - final db = Provider.of(context, listen: false); - - Future.wait([ - db.matchDao.getAllMatches(), - db.playerDao.getAllPlayers(), - db.matchDao.getMatchCount(), - db.groupDao.getGroupCount(), - Future.delayed(Constants.MINIMUM_SKELETON_DURATION), - ]).then((results) async { - if (!mounted) return; - - final matches = results[0] as List; - final players = results[1] as List; - matchCount = results[2] as int; - groupCount = results[3] as int; - - winCounts = _calculateWinsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - matchCounts = _calculateMatchAmountsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - winRates = computeWinRatePercent( - winCounts: winCounts, - matchCounts: matchCounts, - ); - - setState(() { - isLoading = false; - }); - }); - } - - /// Calculates the number of wins for each player - /// and returns a sorted list of tuples (playerName, winCount) - List<(Player, int)> _calculateWinsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> winCounts = []; - final loc = AppLocalizations.of(context); - - // Getting the winners - for (var match in matches) { - final mvps = match.mvp; - for (var winner in mvps) { - final index = winCounts.indexWhere((entry) => entry.$1.id == winner.id); - // -1 means winner not found in winCounts - if (index != -1) { - final current = winCounts[index].$2; - winCounts[index] = (winner, current + 1); - } else { - winCounts.add((winner, 1)); - } - } - } - - // Adding all players with zero wins - for (var player in players) { - final index = winCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in winCounts - if (index == -1) { - winCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < winCounts.length; i++) { - final playerId = winCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - winCounts[i] = (player, winCounts[i].$2); - } - - winCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return winCounts; - } - - /// Calculates the number of matches played for each player - /// and returns a sorted list of tuples (playerName, matchCount) - List<(Player, int)> _calculateMatchAmountsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> matchCounts = []; - final loc = AppLocalizations.of(context); - - // Counting matches for each player - for (var match in matches) { - for (Player player in match.players) { - // Check if the player is already in matchCounts - final index = matchCounts.indexWhere( - (entry) => entry.$1.id == player.id, - ); - - // -1 -> not found - if (index == -1) { - // Add new entry - matchCounts.add((player, 1)); - } else { - // Update existing entry - final currentMatchAmount = matchCounts[index].$2; - matchCounts[index] = (player, currentMatchAmount + 1); - } - } - } - - // Adding all players with zero matches - for (var player in players) { - final index = matchCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in matchCounts - if (index == -1) { - matchCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < matchCounts.length; i++) { - final playerId = matchCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - matchCounts[i] = (player, matchCounts[i].$2); - } - - matchCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return matchCounts; - } - - List<(Player, double)> computeWinRatePercent({ - required List<(Player, int)> winCounts, - required List<(Player, int)> matchCounts, - }) { - final Map winsMap = {for (var e in winCounts) e.$1: e.$2}; - final Map matchesMap = {for (var e in matchCounts) e.$1: e.$2}; - - // Get all unique player names - final player = {...matchesMap.keys}; - - // Calculate win rates - final result = player.map((name) { - final int w = winsMap[name] ?? 0; - final int m = matchesMap[name] ?? 0; - // Calculate percentage and round to 2 decimal places - // Avoid division by zero - final double percent = (m > 0) - ? double.parse(((w / m)).toStringAsFixed(2)) - : 0; - return (name, percent); - }).toList(); - - // Sort the result: first by winrate descending, - // then by wins descending in case of a tie - result.sort((a, b) { - final cmp = b.$2.compareTo(a.$2); - if (cmp != 0) return cmp; - final wa = winsMap[a.$1] ?? 0; - final wb = winsMap[b.$1] ?? 0; - return wb.compareTo(wa); - }); - - return result; - } -} diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart new file mode 100644 index 0000000..a88432a --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -0,0 +1,635 @@ +import 'package:animated_custom_dropdown/custom_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/constants.dart'; +import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; + +class CreateStatisticView extends StatefulWidget { + const CreateStatisticView({super.key, required this.onStatisticCreated}); + + final void Function() onStatisticCreated; + + @override + State createState() => _CreateStatisticViewState(); +} + +class _CreateStatisticViewState extends State { + bool isLoading = false; + + /* Data loaded from the database */ + List players = []; + List games = []; + List groups = []; + + /* User selections */ + StatisticType? selectedType; + List selectedScope = []; + List selectedGames = []; + List selectedPlayers = []; + List selectedGroups = []; + Timeframe? selectedTimeframe; + + @override + void initState() { + loadAllData(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var loc = AppLocalizations.of(context); + + return ScaffoldMessenger( + child: Scaffold( + appBar: AppBar(title: Text(loc.create_statistic)), + body: Stack( + alignment: AlignmentDirectional.center, + children: [ + SingleChildScrollView( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Classifier title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.classifier, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const Text( + 'description', + textAlign: TextAlign.start, + style: TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + softWrap: true, + ), + ], + ), + ), + + // Classifier selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateStatisticTypeToString(item, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedType, enabled) => Text( + translateStatisticTypeToString(selectedType, context), + style: headerStyle, + ), + hintText: loc.select_a_classifier, + items: StatisticType.values, + decoration: decoration, + onChanged: (value) { + setState(() { + selectedType = value; + }); + }, + ), + ), + + const SizedBox(height: 10), + + // Scope title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.scope, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const Text( + 'description', + textAlign: TextAlign.start, + style: TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // Scope selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: loc.select_a_scope, + items: StatisticScope.values, + decoration: decoration, + listItemBuilder: + (context, scope, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateScopeToString(scope, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems + .map((s) => translateScopeToString(s, context)) + .join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedScope = values; + }); + }, + ), + ), + + if (selectedScope.contains(StatisticScope.selectedGames)) ...[ + const SizedBox(height: 10), + + // games title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.games, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_games, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // game selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_game, + items: games, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + translateRulesetToString( + item.ruleset, + context, + ), + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + + // Check icon + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: const TextStyle( + color: CustomTheme.textColor, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedGames = values; + }); + }, + ), + ), + ], + + if (selectedScope.contains( + StatisticScope.selectedGroups, + )) ...[ + const SizedBox(height: 10), + + // groups title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.groups, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_groups, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_group, + items: groups, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + ' ${item.members.length.toString()} ${loc.members}', + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List groups) { + setState(() { + selectedGroups = groups; + }); + }, + ), + ), + ], + + if (selectedScope.contains(StatisticScope.timeframe)) ...[ + const SizedBox(height: 10), + + // timeframe title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.timeframe, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_timeframe, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + enabled: !isLoading, + excludeSelected: false, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading + ? loc.loading + : loc.select_a_timeframe, + items: Timeframe.values, + decoration: decoration, + listItemBuilder: + (context, timeframe, isSelected, onItemSelect) => + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + translateTimeframeToString( + timeframe, + context, + ), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedTimeframe, enabled) => + Text( + translateTimeframeToString( + selectedTimeframe, + context, + ), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onChanged: (Timeframe? timeframe) { + setState(() { + selectedTimeframe = timeframe; + }); + }, + ), + ), + ], + ], + ), + ), + + // Create statistic button + Positioned( + bottom: MediaQuery.of(context).padding.bottom, + child: AnimatedDialogButton( + buttonConstraints: const BoxConstraints(minWidth: 350), + buttonText: loc.create_statistic, + onPressed: selectedType != null && selectedScope.isNotEmpty + ? () => submitStatistic() + : null, + ), + ), + ], + ), + ), + ); + } + + CustomDropdownDecoration get decoration => CustomDropdownDecoration( + listItemDecoration: const ListItemDecoration( + selectedIconBorder: BorderSide(color: CustomTheme.primaryColor, width: 1), + selectedIconColor: CustomTheme.primaryColor, + highlightColor: CustomTheme.secondaryColor, + splashColor: Colors.transparent, + selectedColor: CustomTheme.onBoxColor, + ), + listItemStyle: itemStyle, + headerStyle: headerStyle, + hintStyle: hintStyle, + closedFillColor: CustomTheme.boxColor, + closedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + expandedFillColor: CustomTheme.boxColor, + expandedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + ); + + CustomDropdownDisabledDecoration get disabledDecoration => + CustomDropdownDisabledDecoration( + fillColor: CustomTheme.boxColor.withAlpha(125), + border: Border.all( + color: CustomTheme.boxBorderColor.withAlpha(125), + width: 1, + ), + headerStyle: disabledHeaderStyle, + hintStyle: disabledHintStyle, + ); + + TextStyle get headerStyle => const TextStyle( + color: CustomTheme.textColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get itemStyle => + const TextStyle(color: CustomTheme.textColor, fontSize: 14); + + TextStyle get hintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + TextStyle get disabledHeaderStyle => const TextStyle( + color: CustomTheme.hintColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get disabledHintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + Future loadAllData() async { + isLoading = true; + final db = Provider.of(context, listen: false); + + Future.wait([ + db.playerDao.getAllPlayers(), + db.groupDao.getAllGroups(), + db.gameDao.getAllGames(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]) + .then((results) async { + players = results[0]; + groups = results[1]; + games = results[2]; + isLoading = false; + }) + .catchError((error) { + print('Error loading data: $error'); + }); + } + + void submitStatistic() { + final newStatistic = Statistic( + type: selectedType!, + scopes: selectedScope, + timeframe: selectedTimeframe, + selectedGroups: selectedGroups, + selectedGames: selectedGames, + ); + final db = Provider.of(context, listen: false); + db.statisticDao.addStatistic(statistic: newStatistic); + Navigator.of(context).pop(newStatistic); + } +} + +String translateTimeframeToString(Timeframe timeframe, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (timeframe) { + case Timeframe.last7Days: + return loc.last_7_days; + case Timeframe.last30Days: + return loc.last_30_days; + case Timeframe.last90Days: + return loc.last_90_days; + case Timeframe.last180Days: + return loc.last_180_days; + case Timeframe.lastYear: + return loc.last_year; + case Timeframe.allTime: + return loc.all_time; + } +} + +String translateScopeToString(StatisticScope scope, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (scope) { + case StatisticScope.allPlayers: + return loc.all_players; + case StatisticScope.selectedGroups: + return loc.selected_groups; + case StatisticScope.selectedGames: + return loc.selected_games; + case StatisticScope.timeframe: + return loc.timeframe; + } +} + +String translateStatisticTypeToString( + StatisticType type, + BuildContext context, +) { + final loc = AppLocalizations.of(context); + switch (type) { + case StatisticType.totalMatches: + return loc.total_matches; + case StatisticType.totalWins: + return loc.total_wins; + case StatisticType.totalScore: + return loc.total_score; + case StatisticType.totalLosses: + return loc.total_losses; + case StatisticType.averageScore: + return loc.average_score; + case StatisticType.bestScore: + return loc.best_score; + case StatisticType.worstScore: + return loc.worst_score; + case StatisticType.winrate: + return loc.winrate; + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart new file mode 100644 index 0000000..f1bc209 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; +import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +class StatisticDetailView extends StatefulWidget { + const StatisticDetailView({ + super.key, + required this.statistic, + required this.values, + required this.icon, + required this.barColor, + }); + + final Statistic statistic; + final List<(Player, num)> values; + final IconData icon; + final Color barColor; + + @override + State createState() => _StatisticDetailViewState(); +} + +class _StatisticDetailViewState extends State { + late int displayCount; + + @override + void initState() { + super.initState(); + displayCount = widget.statistic.displayCount; + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final title = translateStatisticTypeToString( + widget.statistic.type, + context, + ); + const style = TextStyle(fontWeight: FontWeight.bold); + + return Scaffold( + appBar: AppBar( + title: Text(title), + leading: HapticIconButton( + icon: const Icon(Icons.arrow_back_ios_new), + onPressed: () => handleBack(context), + ), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + StatisticsTile( + icon: widget.icon, + title: title, + width: MediaQuery.sizeOf(context).width * 0.95, + values: widget.values, + barColor: widget.barColor, + selectedGroups: widget.statistic.selectedGroups, + selectedGames: widget.statistic.selectedGames, + displayCount: displayCount, + showAllValues: true, + ), + const SizedBox(height: 12), + + InfoTile( + icon: Icons.filter_alt, + title: loc.filter, + content: Column( + spacing: 12, + + children: [ + // Scopes + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.scope, style: style), + Text( + widget.statistic.scopes + .map( + (scope) => translateScopeToString(scope, context), + ) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Timeframe + if (widget.statistic.timeframe != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.timeframe, style: style), + Text( + translateTimeframeToString( + widget.statistic.timeframe!, + context, + ), + textAlign: TextAlign.end, + ), + ], + ), + + // Groups + if (widget.statistic.selectedGroups != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.groups, style: style), + Text( + widget.statistic.selectedGroups! + .map((group) => group.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Games + if (widget.statistic.selectedGames != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.games, style: style), + Text( + widget.statistic.selectedGames! + .map((game) => game.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + if (widget.values.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.displayed_entries, style: style), + Row( + children: [ + HapticIconButton( + icon: const Icon(Icons.remove), + onPressed: displayCount <= 1 + ? null + : () => setState(() => displayCount -= 1), + ), + SizedBox( + width: 30, + child: Text( + '$displayCount', + textAlign: TextAlign.center, + ), + ), + HapticIconButton( + icon: const Icon(Icons.add), + onPressed: displayCount >= widget.values.length + ? null + : () => setState(() => displayCount += 1), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + // Handles saving the display count and giving it to statistics view + Future handleBack(BuildContext context) async { + final db = Provider.of(context, listen: false); + await db.statisticDao.updateDisplayCount(widget.statistic.id, displayCount); + if (context.mounted) Navigator.of(context).pop(displayCount); + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart new file mode 100644 index 0000000..807358e --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -0,0 +1,322 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' + show translateStatisticTypeToString; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +List _colorPalette = AppColor.values + .map((c) => getColorFromAppColor(c)) + .toList(); + +/// Returns the icon for the given statistic type. +IconData getStatisticIconForType(StatisticType type) => + _getStatisticIcon(type: type); + +/// Returns a color from the palette based on the statistic's ID. +Color getStatisticColorForStatistic(Statistic stat) => _getStatisticColor(stat); + +/// Computes the statistic values for a given [Statistic]. +List<(Player, num)> computeStatisticValues({ + required Statistic statistic, + required List matches, + required List players, +}) { + final filteredMatches = _getFilterMatches(statistic, matches); + final filteredPlayers = _getFilteredPlayers( + statistic, + players, + filteredMatches, + ); + + return _computeValuesForType( + type: statistic.type, + matches: filteredMatches, + players: filteredPlayers, + ); +} + +/// Build the [StatisticsTile] for a given [Statistic]. +Widget buildStatisticTile({ + required Statistic statistic, + required List matches, + required List players, + required BuildContext context, + double? width, +}) { + final values = computeStatisticValues( + statistic: statistic, + matches: matches, + players: players, + ); + + return StatisticsTile( + icon: _getStatisticIcon(type: statistic.type), + title: translateStatisticTypeToString(statistic.type, context), + width: width ?? MediaQuery.sizeOf(context).width * 0.95, + values: values, + barColor: _getStatisticColor(statistic), + displayCount: statistic.displayCount, + selectedGroups: statistic.selectedGroups, + selectedGames: statistic.selectedGames, + ); +} + +List _getFilterMatches(Statistic statistic, List matches) { + List filteredMatches = matches; + + // Filter timeframe + if (statistic.scopes.contains(StatisticScope.timeframe) && + statistic.timeframe != null) { + final minDate = _getMinimumDate(timeframe: statistic.timeframe!); + print( + 'Filtering matches by timeframe: ${statistic.timeframe}, minDate: $minDate', + ); + if (minDate != null) { + filteredMatches = matches + .where((m) => m.endedAt != null && m.endedAt!.isAfter(minDate)) + .toList(); + } + } + + // Filter games + if (statistic.scopes.contains(StatisticScope.selectedGames) && + (statistic.selectedGames?.isNotEmpty ?? false)) { + final gameIds = statistic.selectedGames!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((match) => gameIds.contains(match.game.id)) + .toList(); + } + + // Filter groups + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final groupIds = statistic.selectedGroups!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((m) => m.group != null && groupIds.contains(m.group!.id)) + .toList(); + } + + return filteredMatches; +} + +/// Returns a [Player] List with the selected players depending on +List _getFilteredPlayers( + Statistic statistic, + List allPlayers, + List filteredMatches, +) { + // allPlayers + if (statistic.scopes.contains(StatisticScope.allPlayers)) { + return allPlayers; + } + + // selectedGroups -> only members + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final Set ids = { + for (final g in statistic.selectedGroups!) + for (final p in g.members) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); + } + + // Else -> all players from filtered matches + final Set ids = { + for (final m in filteredMatches) + for (final p in m.players) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); +} + +/// Returns a [DateTime] with the minimum time and date the [timeframe] allows +DateTime? _getMinimumDate({required Timeframe timeframe}) { + final now = DateTime.now(); + switch (timeframe) { + case Timeframe.last7Days: + return now.subtract(const Duration(days: 7)); + case Timeframe.last30Days: + return now.subtract(const Duration(days: 30)); + case Timeframe.last90Days: + return now.subtract(const Duration(days: 90)); + case Timeframe.last180Days: + return now.subtract(const Duration(days: 180)); + case Timeframe.lastYear: + return now.subtract(const Duration(days: 365)); + case Timeframe.allTime: + return null; + } +} + +/// Computes the statistic values for each player based on the statistic type +/// and returns a list of (Player, value) tuples sorted descending by value. +List<(Player, num)> _computeValuesForType({ + required StatisticType type, + required List matches, + required List players, +}) { + switch (type) { + case StatisticType.totalMatches: + return _sortDesc( + players.map((p) => (p, _matchesPlayed(p, matches) as num)).toList(), + ); + + case StatisticType.totalWins: + return _sortDesc( + players.map((p) => (p, _wins(p, matches) as num)).toList(), + ); + + case StatisticType.totalLosses: + return _sortDesc( + players + .map( + (p) => + (p, (_matchesPlayed(p, matches) - _wins(p, matches)) as num), + ) + .toList(), + ); + + case StatisticType.totalScore: + return _sortDesc( + players.map((p) => (p, _totalScore(p, matches) as num)).toList(), + ); + + case StatisticType.averageScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final avg = scores.isEmpty + ? 0.0 + : double.parse( + (scores.reduce((a, b) => a + b) / scores.length) + .toStringAsFixed(2), + ); + return (p, avg as num); + }).toList(), + ); + + case StatisticType.bestScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final best = scores.isEmpty ? 0 : scores.reduce(max); + return (p, best as num); + }).toList(), + ); + + case StatisticType.worstScore: + // Ascending here is more meaningful for "worst", but keep the + // existing tile semantics (largest bar = top entry) by sorting + // descending on the inverse — i.e. show smallest score on top. + final entries = players.map((p) { + final scores = _scoresOf(p, matches); + final worst = scores.isEmpty ? 0 : scores.reduce(min); + return (p, worst as num); + }).toList(); + entries.sort((a, b) => a.$2.compareTo(b.$2)); + return entries; + + case StatisticType.winrate: + return _sortDesc( + players.map((p) { + final played = _matchesPlayed(p, matches); + final wins = _wins(p, matches); + final rate = played == 0 + ? 0.0 + : double.parse((wins / played).toStringAsFixed(2)); + return (p, rate as num); + }).toList(), + ); + } +} + +/* Helper functions for different statistic types */ + +/// Detemerines how many matches the player has played in the given list of matches. +int _matchesPlayed(Player p, List matches) => + matches.where((m) => m.players.any((mp) => mp.id == p.id)).length; + +/// Determines how many matches the player has won in the given list of matches. +int _wins(Player p, List matches) => + matches.where((m) => m.mvp.any((mp) => mp.id == p.id)).length; + +/// Determines the total score of the player in the given list of matches. +int _totalScore(Player p, List matches) { + var total = 0; + for (final m in matches) { + final s = m.scores[p.id]; + if (s != null) total += s.score; + } + return total; +} + +/// Returns a list of all scores the player has achieved in the given list of matches. +List _scoresOf(Player p, List matches) => [ + for (final m in matches) + if (m.scores[p.id] != null) m.scores[p.id]!.score, +]; + +/// Returns the list of entries sorted descending by the statistic value. +List<(Player, num)> _sortDesc(List<(Player, num)> entries) { + entries.sort((a, b) => b.$2.compareTo(a.$2)); + return entries; +} + +/* Icon and color */ + +/// Returns the icon for the given statistic type. +IconData _getStatisticIcon({required StatisticType type}) { + switch (type) { + case StatisticType.totalMatches: + return Icons.casino; + case StatisticType.totalWins: + return Icons.emoji_events; + case StatisticType.totalLosses: + return Icons.sentiment_dissatisfied; + case StatisticType.totalScore: + return Icons.scoreboard; + case StatisticType.averageScore: + return Icons.show_chart; + case StatisticType.bestScore: + return Icons.trending_up; + case StatisticType.worstScore: + return Icons.trending_down; + case StatisticType.winrate: + return Icons.percent; + } +} + +/// Returns a color from the palette based on the statistic's ID as random seed. +Color _getStatisticColor(Statistic stat) { + final seed = stat.id.hashCode; + return _colorPalette[seed.abs() % _colorPalette.length]; +} + +/* Skeleton data */ + +/// A placeholder tile with mock data for the loading state. +Widget buildSkeletonStatisticTile({required BuildContext context}) { + final count = 4 + Random().nextInt(5); // 4..8 + final values = <(Player, num)>[ + for (var i = 0; i < count; i++) + (Player(name: 'Player ${i + 1}'), count - i), + ]; + + return StatisticsTile( + icon: Icons.bar_chart, + title: 'Skeleton title', + width: MediaQuery.sizeOf(context).width * 0.95, + values: values, + barColor: _colorPalette[Random().nextInt(_colorPalette.length)], + selectedGames: [Game(name: 'Game 1', ruleset: Ruleset.highestScore)], + selectedGroups: [Group(name: 'Group 1', members: [])], + displayCount: 5, + ); +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart new file mode 100644 index 0000000..3381267 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; +import 'package:tallee/core/constants.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_detail_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart'; +import 'package:tallee/presentation/widgets/app_skeleton.dart'; +import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; +import 'package:tallee/presentation/widgets/top_centered_message.dart'; + +class StatisticsView extends StatefulWidget { + /// A view that displays player statistics + const StatisticsView({super.key}); + + @override + State createState() => _StatisticsViewState(); +} + +class _StatisticsViewState extends State { + bool isLoading = true; + List _allMatches = const []; + List _allPlayers = const []; + List _statistics = const []; + List statisticTiles = []; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + loadStatistics(context); + }); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack( + alignment: AlignmentDirectional.bottomCenter, + fit: StackFit.expand, + children: [ + Visibility( + visible: statisticTiles.isNotEmpty, + replacement: Center( + child: TopCenteredMessage( + icon: Icons.info, + title: loc.info, + message: loc.no_statistics_created_yet, + ), + ), + child: SingleChildScrollView( + child: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: Column( + spacing: 12, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...statisticTiles, + SizedBox( + height: MediaQuery.paddingOf(context).bottom + 80, + ), + ], + ), + ), + ), + ), + Positioned( + bottom: MediaQuery.paddingOf(context).bottom + 20, + child: MainMenuButton( + text: loc.create_statistic, + icon: Icons.bar_chart, + onPressed: () async { + Statistic newStatistic = await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => CreateStatisticView( + onStatisticCreated: () => loadStatistics(context), + ), + ), + ); + if (!context.mounted) return; + setState(() { + _statistics = [..._statistics, newStatistic]; + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + }); + }, + ), + ), + ], + ); + }, + ); + } + + Future loadStatistics(BuildContext context) async { + setState(() { + isLoading = true; + statisticTiles = List.generate( + 4, + (index) => Column( + children: [ + buildSkeletonStatisticTile(context: context), + const SizedBox(height: 12), + ], + ), + ); + }); + + final db = Provider.of(context, listen: false); + + final results = await Future.wait([ + db.statisticDao.getAllStatistics(), + db.matchDao.getAllMatches(), + db.playerDao.getAllPlayers(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]); + + if (!mounted) return; + + final statistics = results[0] as List; + _allMatches = results[1] as List; + _allPlayers = results[2] as List; + _statistics = statistics; + + setState(() { + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + isLoading = false; + }); + } + + Widget _buildStatisticTile(BuildContext context, Statistic statistic) { + final values = computeStatisticValues( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + ); + + return GestureDetector( + onTap: () async { + final newDisplayCount = await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => StatisticDetailView( + statistic: statistic, + values: values, + icon: getStatisticIconForType(statistic.type), + barColor: getStatisticColorForStatistic(statistic), + ), + ), + ); + if (newDisplayCount != null && + newDisplayCount != statistic.displayCount) { + setState(() { + _statistics = _statistics + .map( + (stat) => stat.id == statistic.id + ? stat.copyWith(displayCount: newDisplayCount) + : stat, + ) + .toList(); + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + }); + } + }, + child: buildStatisticTile( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + context: context, + ), + ); + } +} diff --git a/lib/presentation/widgets/app_skeleton.dart b/lib/presentation/widgets/app_skeleton.dart index 8a21320..abdfb8d 100644 --- a/lib/presentation/widgets/app_skeleton.dart +++ b/lib/presentation/widgets/app_skeleton.dart @@ -6,11 +6,13 @@ class AppSkeleton extends StatefulWidget { /// - [child]: The widget tree to apply the skeleton effect to. /// - [enabled]: A boolean to enable or disable the skeleton effect. /// - [fixLayoutBuilder]: A boolean to fix the layout builder for AnimatedSwitcher. + /// - [alignment]: The alignment used for the custom layout builder and optional Align wrapper. Defaults to [Alignment.center]. const AppSkeleton({ super.key, required this.child, this.enabled = true, this.fixLayoutBuilder = false, + this.alignment = Alignment.center, }); /// The widget tree to apply the skeleton effect to. @@ -22,6 +24,9 @@ class AppSkeleton extends StatefulWidget { /// A boolean to fix the layout builder for AnimatedSwitcher. final bool fixLayoutBuilder; + /// The alignment used for the custom layout builder and optional Align wrapper + final Alignment alignment; + @override State createState() => _AppSkeletonState(); } @@ -45,13 +50,14 @@ class _AppSkeletonState extends State { layoutBuilder: !widget.fixLayoutBuilder ? AnimatedSwitcher.defaultLayoutBuilder : (Widget? currentChild, List previousChildren) { - return Stack( - alignment: Alignment.topCenter, - children: [...previousChildren, ?currentChild], - ); + final children = [...previousChildren]; + if (currentChild != null) children.add(currentChild); + return Stack(alignment: widget.alignment, children: children); }, ), - child: widget.child, + child: widget.fixLayoutBuilder + ? Align(alignment: widget.alignment, child: widget.child) + : widget.child, ); } } diff --git a/lib/presentation/widgets/buttons/animated_dialog_button.dart b/lib/presentation/widgets/buttons/animated_dialog_button.dart index 8c8765e..5062b23 100644 --- a/lib/presentation/widgets/buttons/animated_dialog_button.dart +++ b/lib/presentation/widgets/buttons/animated_dialog_button.dart @@ -11,7 +11,7 @@ class AnimatedDialogButton extends StatefulWidget { const AnimatedDialogButton({ super.key, required this.buttonText, - required this.onPressed, + this.onPressed, this.buttonConstraints, this.buttonType = ButtonType.primary, this.isDescructive = false, @@ -19,7 +19,7 @@ class AnimatedDialogButton extends StatefulWidget { final String buttonText; - final VoidCallback onPressed; + final VoidCallback? onPressed; final BoxConstraints? buttonConstraints; @@ -38,28 +38,38 @@ class _AnimatedDialogButtonState extends State { Widget build(BuildContext context) { final textStyling = _getTextStyling(); final buttonDecoration = _getButtonDecoration(); + bool isDisabled = widget.onPressed == null; - return GestureDetector( - onTapDown: (_) => setState(() => _isPressed = true), - onTapUp: (_) => setState(() => _isPressed = false), - onTapCancel: () => setState(() => _isPressed = false), - onTap: widget.onPressed, - child: AnimatedScale( - scale: _isPressed ? 0.95 : 1.0, - duration: const Duration(milliseconds: 100), - child: AnimatedOpacity( - opacity: _isPressed ? 0.6 : 1.0, - duration: const Duration(milliseconds: 100), - child: Center( - child: Container( - constraints: widget.buttonConstraints, - decoration: buttonDecoration, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - margin: const EdgeInsets.symmetric(vertical: 8), - child: Text( - widget.buttonText, - style: textStyling, - textAlign: TextAlign.center, + return IgnorePointer( + ignoring: isDisabled, + child: Opacity( + opacity: isDisabled ? 0.5 : 1.0, + child: GestureDetector( + onTapDown: (_) => setState(() => _isPressed = true), + onTapUp: (_) => setState(() => _isPressed = false), + onTapCancel: () => setState(() => _isPressed = false), + onTap: widget.onPressed, + child: AnimatedScale( + scale: _isPressed ? 0.95 : 1.0, + duration: const Duration(milliseconds: 100), + child: AnimatedOpacity( + opacity: _isPressed ? 0.6 : 1.0, + duration: const Duration(milliseconds: 100), + child: Center( + child: Container( + constraints: widget.buttonConstraints, + decoration: buttonDecoration, + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + margin: const EdgeInsets.symmetric(vertical: 8), + child: Text( + widget.buttonText, + style: textStyling, + textAlign: TextAlign.center, + ), + ), ), ), ), diff --git a/lib/presentation/widgets/dialog/custom_alert_dialog.dart b/lib/presentation/widgets/dialog/custom_alert_dialog.dart index 606fc49..7dfba98 100644 --- a/lib/presentation/widgets/dialog/custom_alert_dialog.dart +++ b/lib/presentation/widgets/dialog/custom_alert_dialog.dart @@ -19,7 +19,6 @@ class CustomAlertDialog extends StatelessWidget { final String title; final Widget content; final List actions; - @override Widget build(BuildContext context) { return AlertDialog( diff --git a/lib/presentation/widgets/dialog/custom_dialog_action.dart b/lib/presentation/widgets/dialog/custom_dialog_action.dart index 26dc40d..0c0b2e0 100644 --- a/lib/presentation/widgets/dialog/custom_dialog_action.dart +++ b/lib/presentation/widgets/dialog/custom_dialog_action.dart @@ -10,7 +10,7 @@ class CustomDialogAction extends StatelessWidget { /// - [onPressed]: Callback function that is triggered when the button is pressed. const CustomDialogAction({ super.key, - required this.onPressed, + this.onPressed, required this.text, this.buttonType = ButtonType.primary, this.isDestructive = false, @@ -20,17 +20,18 @@ class CustomDialogAction extends StatelessWidget { final ButtonType buttonType; - final VoidCallback onPressed; + final VoidCallback? onPressed; final bool isDestructive; - @override Widget build(BuildContext context) { return AnimatedDialogButton( - onPressed: () async { - await HapticFeedback.selectionClick(); - onPressed.call(); - }, + onPressed: onPressed != null + ? () async { + await HapticFeedback.selectionClick(); + onPressed?.call(); + } + : null, buttonText: text, buttonType: buttonType, isDescructive: isDestructive, diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index 553e637..2e6bf74 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -12,41 +12,44 @@ class GameLabel extends StatelessWidget { final String title; final String description; - final GameColor color; + final AppColor color; @override Widget build(BuildContext context) { - final backgroundColor = getColorFromGameColor(color); + final backgroundColor = getColorFromAppColor(color); final fontColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; - return IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Title - Container( - decoration: BoxDecoration( - color: backgroundColor.withAlpha(230), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - bottomLeft: Radius.circular(8), - ), - ), - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - child: Text( - title, - style: TextStyle( - fontSize: 12, - color: fontColor, - fontWeight: FontWeight.bold, - ), + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Title + Container( + decoration: BoxDecoration( + color: backgroundColor.withAlpha(230), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + bottomLeft: Radius.circular(8), ), ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: TextStyle( + fontSize: 12, + color: fontColor, + fontWeight: FontWeight.bold, + ), + ), + ), - // Description - Container( + // Description + Flexible( + child: Container( decoration: BoxDecoration( color: backgroundColor.withAlpha(140), borderRadius: const BorderRadius.only( @@ -57,6 +60,9 @@ class GameLabel extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Text( description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, style: TextStyle( fontSize: 12, color: fontColor, @@ -64,8 +70,8 @@ class GameLabel extends StatelessWidget { ), ), ), - ], - ), + ), + ], ); } } diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index 00d6c11..6a62c95 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -26,6 +26,7 @@ class PlayerSelection extends StatefulWidget { this.availablePlayers, this.initialSelectedPlayers, required this.onChanged, + this.onPlayerCreated, }); /// An optional list of players to choose from. If null, all players from the database are used. @@ -37,6 +38,9 @@ class PlayerSelection extends StatefulWidget { /// A callback function that is invoked whenever the selection changes, final Function(List value) onChanged; + /// A callback function that is invoked when a player was created in this widget + final VoidCallback? onPlayerCreated; + @override State createState() => _PlayerSelectionState(); } @@ -323,6 +327,7 @@ class _PlayerSelectionState extends State { /// Updates the state after successfully adding a new player. void _handleSuccessfulPlayerCreation(Player player) { + widget.onPlayerCreated?.call(); selectedPlayers.insert(0, player); widget.onChanged([...selectedPlayers]); allPlayers.add(player); diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/game_tile.dart index ee5acf0..4fb12d1 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/game_tile.dart @@ -51,7 +51,7 @@ class GameTile extends StatelessWidget { ? (badgeColor!.computeLuminance() > 0.5 ? Colors.black : Colors.white) : Colors.white; - final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange); + final gameColor = badgeColor ?? getColorFromAppColor(AppColor.orange); return GestureDetector( onTap: () async { diff --git a/lib/presentation/widgets/tiles/group_tile.dart b/lib/presentation/widgets/tiles/group_tile.dart index f6c406e..c01dcbe 100644 --- a/lib/presentation/widgets/tiles/group_tile.dart +++ b/lib/presentation/widgets/tiles/group_tile.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/data/models/group.dart'; +import 'package:tallee/presentation/views/main_menu/player_detail_view.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; class GroupTile extends StatefulWidget { @@ -15,6 +17,7 @@ class GroupTile extends StatefulWidget { required this.group, this.isHighlighted = false, this.onTap, + this.onPlayerChanged, }); /// The group data to be displayed. @@ -26,6 +29,9 @@ class GroupTile extends StatefulWidget { /// Callback function to be executed when the tile is tapped. final VoidCallback? onTap; + /// Callback function to be executed when the players in the group are changed. + final VoidCallback? onPlayerChanged; + @override State createState() => _GroupTileState(); } @@ -92,6 +98,19 @@ class _GroupTileState extends State { text: member.name, suffixText: getNameCountText(member), iconEnabled: false, + onTileTap: () { + Navigator.push( + context, + adaptivePageRoute( + builder: (context) => PlayerDetailView( + player: member, + callback: () { + widget.onPlayerChanged?.call(); + }, + ), + ), + ); + }, ), ], ), diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index 6a81dc3..c3d0242 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -3,11 +3,13 @@ import 'dart:core' hide Match; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; +import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/player_detail_view.dart'; import 'package:tallee/presentation/widgets/game_label.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; @@ -24,6 +26,7 @@ class MatchTile extends StatefulWidget { required this.onTap, this.width, this.compact = false, + this.onPlayerEdited, }); /// The match data to be displayed. @@ -32,6 +35,9 @@ class MatchTile extends StatefulWidget { /// The callback invoked when the tile is tapped. final VoidCallback onTap; + /// The callback invoked when the players are edited + final VoidCallback? onPlayerEdited; + /// Optional width for the tile. final double? width; @@ -224,6 +230,19 @@ class _MatchTileState extends State { text: player.name, suffixText: getNameCountText(player), iconEnabled: false, + onTileTap: () { + Navigator.push( + context, + adaptivePageRoute( + builder: (context) => PlayerDetailView( + player: player, + callback: () { + widget.onPlayerEdited?.call(); + }, + ), + ), + ); + }, ); }).toList(), ), diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index ea9cb49..02cdb75 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -1,8 +1,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; @@ -21,8 +25,11 @@ class StatisticsTile extends StatelessWidget { required this.title, required this.width, required this.values, - required this.itemCount, required this.barColor, + required this.displayCount, + this.selectedGroups, + this.selectedGames, + this.showAllValues = false, }); /// The icon displayed next to the title. @@ -37,12 +44,16 @@ class StatisticsTile extends StatelessWidget { /// A list of tuples containing labels and their corresponding numeric values. final List<(Player, num)> values; - /// The maximum number of items to display. - final int itemCount; - /// The color of the bars representing the values. final Color barColor; + // statistic data + final int displayCount; + final List? selectedGroups; + final List? selectedGames; + + final bool showAllValues; + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -52,91 +63,202 @@ class StatisticsTile extends StatelessWidget { title: title, icon: icon, content: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Visibility( visible: values.isNotEmpty, + + // No data avaiable message replacement: Center( heightFactor: 4, child: Text(loc.no_data_available), ), + + // Bar chart child: LayoutBuilder( builder: (context, constraints) { - final maxBarWidth = constraints.maxWidth * 0.65; + final maxBarWidth = constraints.maxWidth * 0.8; + + // If displayCount wasnt provided, take all values + final valuesShown = showAllValues + ? values.length + : min(values.length, displayCount); + final displayValues = values.take(valuesShown).toList(); + final maxVal = displayValues.isNotEmpty + ? displayValues.fold( + 0, + (currentMax, entry) => + entry.$2 > currentMax ? entry.$2 : currentMax, + ) + : 0; + return Column( - children: List.generate(min(values.length, itemCount), (index) { - /// The maximum wins among all players - final maxMatches = values.isNotEmpty ? values[0].$2 : 0; + children: [ + // Bars + ...List.generate(valuesShown, (index) { + /// Fraction of wins + final double fraction = (maxVal > 0) + ? (displayValues[index].$2 / maxVal) + : 0.0; - /// Fraction of wins - final double fraction = (maxMatches > 0) - ? (values[index].$2 / maxMatches) - : 0.0; + /// Calculated width for current the bar + final double barWidth = (maxBarWidth * fraction).clamp( + 0.0, + maxBarWidth, + ); - /// Calculated width for current the bar - final double barWidth = maxBarWidth * fraction; + final barClr = index >= displayCount + ? barColor.withAlpha(150) + : barColor; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Stack( - children: [ - Container( - height: 24, - width: barWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: barColor, - ), - ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: values[index].$1.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: getNameCountText(values[index].$1), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor.withAlpha( - 150, - ), - ), - ), - ], + var textClr = barColor.computeLuminance() > 0.5 + ? const Color(0xFF101010) + : CustomTheme.textColor; + textClr = textClr.withAlpha( + index >= displayCount ? 220 : 255, + ); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: maxBarWidth, + child: Stack( + clipBehavior: Clip.hardEdge, + children: [ + // Bar + Container( + height: 24, + width: barWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: barClr, + ), ), - ), - ), - ], - ), - const Spacer(), - Center( - child: Text( - values[index].$2 <= 1 && values[index].$2 is double - ? values[index].$2.toStringAsFixed(2) - : values[index].$2.toString(), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + + // Player + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: RichText( + maxLines: 1, + softWrap: false, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: displayValues[index].$1.name, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: textClr, + ), + ), + TextSpan( + text: getNameCountText( + displayValues[index].$1, + ), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: + (barColor == + getColorFromAppColor( + AppColor.yellow, + ) + ? const Color( + 0xFF101010, + ) + : CustomTheme.textColor) + .withAlpha(150), + ), + ), + ], + ), + ), + ), + ], ), ), - ), - ], + const Spacer(), + + // Value + Center( + child: Text( + displayValues[index].$2 <= 1 && + displayValues[index].$2 is double + ? displayValues[index].$2.toStringAsFixed(2) + : displayValues[index].$2.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ); + }), + + // Group & Game info + if (hasGame || hasGroup) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + runSpacing: 4, + children: [ + // Game + if (hasGroup) + Row( + spacing: 8, + children: [ + const Icon( + RpgAwesome.clovers_card, + color: CustomTheme.hintColor, + size: 20, + ), + Text( + getGameText(selectedGames!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + if (hasGroup && hasGame) const SizedBox(width: 20), + + // Group + if (hasGroup) + Row( + spacing: 8, + children: [ + const Icon( + Icons.groups, + color: CustomTheme.hintColor, + ), + Text( + getGroupText(selectedGroups!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ], + ), ), - ); - }), + ], ); }, ), @@ -144,4 +266,24 @@ class StatisticsTile extends StatelessWidget { ), ); } + + String getGroupText(List groups) { + var text = groups[0].name; + if (groups.length > 1) { + return '$text + ${groups.length - 1}'; + } + return text; + } + + String getGameText(List games) { + var text = games[0].name; + if (games.length > 1) { + return '$text + ${games.length - 1}'; + } + return text; + } + + bool get hasGroup => selectedGroups != null && selectedGroups!.isNotEmpty; + + bool get hasGame => selectedGames != null && selectedGames!.isNotEmpty; } diff --git a/lib/presentation/widgets/tiles/text_icon_tile.dart b/lib/presentation/widgets/tiles/text_icon_tile.dart index 541b6ae..a5ef399 100644 --- a/lib/presentation/widgets/tiles/text_icon_tile.dart +++ b/lib/presentation/widgets/tiles/text_icon_tile.dart @@ -12,6 +12,7 @@ class TextIconTile extends StatelessWidget { this.suffixText = '', this.iconEnabled = true, this.onIconTap, + this.onTileTap, }); /// The text to display in the tile. @@ -25,52 +26,58 @@ class TextIconTile extends StatelessWidget { /// The callback to be invoked when the icon is tapped. final VoidCallback? onIconTap; + /// The callback to be invoked when the tile is tapped. + final VoidCallback? onTileTap; + @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: CustomTheme.onBoxColor, - borderRadius: BorderRadius.circular(12), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.min, - children: [ - if (iconEnabled) const SizedBox(width: 3), - Flexible( - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: text, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, + return GestureDetector( + onTap: onTileTap, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: CustomTheme.onBoxColor, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, + children: [ + if (iconEnabled) const SizedBox(width: 3), + Flexible( + child: RichText( + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: text, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), ), - ), - TextSpan( - text: suffixText, - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: CustomTheme.textColor.withAlpha(120), + TextSpan( + text: suffixText, + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: CustomTheme.textColor.withAlpha(120), + ), ), - ), - ], + ], + ), ), ), - ), - if (iconEnabled) ...[ - const SizedBox(width: 3), - GestureDetector( - onTap: onIconTap, - child: const Icon(Icons.close, size: 20), - ), + if (iconEnabled) ...[ + const SizedBox(width: 3), + GestureDetector( + onTap: onIconTap, + child: const Icon(Icons.close, size: 20), + ), + ], ], - ], + ), ), ); } diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 29199f8..9dbf955 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -20,6 +20,7 @@ class DataTransferService { static Future deleteAllData(BuildContext context) async { final db = Provider.of(context, listen: false); + await db.statisticDao.deleteAllStatistics(); await db.matchDao.deleteAllMatches(); await db.teamDao.deleteAllTeams(); await db.groupDao.deleteAllGroups(); @@ -278,7 +279,7 @@ class DataTransferService { name: 'Unknown', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); } diff --git a/pubspec.lock b/pubspec.lock index 2ec2197..fc30e57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" + animated_custom_dropdown: + dependency: "direct main" + description: + name: animated_custom_dropdown + sha256: "5a72dc209041bb53f6c7164bc2e366552d5197cdb032b1c9b2c36e3013024486" + url: "https://pub.dev" + source: hosted + version: "3.1.1" arb_utils: dependency: "direct dev" description: @@ -85,10 +93,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6" + sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" url: "https://pub.dev" source: hosted - version: "2.15.0" + version: "2.13.1" built_collection: dependency: transitive description: @@ -177,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" + url: "https://pub.dev" + source: hosted + version: "4.11.1" collection: dependency: "direct main" description: @@ -325,26 +341,34 @@ packages: dependency: "direct main" description: name: drift - sha256: "8033500116b24398fba0cca0369cc31678cd627c01e41753a61186911cea743e" + sha256: "970cd188fddb111b26ea6a9b07a62bf5c2432d74147b8122c67044ae3b97e99e" url: "https://pub.dev" source: hosted - version: "2.33.0" + version: "2.31.0" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: b3dd5b75e30522a91da8abda9f5bb17230cb038097f6d15fa75d42bb563428aa + sha256: "917184b2fb867b70a548a83bf0d36268423b38d39968c06cce4905683da49587" url: "https://pub.dev" source: hosted - version: "2.33.0" + version: "2.31.0" drift_flutter: dependency: "direct main" description: name: drift_flutter - sha256: "887fdec622174dc7eaefd0048403e34ee07cc18626ac8a7544cc3b8a4a172166" + sha256: c07120854742a0cae2f7501a0da02493addde550db6641d284983c08762e60a7 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.2.8" + dropdown_flutter: + dependency: "direct main" + description: + name: dropdown_flutter + sha256: "5ae3d05d768d0bb6030ff735e6b4b93f7b29be3cf3bec7c86cd4f444c8f067ff" + url: "https://pub.dev" + source: hosted + version: "1.0.3" equatable: dependency: transitive description: @@ -389,10 +413,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "68c9a085d9bb4546e0a31d1e583a48d7c17a6987d538788ea064f0043b1fc02d" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.3.1" fixnum: dependency: transitive description: @@ -1114,38 +1138,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.2" - sqlcipher_flutter_libs: - dependency: transitive - description: - name: sqlcipher_flutter_libs - sha256: "38d62d659d2fb8739bf25a42c9a350d1fdd6c29a5a61f13a946778ec75d27929" - url: "https://pub.dev" - source: hosted - version: "0.7.0+eol" sqlite3: dependency: transitive description: name: sqlite3 - sha256: "56da3e13ed7d28a66f930aa2b2b29db6736a233f08283326e96321dd812030f5" + sha256: "3145bd74dcdb4fd6f5c6dda4d4e4490a8087d7f286a14dee5d37087290f0f8a2" url: "https://pub.dev" source: hosted - version: "3.3.1" + version: "2.9.4" sqlite3_flutter_libs: dependency: transitive description: name: sqlite3_flutter_libs - sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" + sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad url: "https://pub.dev" source: hosted - version: "0.6.0+eol" + version: "0.5.42" sqlparser: dependency: transitive description: name: sqlparser - sha256: ecdc06d4a7d79dcbc928d99afd2f7f5b0f98a637c46f89be83d911617f759978 + sha256: "337e9997f7141ffdd054259128553c348635fa318f7ca492f07a4ab76f850d19" url: "https://pub.dev" source: hosted - version: "0.44.4" + version: "0.43.1" stack_trace: dependency: transitive description: @@ -1427,5 +1443,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.12.0 <4.0.0" - flutter: ">=3.41.0" + dart: ">=3.10.3 <4.0.0" + flutter: ">=3.38.4" diff --git a/pubspec.yaml b/pubspec.yaml index d8b07ed..183335c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,16 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.35+283 +version: 0.0.33+281 environment: sdk: ^3.12.0 dependencies: + animated_custom_dropdown: ^3.1.1 clock: ^1.1.2 collection: ^1.19.1 + dropdown_flutter: ^1.0.3 cupertino_icons: ^1.0.9 drift: ^2.33.0 drift_flutter: ^0.3.0 diff --git a/test/db_tests/aggregates/group_test.dart b/test/db_tests/aggregates/group_test.dart index 1498523..3ea625f 100644 --- a/test/db_tests/aggregates/group_test.dart +++ b/test/db_tests/aggregates/group_test.dart @@ -194,6 +194,31 @@ void main() { expect(allGroups, isEmpty); }); + test('getGroupsByPlayer() works correctly', () async { + await database.groupDao.addGroupsAsList( + groups: [testGroup1, testGroup2], + ); + + final groups = await database.groupDao.getGroupsByPlayer( + playerId: testPlayer2.id, + ); + + expect(groups, hasLength(2)); + expect(groups.any((group) => group.id == testGroup1.id), isTrue); + expect(groups.any((group) => group.id == testGroup2.id), isTrue); + }); + + test( + 'getGroupsByPlayer() returns empty list for non-existent player', + () async { + final groups = await database.groupDao.getGroupsByPlayer( + playerId: 'non-existent-player-id', + ); + + expect(groups, isEmpty); + }, + ); + test('addGroupsAsList() with duplicate groups only adds once', () async { await database.groupDao.addGroupsAsList( groups: [testGroup1, testGroup1, testGroup1], diff --git a/test/db_tests/aggregates/match_test.dart b/test/db_tests/aggregates/match_test.dart index 37c1cd0..1144a73 100644 --- a/test/db_tests/aggregates/match_test.dart +++ b/test/db_tests/aggregates/match_test.dart @@ -56,7 +56,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( @@ -260,6 +260,34 @@ void main() { expect(match.group!.id, testGroup1.id); }); + test('getMatchesByPlayer() works correctly', () async { + await database.matchDao.addMatchesAsList( + matches: [testMatch1, testMatch2], + ); + + final matches = await database.matchDao.getMatchesByPlayer( + playerId: testPlayer1.id, + ); + + expect(matches, hasLength(1)); + expect(matches.first.id, testMatch2.id); + expect( + matches.first.players.any((p) => p.id == testPlayer1.id), + isTrue, + ); + }); + + test( + 'getMatchesByPlayer() returns empty list for non-existent player', + () async { + final matches = await database.matchDao.getMatchesByPlayer( + playerId: 'non-existing-player-id', + ); + + expect(matches, isEmpty); + }, + ); + test('getMatchCount() works correctly', () async { var count = await database.matchDao.getMatchCount(); expect(count, 0); diff --git a/test/db_tests/aggregates/team_test.dart b/test/db_tests/aggregates/team_test.dart index fefdcc5..381d22b 100644 --- a/test/db_tests/aggregates/team_test.dart +++ b/test/db_tests/aggregates/team_test.dart @@ -49,7 +49,7 @@ void main() { testGame = Game( name: 'Test Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/entities/game_test.dart b/test/db_tests/entities/game_test.dart index 778d43b..f7e7dcd 100644 --- a/test/db_tests/entities/game_test.dart +++ b/test/db_tests/entities/game_test.dart @@ -28,7 +28,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'A classic strategy game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); testGame2 = Game( @@ -36,7 +36,7 @@ void main() { name: 'Poker', ruleset: Ruleset.multipleWinners, description: 'Card game with multiple winners', - color: GameColor.red, + color: AppColor.red, icon: 'poker_icon', ); testGame3 = Game( @@ -44,7 +44,7 @@ void main() { name: 'Monopoly', ruleset: Ruleset.highestScore, description: 'A board game about real estate', - color: GameColor.orange, + color: AppColor.orange, icon: '', ); }); @@ -124,7 +124,7 @@ void main() { name: 'Game\'s & "Special" ', ruleset: Ruleset.multipleWinners, description: 'Description with émojis 🎮🎲', - color: GameColor.purple, + color: AppColor.purple, icon: '', ); await database.gameDao.addGame(game: specialGame); @@ -280,19 +280,19 @@ void main() { await database.gameDao.updateGameColor( gameId: testGame1.id, - color: GameColor.green, + color: AppColor.green, ); final updatedGame = await database.gameDao.getGameById( gameId: testGame1.id, ); - expect(updatedGame.color, GameColor.green); + expect(updatedGame.color, AppColor.green); }); test('updateGameColor() does nothing for non-existent game', () async { final updated = await database.gameDao.updateGameColor( gameId: 'non-existent-id', - color: GameColor.green, + color: AppColor.green, ); expect(updated, isFalse); @@ -336,7 +336,7 @@ void main() { name: newName, ); - const newGameColor = GameColor.teal; + const newGameColor = AppColor.teal; await database.gameDao.updateGameColor( gameId: testGame1.id, color: newGameColor, diff --git a/test/db_tests/entities/player_test.dart b/test/db_tests/entities/player_test.dart index bfcced4..4105df1 100644 --- a/test/db_tests/entities/player_test.dart +++ b/test/db_tests/entities/player_test.dart @@ -233,6 +233,95 @@ void main() { expect(allPlayers, isEmpty); }); + test('updatePlayerName() sets correct nameCount with 2 player', () async { + await database.playerDao.addPlayer(player: testPlayer1); + await database.playerDao.addPlayer(player: testPlayer2); + + final newName = testPlayer1.name; + await database.playerDao.updatePlayerName( + playerId: testPlayer2.id, + name: newName, + ); + + var player = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(player.nameCount, 1); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer2.id, + ); + expect(player.nameCount, 2); + + await database.playerDao.updatePlayerName( + playerId: testPlayer1.id, + name: 'different name', + ); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(player.nameCount, 0); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer2.id, + ); + expect(player.nameCount, 0); + }); + + test('updatePlayerName() sets correct nameCount with 3 player', () async { + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer2, testPlayer3], + ); + + // Changing both names to player 1's name + final newName = testPlayer1.name; + await database.playerDao.updatePlayerName( + playerId: testPlayer2.id, + name: newName, + ); + await database.playerDao.updatePlayerName( + playerId: testPlayer3.id, + name: newName, + ); + + var player = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(player.nameCount, 1); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer2.id, + ); + expect(player.nameCount, 2); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer3.id, + ); + expect(player.nameCount, 3); + + // Changing the middle players name + await database.playerDao.updatePlayerName( + playerId: testPlayer2.id, + name: 'different name', + ); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(player.nameCount, 1); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer2.id, + ); + expect(player.nameCount, 0); + + player = await database.playerDao.getPlayerById( + playerId: testPlayer3.id, + ); + expect(player.nameCount, 2); + }); + test('updatePlayerDescription() works correctly', () async { await database.playerDao.addPlayer(player: testPlayer1); @@ -372,14 +461,22 @@ void main() { final player1 = Player(name: testPlayer1.name, description: ''); await database.playerDao.addPlayer(player: player1); + final player2 = Player(name: testPlayer1.name, description: ''); + await database.playerDao.addPlayer(player: player2); + var players = await database.playerDao.getAllPlayers(); - expect(players.length, 2); + expect(players.length, 3); players.sort((a, b) => a.nameCount.compareTo(b.nameCount)); for (int i = 0; i < players.length - 1; i++) { expect(players[i].nameCount, i + 1); } + + // ids are correct in the right order + expect(players[0].id, testPlayer1.id); + expect(players[1].id, player1.id); + expect(players[2].id, player2.id); }, ); @@ -404,24 +501,62 @@ void main() { for (int i = 0; i < players.length - 1; i++) { expect(players[i].nameCount, i + 1); } + + // ids are correct in the right order + expect(players[0].id, testPlayer1.id); + expect(players[1].id, player1.id); + expect(players[2].id, player2.id); + expect(players[3].id, player3.id); }, ); - test('getNameCount works correctly', () async { + test('getNameCount works correctly', () async { + final player1 = Player(name: testPlayer1.name); final player2 = Player(name: testPlayer1.name); - final player3 = Player(name: testPlayer1.name); - await database.playerDao.addPlayersAsList( - players: [testPlayer1, player2, player3], + await database.playerDao.addPlayer(player: testPlayer1); + + var nameCount = await database.playerDao.getNameCount( + name: testPlayer1.name, ); - final nameCount = await database.playerDao.getNameCount( + expect(nameCount, 1); + + await database.playerDao.addPlayersAsList(players: [player1, player2]); + + nameCount = await database.playerDao.getNameCount( name: testPlayer1.name, ); expect(nameCount, 3); }); + test('calculateNameCount works correctly', () async { + final player1 = Player(name: testPlayer1.name); + final player2 = Player(name: testPlayer1.name); + + // Case 1: No existing players with the name + var nameCount = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(nameCount, 0); + + // Case 2: One existing player with the name. Should return 2 for + // the new player + await database.playerDao.addPlayer(player: testPlayer1); + nameCount = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(nameCount, 2); + + // Case 3: Multiple existing players with the name. Should return count + 1 + await database.playerDao.addPlayersAsList(players: [player1, player2]); + nameCount = await database.playerDao.calculateNameCount( + name: testPlayer1.name, + ); + expect(nameCount, 4); + }); + test('updateNameCount works correctly', () async { await database.playerDao.addPlayer(player: testPlayer1); @@ -441,14 +576,24 @@ void main() { final player2 = Player(name: testPlayer1.name, description: ''); final player3 = Player(name: testPlayer1.name, description: ''); - await database.playerDao.addPlayersAsList( - players: [testPlayer1, player2, player3], - ); - - final player = await database.playerDao.getPlayerWithHighestNameCount( + await database.playerDao.addPlayer(player: testPlayer1); + var player = await database.playerDao.getPlayerWithHighestNameCount( name: testPlayer1.name, ); + expect(player, isNotNull); + expect(player!.nameCount, 0); + await database.playerDao.addPlayer(player: player2); + player = await database.playerDao.getPlayerWithHighestNameCount( + name: testPlayer1.name, + ); + expect(player, isNotNull); + expect(player!.nameCount, 2); + + await database.playerDao.addPlayer(player: player3); + player = await database.playerDao.getPlayerWithHighestNameCount( + name: testPlayer1.name, + ); expect(player, isNotNull); expect(player!.nameCount, 3); }); @@ -460,32 +605,6 @@ void main() { expect(player, isNull); }); - test('calculateNameCount works correctly', () async { - // Case 1: No existing players with the name - var count = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(count, 0); - - // Case 2: One existing player with the name. Should update that - // player's nameCount to 1 and return 2 for the new player - await database.playerDao.addPlayer(player: testPlayer1); - - count = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(count, 2); - - // Case 3: Multiple existing players with the name. - final player2 = Player(name: testPlayer1.name, nameCount: count); - await database.playerDao.addPlayer(player: player2); - - count = await database.playerDao.calculateNameCount( - name: testPlayer1.name, - ); - expect(count, 3); - }); - test('getPlayerWithHighestNameCount with non existing player', () async { await database.playerDao.addPlayer(player: testPlayer1); await database.playerDao.initializeNameCount(name: testPlayer1.name); diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart index 6d879c3..fa7ec21 100644 --- a/test/db_tests/relationships/player_match_test.dart +++ b/test/db_tests/relationships/player_match_test.dart @@ -42,7 +42,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart new file mode 100644 index 0000000..bd590c7 --- /dev/null +++ b/test/db_tests/statistics/statistic_test.dart @@ -0,0 +1,122 @@ +import 'dart:core'; + +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNotNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Group testGroup1; + late Group testGroup2; + late Game testGame; + /*late Match testMatch1; + late Match testMatch2; + late Match testMatchOnlyPlayers; + late Match testMatchOnlyGroup;*/ + final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() async { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testPlayer5 = Player(name: 'Eve'); + testGroup1 = Group( + name: 'Test Group 1', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testGroup2 = Group( + name: 'Test Group 2', + description: '', + members: [testPlayer4, testPlayer5], + ); + testGame = Game( + name: 'Test Game', + ruleset: Ruleset.singleWinner, + description: 'A test game', + color: AppColor.blue, + icon: '', + ); + /*testMatch1 = Match( + name: 'First Test Match', + game: testGame, + group: testGroup1, + players: [testPlayer4, testPlayer5], + scores: {testPlayer4.id: ScoreEntry(score: 1)}, + ); + testMatch2 = Match( + name: 'Second Test Match', + game: testGame, + group: testGroup2, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyPlayers = Match( + name: 'Test Match with Players', + game: testGame, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyGroup = Match( + name: 'Test Match with Group', + game: testGame, + group: testGroup2, + players: testGroup2.members, + );*/ + }); + await database.playerDao.addPlayersAsList( + players: [ + testPlayer1, + testPlayer2, + testPlayer3, + testPlayer4, + testPlayer5, + ], + ); + await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); + await database.gameDao.addGame(game: testGame); + }); + tearDown(() async { + await database.close(); + }); + + test('Adding/fetching statistic works correclty', () async { + final statistic = Statistic( + type: StatisticType.averageScore, + scopes: [StatisticScope.allPlayers, StatisticScope.selectedGames], + timeframe: Timeframe.allTime, + selectedGames: [testGame], + selectedGroups: [testGroup1], + ); + + final added = await database.statisticDao.addStatistic( + statistic: statistic, + ); + expect(added, isTrue); + + final fetched = await database.statisticDao.getStatisticById(statistic.id); + expect(fetched, isNotNull); + expect(fetched!.type, statistic.type); + }); +} diff --git a/test/db_tests/values/score_entry_test.dart b/test/db_tests/values/score_entry_test.dart index f6cc292..593d194 100644 --- a/test/db_tests/values/score_entry_test.dart +++ b/test/db_tests/values/score_entry_test.dart @@ -40,7 +40,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/services/data_transfer_service_test.dart b/test/services/data_transfer_service_test.dart index 586138a..94ed977 100644 --- a/test/services/data_transfer_service_test.dart +++ b/test/services/data_transfer_service_test.dart @@ -45,7 +45,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'Strategic board game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); @@ -448,19 +448,19 @@ void main() { Game( name: 'Red Game', ruleset: Ruleset.singleWinner, - color: GameColor.red, + color: AppColor.red, icon: 'icon', ), Game( name: 'Blue Game', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Green Game', ruleset: Ruleset.singleWinner, - color: GameColor.green, + color: AppColor.green, icon: 'icon', ), ]; @@ -484,19 +484,19 @@ void main() { Game( name: 'Highest Score Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Lowest Score Game', ruleset: Ruleset.lowestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Single Winner', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), ]; -- 2.49.1