From 0a316035c065ac7c9d249f7b537fa227b8aae347 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Tue, 8 Jul 2025 22:28:46 +0200 Subject: [PATCH 001/108] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e5e187d..20b0cae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CABO Counter -![Version](https://img.shields.io/badge/Version-0.3.0-orange) +![Version](https://img.shields.io/badge/Version-0.3.8-orange) ![Flutter](https://img.shields.io/badge/Flutter-3.32.1-blue?logo=flutter) ![Dart](https://img.shields.io/badge/Dart-3.8.1-blue?logo=dart) ![iOS](https://img.shields.io/badge/iOS-18.5-white?logo=apple) From 34207997f62db9560a9a1570b2278601f836edc8 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 14:15:36 +0200 Subject: [PATCH 002/108] Tried new design for im- and export-button --- lib/views/settings_view.dart | 39 ++++++++++++++++++++++++++++++++++-- pubspec.yaml | 2 +- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/views/settings_view.dart b/lib/views/settings_view.dart index 2e86122..8185d8a 100644 --- a/lib/views/settings_view.dart +++ b/lib/views/settings_view.dart @@ -107,7 +107,7 @@ class _SettingsViewState extends State { style: CustomTheme.rowTitle, ), ), - Padding( + /*Padding( padding: const EdgeInsets.only(top: 30), child: Center( heightFactor: 1, @@ -163,7 +163,42 @@ class _SettingsViewState extends State { ), ], )), - ) + ),*/ + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 0), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CupertinoFormRow( + prefix: Row( + children: [ + Icon( + CupertinoIcons.square_arrow_up, + color: CustomTheme.primaryColor, + ), + const SizedBox(width: 10), + const Text('Spieldaten exportieren'), + ], + ), + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 15), + child: const CupertinoListTileChevron()), + CupertinoFormRow( + prefix: Row( + children: [ + Icon( + CupertinoIcons.square_arrow_down, + color: CustomTheme.primaryColor, + ), + const SizedBox(width: 10), + const Text('Spieldaten importieren'), + ], + ), + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 15), + child: const CupertinoListTileChevron()) + ])), ], ), Positioned( diff --git a/pubspec.yaml b/pubspec.yaml index 562b189..9b8aaa4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.3.9+331 +version: 0.3.9+333 environment: sdk: ^3.5.4 From 1bae1be93b7d6e5957f4c6502210537ea596baea Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 14:17:54 +0200 Subject: [PATCH 003/108] Moved views to presentation folder --- lib/main.dart | 2 +- lib/{ => presentation}/views/active_game_view.dart | 6 +++--- lib/{ => presentation}/views/create_game_view.dart | 4 ++-- lib/{ => presentation}/views/graph_view.dart | 0 lib/{ => presentation}/views/information_view.dart | 0 lib/{ => presentation}/views/main_menu_view.dart | 6 +++--- lib/{ => presentation}/views/mode_selection_view.dart | 0 lib/{ => presentation}/views/round_view.dart | 0 lib/{ => presentation}/views/settings_view.dart | 0 lib/{ => presentation}/views/tab_view.dart | 4 ++-- 10 files changed, 11 insertions(+), 11 deletions(-) rename lib/{ => presentation}/views/active_game_view.dart (98%) rename lib/{ => presentation}/views/create_game_view.dart (98%) rename lib/{ => presentation}/views/graph_view.dart (100%) rename lib/{ => presentation}/views/information_view.dart (100%) rename lib/{ => presentation}/views/main_menu_view.dart (98%) rename lib/{ => presentation}/views/mode_selection_view.dart (100%) rename lib/{ => presentation}/views/round_view.dart (100%) rename lib/{ => presentation}/views/settings_view.dart (100%) rename lib/{ => presentation}/views/tab_view.dart (90%) diff --git a/lib/main.dart b/lib/main.dart index cd3d05f..06e2d2b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,8 @@ import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/tab_view.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; diff --git a/lib/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart similarity index 98% rename from lib/views/active_game_view.dart rename to lib/presentation/views/active_game_view.dart index 5945a3e..ddc0299 100644 --- a/lib/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -1,11 +1,11 @@ import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/create_game_view.dart'; +import 'package:cabo_counter/presentation/views/graph_view.dart'; +import 'package:cabo_counter/presentation/views/round_view.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/create_game_view.dart'; -import 'package:cabo_counter/views/graph_view.dart'; -import 'package:cabo_counter/views/round_view.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/create_game_view.dart b/lib/presentation/views/create_game_view.dart similarity index 98% rename from lib/views/create_game_view.dart rename to lib/presentation/views/create_game_view.dart index f6099f4..2c04e74 100644 --- a/lib/views/create_game_view.dart +++ b/lib/presentation/views/create_game_view.dart @@ -1,10 +1,10 @@ import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/active_game_view.dart'; +import 'package:cabo_counter/presentation/views/mode_selection_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/active_game_view.dart'; -import 'package:cabo_counter/views/mode_selection_view.dart'; import 'package:flutter/cupertino.dart'; enum CreateStatus { diff --git a/lib/views/graph_view.dart b/lib/presentation/views/graph_view.dart similarity index 100% rename from lib/views/graph_view.dart rename to lib/presentation/views/graph_view.dart diff --git a/lib/views/information_view.dart b/lib/presentation/views/information_view.dart similarity index 100% rename from lib/views/information_view.dart rename to lib/presentation/views/information_view.dart diff --git a/lib/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart similarity index 98% rename from lib/views/main_menu_view.dart rename to lib/presentation/views/main_menu_view.dart index e087cfc..230c4de 100644 --- a/lib/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,11 +1,11 @@ import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/active_game_view.dart'; +import 'package:cabo_counter/presentation/views/create_game_view.dart'; +import 'package:cabo_counter/presentation/views/settings_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/active_game_view.dart'; -import 'package:cabo_counter/views/create_game_view.dart'; -import 'package:cabo_counter/views/settings_view.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/views/mode_selection_view.dart b/lib/presentation/views/mode_selection_view.dart similarity index 100% rename from lib/views/mode_selection_view.dart rename to lib/presentation/views/mode_selection_view.dart diff --git a/lib/views/round_view.dart b/lib/presentation/views/round_view.dart similarity index 100% rename from lib/views/round_view.dart rename to lib/presentation/views/round_view.dart diff --git a/lib/views/settings_view.dart b/lib/presentation/views/settings_view.dart similarity index 100% rename from lib/views/settings_view.dart rename to lib/presentation/views/settings_view.dart diff --git a/lib/views/tab_view.dart b/lib/presentation/views/tab_view.dart similarity index 90% rename from lib/views/tab_view.dart rename to lib/presentation/views/tab_view.dart index 4abd411..efa4311 100644 --- a/lib/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,7 +1,7 @@ import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/information_view.dart'; +import 'package:cabo_counter/presentation/views/main_menu_view.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/views/information_view.dart'; -import 'package:cabo_counter/views/main_menu_view.dart'; import 'package:flutter/cupertino.dart'; class TabView extends StatefulWidget { From ed8aced8a870426bdb50d5528c38912f33f450bb Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 14:18:28 +0200 Subject: [PATCH 004/108] Moved widgets to presentation folder --- lib/presentation/views/settings_view.dart | 2 +- lib/{ => presentation}/widgets/stepper.dart | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename lib/{ => presentation}/widgets/stepper.dart (100%) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 8185d8a..7875890 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,9 +1,9 @@ import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:cabo_counter/utility/globals.dart'; -import 'package:cabo_counter/widgets/stepper.dart'; import 'package:flutter/cupertino.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/widgets/stepper.dart b/lib/presentation/widgets/stepper.dart similarity index 100% rename from lib/widgets/stepper.dart rename to lib/presentation/widgets/stepper.dart From 0c774366594d944e2250fd35ae3585fd1a04ec29 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 15:20:05 +0200 Subject: [PATCH 005/108] Implemented CustomRowForm Widget --- lib/presentation/widgets/custom_form_row.dart | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 lib/presentation/widgets/custom_form_row.dart diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart new file mode 100644 index 0000000..61d0fdb --- /dev/null +++ b/lib/presentation/widgets/custom_form_row.dart @@ -0,0 +1,49 @@ +import 'package:cabo_counter/utility/custom_theme.dart'; +import 'package:flutter/cupertino.dart'; + +class CustomFormRow extends StatefulWidget { + final String prefixText; + final IconData prefixIcon; + final Widget? suffixWidget; + final void Function()? onTap; + const CustomFormRow({ + super.key, + required this.prefixText, + required this.prefixIcon, + required this.onTap, + this.suffixWidget, + }); + + @override + State createState() => _CustomFormRowState(); +} + +class _CustomFormRowState extends State { + late Widget suffixWidget; + @override + void initState() { + super.initState(); + suffixWidget = widget.suffixWidget ?? const SizedBox.shrink(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: CupertinoFormRow( + prefix: Row( + children: [ + Icon( + widget.prefixIcon, + color: CustomTheme.primaryColor, + ), + const SizedBox(width: 10), + Text(widget.prefixText), + ], + ), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + child: suffixWidget, + ), + ); + } +} From 7fa95f4bcaf7c27438955dd413b37cc366de4c09 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 15:20:28 +0200 Subject: [PATCH 006/108] Used new custom form row --- lib/presentation/views/settings_view.dart | 106 +++++----------------- pubspec.yaml | 2 +- 2 files changed, 22 insertions(+), 86 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 7875890..4c44e1b 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,10 +1,12 @@ import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:cabo_counter/utility/globals.dart'; import 'package:flutter/cupertino.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -107,97 +109,31 @@ class _SettingsViewState extends State { style: CustomTheme.rowTitle, ), ), - /*Padding( - padding: const EdgeInsets.only(top: 30), - child: Center( - heightFactor: 1, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CupertinoButton( - color: CustomTheme.primaryColor, - sizeStyle: CupertinoButtonSize.medium, - child: Text( - AppLocalizations.of(context).import_data, - style: - TextStyle(color: CustomTheme.backgroundColor), - ), - onPressed: () async { - final success = - await LocalStorageService.importJsonFile(); - showFeedbackDialog(success); - }), - const SizedBox( - width: 20, - ), - CupertinoButton( - color: CustomTheme.primaryColor, - sizeStyle: CupertinoButtonSize.medium, - child: Text( - AppLocalizations.of(context).export_data, - style: - TextStyle(color: CustomTheme.backgroundColor), - ), - onPressed: () async { - final success = - await LocalStorageService.exportGameData(); - if (!success && context.mounted) { - showCupertinoDialog( - context: context, - builder: (context) => CupertinoAlertDialog( - title: Text(AppLocalizations.of(context) - .export_error_title), - content: Text(AppLocalizations.of(context) - .export_error_message), - actions: [ - CupertinoDialogAction( - child: - Text(AppLocalizations.of(context).ok), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ); - } - }, - ), - ], - )), - ),*/ Padding( padding: const EdgeInsets.fromLTRB(10, 15, 10, 0), child: CupertinoFormSection.insetGrouped( backgroundColor: CustomTheme.backgroundColor, margin: EdgeInsets.zero, children: [ - CupertinoFormRow( - prefix: Row( - children: [ - Icon( - CupertinoIcons.square_arrow_up, - color: CustomTheme.primaryColor, - ), - const SizedBox(width: 10), - const Text('Spieldaten exportieren'), - ], - ), - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - child: const CupertinoListTileChevron()), - CupertinoFormRow( - prefix: Row( - children: [ - Icon( - CupertinoIcons.square_arrow_down, - color: CustomTheme.primaryColor, - ), - const SizedBox(width: 10), - const Text('Spieldaten importieren'), - ], - ), - padding: const EdgeInsets.symmetric( - vertical: 10, horizontal: 15), - child: const CupertinoListTileChevron()) + CustomFormRow( + prefixText: 'Spieldaten importieren', + prefixIcon: CupertinoIcons.square_arrow_down, + onTap: () {}, + suffixWidget: CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: 'Spieldaten exportieren', + prefixIcon: CupertinoIcons.square_arrow_up, + onTap: () {}, + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).create_issue, + prefixIcon: FontAwesomeIcons.github, + onTap: () => launchUrl(Uri.parse( + 'https://github.com/flixcoo/Cabo-Counter/issues')), + suffixWidget: const CupertinoListTileChevron(), + ), ])), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 9b8aaa4..2bae78c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.3.9+333 +version: 0.3.9+337 environment: sdk: ^3.5.4 From 2d34ebf35b71d52b9f1702e70668da5cc6aba869 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 15:21:46 +0200 Subject: [PATCH 007/108] Removed double information --- lib/presentation/views/settings_view.dart | 60 +++++++++-------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 4c44e1b..baaf24e 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -141,44 +141,28 @@ class _SettingsViewState extends State { bottom: 30, left: 0, right: 0, - child: Column( - children: [ - Center( - child: Text(AppLocalizations.of(context).error_found), - ), - Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 30), - child: Center( - child: CupertinoButton( - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/issues')), - child: Text(AppLocalizations.of(context).create_issue), - ), - ), - ), - FutureBuilder( - future: _getPackageInfo(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - '${Globals.appDevPhase} ${snapshot.data!.version} ' - '(${AppLocalizations.of(context).build} ${snapshot.data!.buildNumber})', - textAlign: TextAlign.center, - ); - } else if (snapshot.hasError) { - return Text( - '${AppLocalizations.of(context).app_version} -.-.- (${AppLocalizations.of(context).build} -)', - textAlign: TextAlign.center, - ); - } - return Text( - AppLocalizations.of(context).load_version, - textAlign: TextAlign.center, - ); - }, - ) - ], - )), + child: Center( + child: FutureBuilder( + future: _getPackageInfo(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text( + '${Globals.appDevPhase} ${snapshot.data!.version} ' + '(${AppLocalizations.of(context).build} ${snapshot.data!.buildNumber})', + textAlign: TextAlign.center, + ); + } else if (snapshot.hasError) { + return Text( + '${AppLocalizations.of(context).app_version} -.-.- (${AppLocalizations.of(context).build} -)', + textAlign: TextAlign.center, + ); + } + return Text( + AppLocalizations.of(context).load_version, + textAlign: TextAlign.center, + ); + }, + ))), ], )), ); From ee5ec4d0d99cdc3f5649c9273e3a153de560d459 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 16:19:16 +0200 Subject: [PATCH 008/108] Refactored methods to private --- lib/services/local_storage_service.dart | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/services/local_storage_service.dart b/lib/services/local_storage_service.dart index 12130a1..29ac58b 100644 --- a/lib/services/local_storage_service.dart +++ b/lib/services/local_storage_service.dart @@ -21,7 +21,7 @@ class LocalStorageService { static const String _fileName = 'game_data.json'; /// Writes the game session list to a JSON file and returns it as string. - static String getGameDataAsJsonFile() { + static String _getGameDataAsJsonFile() { final jsonFile = gameManager.gameList.map((session) => session.toJson()).toList(); return json.encode(jsonFile); @@ -39,7 +39,7 @@ class LocalStorageService { print('[local_storage_service.dart] Versuche, Daten zu speichern...'); try { final file = await _getFilePath(); - final jsonFile = getGameDataAsJsonFile(); + final jsonFile = _getGameDataAsJsonFile(); await file.writeAsString(jsonFile); print( '[local_storage_service.dart] Die Spieldaten wurden zwischengespeichert.'); @@ -70,7 +70,7 @@ class LocalStorageService { return false; } - if (!await validateJsonSchema(jsonString, true)) { + if (!await _validateJsonSchema(jsonString, true)) { print( '[local_storage_service.dart] Die Datei konnte nicht validiert werden'); gameManager.gameList = []; @@ -105,7 +105,7 @@ class LocalStorageService { /// Opens the file picker to export game data as a JSON file. /// This method will export the given [jsonString] as a JSON file. It opens /// the file picker with the choosen [fileName]. - static Future exportJsonData( + static Future _exportJsonData( String jsonString, String fileName, ) async { @@ -133,16 +133,16 @@ class LocalStorageService { /// Opens the file picker to export all game sessions as a JSON file. static Future exportGameData() async { - String jsonString = getGameDataAsJsonFile(); + String jsonString = _getGameDataAsJsonFile(); String fileName = 'cabo_counter-game_data'; - return exportJsonData(jsonString, fileName); + return _exportJsonData(jsonString, fileName); } /// Opens the file picker to save a single game session as a JSON file. static Future exportSingleGameSession(GameSession session) async { String jsonString = json.encode(session.toJson()); String fileName = 'cabo_counter-game_${session.id.substring(0, 7)}'; - return exportJsonData(jsonString, fileName); + return _exportJsonData(jsonString, fileName); } /// Opens the file picker to import a JSON file and loads the game data from it. @@ -162,7 +162,7 @@ class LocalStorageService { try { final jsonString = await _readFileContent(path.files.single); - if (await validateJsonSchema(jsonString, true)) { + if (await _validateJsonSchema(jsonString, true)) { // Checks if the JSON String is in the gameList format final jsonData = json.decode(jsonString) as List; @@ -172,12 +172,12 @@ class LocalStorageService { .toList(); for (GameSession s in importedList) { - importSession(s); + _importSession(s); } - } else if (await validateJsonSchema(jsonString, false)) { + } else if (await _validateJsonSchema(jsonString, false)) { // Checks if the JSON String is in the single game format final jsonData = json.decode(jsonString) as Map; - importSession(GameSession.fromJson(jsonData)); + _importSession(GameSession.fromJson(jsonData)); } else { return ImportStatus.validationError; } @@ -198,7 +198,7 @@ class LocalStorageService { } /// Imports a single game session into the gameList. - static Future importSession(GameSession session) async { + static Future _importSession(GameSession session) async { if (gameManager.gameExistsInGameList(session.id)) { print( '[local_storage_service.dart] Die Session mit der ID ${session.id} existiert bereits. Sie wird überschrieben.'); @@ -221,7 +221,7 @@ class LocalStorageService { /// This method checks if the provided [jsonString] is valid against the /// JSON schema. It takes a boolean [isGameList] to determine /// which schema to use (game list or single game). - static Future validateJsonSchema( + static Future _validateJsonSchema( String jsonString, bool isGameList) async { final String schemaString; From 249609276411d0399e57ca44a5c9c2e2ae1dde9f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 16:21:14 +0200 Subject: [PATCH 009/108] Changed label --- lib/l10n/app_de.arb | 6 +++--- lib/l10n/app_en.arb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 9281ac1..632988c 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -82,9 +82,9 @@ "point_limit": "Punkte-Limit", "point_limit_subtitle": "... hier ist Schluss", "reset_to_default": "Auf Standard zurücksetzen", - "game_data": "Spieldaten", - "import_data": "Daten importieren", - "export_data": "Daten exportieren", + "app": "App", + "import_data": "Spieldaten importieren", + "export_data": "Spieldaten exportieren", "import_success_title": "Import erfolgreich", "import_success_message":"Die Spieldaten wurden erfolgreich importiert.", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 8b328ba..1948270 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -78,7 +78,7 @@ "point_limit": "Point Limit", "point_limit_subtitle": "... the game ends here.", "reset_to_default": "Reset to Default", - "game_data": "Game Data", + "app": "App", "import_data": "Import Data", "export_data": "Export Data", "id_error_title": "ID Error", From 847e3dcd19b834e86d25aa6378fa064fed0a6bb2 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 16:21:32 +0200 Subject: [PATCH 010/108] Modified paddings and text color --- lib/presentation/widgets/stepper.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/presentation/widgets/stepper.dart b/lib/presentation/widgets/stepper.dart index 879235e..8ca2635 100644 --- a/lib/presentation/widgets/stepper.dart +++ b/lib/presentation/widgets/stepper.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; // Für iOS-Style class Stepper extends StatefulWidget { @@ -34,18 +35,20 @@ class _StepperState extends State { Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, children: [ CupertinoButton( - padding: const EdgeInsets.all(8), + padding: EdgeInsets.zero, onPressed: _decrement, child: const Icon(CupertinoIcons.minus), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Text('$_value', style: const TextStyle(fontSize: 18)), + child: Text('$_value', + style: TextStyle(fontSize: 18, color: CustomTheme.white)), ), CupertinoButton( - padding: const EdgeInsets.all(8), + padding: EdgeInsets.zero, onPressed: _increment, child: const Icon(CupertinoIcons.add), ), From 21920d2ac4807e6f40ce4842bb024e74e0741b5f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:14:20 +0200 Subject: [PATCH 011/108] Changed string --- lib/l10n/app_de.arb | 5 +++-- lib/l10n/app_en.arb | 13 +++++++------ lib/l10n/app_localizations.dart | 22 ++++++++++++++-------- lib/l10n/app_localizations_de.dart | 11 +++++++---- lib/l10n/app_localizations_en.dart | 7 +++++-- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 632988c..b83c646 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -82,9 +82,10 @@ "point_limit": "Punkte-Limit", "point_limit_subtitle": "... hier ist Schluss", "reset_to_default": "Auf Standard zurücksetzen", - "app": "App", + "data": "Daten", "import_data": "Spieldaten importieren", "export_data": "Spieldaten exportieren", + "app": "App", "import_success_title": "Import erfolgreich", "import_success_message":"Die Spieldaten wurden erfolgreich importiert.", @@ -101,7 +102,7 @@ "create_issue": "Issue erstellen", "app_version": "App-Version", "build": "Build", - "load_version": "Lade Version...", + "loading": "Lädt...", "about_text": "Hey :) Danke, dass du als eine:r der ersten User meiner ersten eigenen App dabei bist! Ich hab sehr viel Arbeit in dieses Projekt gesteckt und auch, wenn ich (hoffentlich) an vieles Gedacht hab, wird auf jeden Fall noch nicht alles 100% funktionieren. Solltest du also irgendwelche Fehler entdecken oder Feedback zum Design oder der Benutzerfreundlichkeit haben, teile Sie mir gern über die Testflight App oder auf den dir bekannten Wegen mit. Danke! " } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1948270..1c111e2 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -71,6 +71,10 @@ "export_game": "Export Game", "game_process": "Spielverlauf", + "id_error_title": "ID Error", + "id_error_message": "The game has not yet been assigned an ID. If you want to delete the game, please do so via the main menu. All newly created games have an ID.", + "end_game_title": "End the game?", + "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", "settings": "Settings", "cabo_penalty": "Cabo Penalty", @@ -78,13 +82,10 @@ "point_limit": "Point Limit", "point_limit_subtitle": "... the game ends here.", "reset_to_default": "Reset to Default", - "app": "App", + "data": "Data", "import_data": "Import Data", "export_data": "Export Data", - "id_error_title": "ID Error", - "id_error_message": "The game has not yet been assigned an ID. If you want to delete the game, please do so via the main menu. All newly created games have an ID.", - "end_game_title": "End the game?", - "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", + "app": "App", "import_success_title": "Import successful", "import_success_message":"The game data has been successfully imported.", @@ -100,7 +101,7 @@ "error_found": "Found a bug?", "create_issue": "Create Issue", "app_version": "App Version", - "load_version": "Loading version...", + "loading": "Loading...", "build": "Build", "about_text": "Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!" diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index c836194..84295df 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -458,24 +458,30 @@ abstract class AppLocalizations { /// **'Auf Standard zurücksetzen'** String get reset_to_default; - /// No description provided for @game_data. + /// No description provided for @data. /// /// In de, this message translates to: - /// **'Spieldaten'** - String get game_data; + /// **'Daten'** + String get data; /// No description provided for @import_data. /// /// In de, this message translates to: - /// **'Daten importieren'** + /// **'Spieldaten importieren'** String get import_data; /// No description provided for @export_data. /// /// In de, this message translates to: - /// **'Daten exportieren'** + /// **'Spieldaten exportieren'** String get export_data; + /// No description provided for @app. + /// + /// In de, this message translates to: + /// **'App'** + String get app; + /// No description provided for @import_success_title. /// /// In de, this message translates to: @@ -560,11 +566,11 @@ abstract class AppLocalizations { /// **'Build'** String get build; - /// No description provided for @load_version. + /// No description provided for @loading. /// /// In de, this message translates to: - /// **'Lade Version...'** - String get load_version; + /// **'Lädt...'** + String get loading; /// No description provided for @about_text. /// diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 5b9d841..f4a2bbd 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -200,13 +200,16 @@ class AppLocalizationsDe extends AppLocalizations { String get reset_to_default => 'Auf Standard zurücksetzen'; @override - String get game_data => 'Spieldaten'; + String get data => 'Daten'; @override - String get import_data => 'Daten importieren'; + String get import_data => 'Spieldaten importieren'; @override - String get export_data => 'Daten exportieren'; + String get export_data => 'Spieldaten exportieren'; + + @override + String get app => 'App'; @override String get import_success_title => 'Import erfolgreich'; @@ -254,7 +257,7 @@ class AppLocalizationsDe extends AppLocalizations { String get build => 'Build'; @override - String get load_version => 'Lade Version...'; + String get loading => 'Lädt...'; @override String get about_text => diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c98dddd..aac55f2 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -197,7 +197,7 @@ class AppLocalizationsEn extends AppLocalizations { String get reset_to_default => 'Reset to Default'; @override - String get game_data => 'Game Data'; + String get data => 'Data'; @override String get import_data => 'Import Data'; @@ -205,6 +205,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get export_data => 'Export Data'; + @override + String get app => 'App'; + @override String get import_success_title => 'Import successful'; @@ -251,7 +254,7 @@ class AppLocalizationsEn extends AppLocalizations { String get build => 'Build'; @override - String get load_version => 'Loading version...'; + String get loading => 'Loading...'; @override String get about_text => From 9bc80a8cd94e827576bc726d506b9319b5ba1220 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:15:00 +0200 Subject: [PATCH 012/108] Updated CustomFormRow padding and pressed handler --- lib/presentation/widgets/custom_form_row.dart | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart index 61d0fdb..f03b453 100644 --- a/lib/presentation/widgets/custom_form_row.dart +++ b/lib/presentation/widgets/custom_form_row.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; @@ -5,12 +6,12 @@ class CustomFormRow extends StatefulWidget { final String prefixText; final IconData prefixIcon; final Widget? suffixWidget; - final void Function()? onTap; + final void Function()? onPressed; const CustomFormRow({ super.key, required this.prefixText, required this.prefixIcon, - required this.onTap, + this.onPressed, this.suffixWidget, }); @@ -28,8 +29,9 @@ class _CustomFormRowState extends State { @override Widget build(BuildContext context) { - return GestureDetector( - onTap: widget.onTap, + return CupertinoButton( + padding: EdgeInsets.zero, + onPressed: widget.onPressed, child: CupertinoFormRow( prefix: Row( children: [ @@ -41,7 +43,9 @@ class _CustomFormRowState extends State { Text(widget.prefixText), ], ), - padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), + padding: suffixWidget is Stepper + ? const EdgeInsets.fromLTRB(15, 0, 0, 0) + : const EdgeInsets.symmetric(vertical: 10, horizontal: 15), child: suffixWidget, ), ); From 696ade5b9b1c60f52cda8aed76d21c708d0c6842 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:15:30 +0200 Subject: [PATCH 013/108] Implemented various new forms of CustomFormRow into SettingsView --- lib/presentation/views/settings_view.dart | 234 +++++++++++++--------- pubspec.yaml | 2 +- 2 files changed, 139 insertions(+), 97 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index baaf24e..34540a4 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -46,66 +46,89 @@ class _SettingsViewState extends State { ), ), Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoListTile( - padding: EdgeInsets.zero, - title: Text(AppLocalizations.of(context).cabo_penalty), - subtitle: Text( - AppLocalizations.of(context).cabo_penalty_subtitle), - trailing: Stepper( - key: _stepperKey1, - initialValue: ConfigService.caboPenalty, - minValue: 0, - maxValue: 50, - step: 1, - onChanged: (newCaboPenalty) { - setState(() { - ConfigService.setCaboPenalty(newCaboPenalty); - ConfigService.caboPenalty = newCaboPenalty; - }); - }, - ), - )), - Padding( - padding: const EdgeInsets.fromLTRB(15, 10, 10, 0), - child: CupertinoListTile( - padding: EdgeInsets.zero, - title: Text(AppLocalizations.of(context).point_limit), - subtitle: - Text(AppLocalizations.of(context).point_limit_subtitle), - trailing: Stepper( - key: _stepperKey2, - initialValue: ConfigService.pointLimit, - minValue: 30, - maxValue: 1000, - step: 10, - onChanged: (newPointLimit) { - setState(() { - ConfigService.setPointLimit(newPointLimit); - ConfigService.pointLimit = newPointLimit; - }); - }, - ), - )), - Padding( - padding: const EdgeInsets.fromLTRB(0, 10, 0, 0), - child: Center( - heightFactor: 0.9, - child: CupertinoButton( - padding: EdgeInsets.zero, - onPressed: () => setState(() { - ConfigService.resetConfig(); - _stepperKey1 = UniqueKey(); - _stepperKey2 = UniqueKey(); - }), - child: - Text(AppLocalizations.of(context).reset_to_default), - ), - )), + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + key: _stepperKey1, + prefixText: 'Cabo-Strafe', + prefixIcon: CupertinoIcons.minus_square, + suffixWidget: Stepper( + initialValue: ConfigService.caboPenalty, + minValue: 0, + maxValue: 50, + step: 1, + onChanged: (newCaboPenalty) { + setState(() { + ConfigService.setCaboPenalty(newCaboPenalty); + ConfigService.caboPenalty = newCaboPenalty; + }); + }, + ), + ), + CustomFormRow( + key: _stepperKey2, + prefixText: 'Punkte-Limit', + prefixIcon: FontAwesomeIcons.bullseye, + suffixWidget: Stepper( + initialValue: ConfigService.pointLimit, + minValue: 30, + maxValue: 1000, + step: 10, + onChanged: (newPointLimit) { + setState(() { + ConfigService.setPointLimit(newPointLimit); + ConfigService.pointLimit = newPointLimit; + }); + }, + ), + ), + CustomFormRow( + prefixText: + AppLocalizations.of(context).reset_to_default, + prefixIcon: CupertinoIcons.arrow_counterclockwise, + onPressed: () { + ConfigService.resetConfig(); + setState(() { + _stepperKey1 = UniqueKey(); + _stepperKey2 = UniqueKey(); + print('Config reset to default'); + }); + }, + ) + ])), Padding( padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( - AppLocalizations.of(context).game_data, + AppLocalizations.of(context).data, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).import_data, + prefixIcon: CupertinoIcons.square_arrow_down, + onPressed: () => LocalStorageService.importJsonFile(), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).export_data, + prefixIcon: CupertinoIcons.square_arrow_up, + onPressed: () => LocalStorageService.importJsonFile(), + suffixWidget: const CupertinoListTileChevron(), + ), + ])), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).app, style: CustomTheme.rowTitle, ), ), @@ -115,54 +138,73 @@ class _SettingsViewState extends State { backgroundColor: CustomTheme.backgroundColor, margin: EdgeInsets.zero, children: [ - CustomFormRow( - prefixText: 'Spieldaten importieren', - prefixIcon: CupertinoIcons.square_arrow_down, - onTap: () {}, - suffixWidget: CupertinoListTileChevron(), - ), - CustomFormRow( - prefixText: 'Spieldaten exportieren', - prefixIcon: CupertinoIcons.square_arrow_up, - onTap: () {}, - suffixWidget: const CupertinoListTileChevron(), - ), CustomFormRow( prefixText: AppLocalizations.of(context).create_issue, prefixIcon: FontAwesomeIcons.github, - onTap: () => launchUrl(Uri.parse( + onPressed: () => launchUrl(Uri.parse( 'https://github.com/flixcoo/Cabo-Counter/issues')), suffixWidget: const CupertinoListTileChevron(), ), + CustomFormRow( + prefixText: 'App-Version', + prefixIcon: CupertinoIcons.info, + onPressed: null, + suffixWidget: FutureBuilder( + future: _getPackageInfo(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text( + '${Globals.appDevPhase} ${snapshot.data!.version} ', + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ); + } else if (snapshot.hasError) { + return Text( + '${AppLocalizations.of(context).app_version} -.-.-', + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ); + } + return Text( + AppLocalizations.of(context).loading, + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ); + }, + )), + CustomFormRow( + prefixText: 'Build-Nr.', + prefixIcon: CupertinoIcons.info, + onPressed: null, + suffixWidget: FutureBuilder( + future: _getPackageInfo(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Text( + snapshot.data!.buildNumber, + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ); + } else if (snapshot.hasError) { + return Text( + '-', + style: TextStyle( + color: CustomTheme.primaryColor, + ), + ); + } + return Text( + AppLocalizations.of(context).loading, + ); + }, + )), ])), ], ), - Positioned( - bottom: 30, - left: 0, - right: 0, - child: Center( - child: FutureBuilder( - future: _getPackageInfo(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - '${Globals.appDevPhase} ${snapshot.data!.version} ' - '(${AppLocalizations.of(context).build} ${snapshot.data!.buildNumber})', - textAlign: TextAlign.center, - ); - } else if (snapshot.hasError) { - return Text( - '${AppLocalizations.of(context).app_version} -.-.- (${AppLocalizations.of(context).build} -)', - textAlign: TextAlign.center, - ); - } - return Text( - AppLocalizations.of(context).load_version, - textAlign: TextAlign.center, - ); - }, - ))), ], )), ); diff --git a/pubspec.yaml b/pubspec.yaml index 2bae78c..fdf9916 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.3.9+337 +version: 0.4.0+371 environment: sdk: ^3.5.4 From aba4bf9c0bd8b09a67e0f104145fa81e08d86e1a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:30:28 +0200 Subject: [PATCH 014/108] Implemented VersionService --- lib/main.dart | 2 + lib/presentation/views/settings_view.dart | 64 ++++------------------- lib/services/version_service.dart | 32 ++++++++++++ pubspec.yaml | 2 +- 4 files changed, 44 insertions(+), 56 deletions(-) create mode 100644 lib/services/version_service.dart diff --git a/lib/main.dart b/lib/main.dart index 06e2d2b..b826072 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:cabo_counter/services/version_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -13,6 +14,7 @@ Future main() async { await ConfigService.initConfig(); ConfigService.pointLimit = await ConfigService.getPointLimit(); ConfigService.caboPenalty = await ConfigService.getCaboPenalty(); + await VersionService.init(); runApp(const App()); } diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 34540a4..2ed422f 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -3,11 +3,10 @@ import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; +import 'package:cabo_counter/services/version_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; -import 'package:cabo_counter/utility/globals.dart'; import 'package:flutter/cupertino.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class SettingsView extends StatefulWidget { @@ -149,59 +148,18 @@ class _SettingsViewState extends State { prefixText: 'App-Version', prefixIcon: CupertinoIcons.info, onPressed: null, - suffixWidget: FutureBuilder( - future: _getPackageInfo(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - '${Globals.appDevPhase} ${snapshot.data!.version} ', - style: TextStyle( - color: CustomTheme.primaryColor, - ), - ); - } else if (snapshot.hasError) { - return Text( - '${AppLocalizations.of(context).app_version} -.-.-', - style: TextStyle( - color: CustomTheme.primaryColor, - ), - ); - } - return Text( - AppLocalizations.of(context).loading, - style: TextStyle( - color: CustomTheme.primaryColor, - ), - ); - }, - )), + suffixWidget: Text(VersionService.getVersion(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), CustomFormRow( prefixText: 'Build-Nr.', prefixIcon: CupertinoIcons.info, onPressed: null, - suffixWidget: FutureBuilder( - future: _getPackageInfo(), - builder: (context, snapshot) { - if (snapshot.hasData) { - return Text( - snapshot.data!.buildNumber, - style: TextStyle( - color: CustomTheme.primaryColor, - ), - ); - } else if (snapshot.hasError) { - return Text( - '-', - style: TextStyle( - color: CustomTheme.primaryColor, - ), - ); - } - return Text( - AppLocalizations.of(context).loading, - ); - }, - )), + suffixWidget: Text(VersionService.getBuildNumber(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), ])), ], ), @@ -210,10 +168,6 @@ class _SettingsViewState extends State { ); } - Future _getPackageInfo() async { - return await PackageInfo.fromPlatform(); - } - void showFeedbackDialog(ImportStatus status) { if (status == ImportStatus.canceled) return; final (title, message) = _getDialogContent(status); diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart new file mode 100644 index 0000000..c23d187 --- /dev/null +++ b/lib/services/version_service.dart @@ -0,0 +1,32 @@ +import 'package:cabo_counter/utility/globals.dart'; +import 'package:package_info_plus/package_info_plus.dart'; + +class VersionService { + static String _version = '-.-.-'; + static String _buildNumber = '-'; + + static Future init() async { + var packageInfo = await PackageInfo.fromPlatform(); + _version = packageInfo.version; + _buildNumber = packageInfo.buildNumber; + } + + static String getVersionNumber() { + return _version; + } + + static String getVersion() { + if (_version == '-.-.-') { + return getVersionNumber(); + } + return '${Globals.appDevPhase} $_version'; + } + + static String getBuildNumber() { + return _buildNumber; + } + + static String getVersionWithBuild() { + return '$_version ($_buildNumber)'; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index fdf9916..414389d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+371 +version: 0.4.0+376 environment: sdk: ^3.5.4 From 55f3da8052c067b86650aa91d40e787f129c1a6a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:41:54 +0200 Subject: [PATCH 015/108] Updated strings, added wiki button --- lib/l10n/app_de.arb | 6 ++++-- lib/l10n/app_en.arb | 5 +++-- lib/l10n/app_localizations.dart | 14 ++++++++++---- lib/l10n/app_localizations_de.dart | 7 +++++-- lib/l10n/app_localizations_en.dart | 7 +++++-- lib/presentation/views/settings_view.dart | 18 +++++++++++++----- pubspec.yaml | 4 ++-- 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index b83c646..9c88d6a 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -82,7 +82,7 @@ "point_limit": "Punkte-Limit", "point_limit_subtitle": "... hier ist Schluss", "reset_to_default": "Auf Standard zurücksetzen", - "data": "Daten", + "game_data": "Spieldaten", "import_data": "Spieldaten importieren", "export_data": "Spieldaten exportieren", "app": "App", @@ -98,10 +98,12 @@ "export_error_title": "Fehler", "export_error_message": "Datei konnte nicht exportiert werden", + "error_found": "Fehler gefunden?", "create_issue": "Issue erstellen", + "wiki": "Wiki", "app_version": "App-Version", - "build": "Build", + "build": "Build-Nr.", "loading": "Lädt...", "about_text": "Hey :) Danke, dass du als eine:r der ersten User meiner ersten eigenen App dabei bist! Ich hab sehr viel Arbeit in dieses Projekt gesteckt und auch, wenn ich (hoffentlich) an vieles Gedacht hab, wird auf jeden Fall noch nicht alles 100% funktionieren. Solltest du also irgendwelche Fehler entdecken oder Feedback zum Design oder der Benutzerfreundlichkeit haben, teile Sie mir gern über die Testflight App oder auf den dir bekannten Wegen mit. Danke! " diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 1c111e2..60fc593 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -82,7 +82,7 @@ "point_limit": "Point Limit", "point_limit_subtitle": "... the game ends here.", "reset_to_default": "Reset to Default", - "data": "Data", + "game_data": "Game Data", "import_data": "Import Data", "export_data": "Export Data", "app": "App", @@ -100,9 +100,10 @@ "export_error_message": "Could not export file", "error_found": "Found a bug?", "create_issue": "Create Issue", + "wiki": "Wiki", "app_version": "App Version", "loading": "Loading...", - "build": "Build", + "build": "Build No.", "about_text": "Hey :) Thanks for being one of the first users of my app! I’ve put a lot of work into this project, and even though I tried to think of everything, it might not work perfectly just yet. So if you discover any bugs or have feedback on the design or usability, please let me know via the TestFlight app or by sending me a message or email. Thank you very much!" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 84295df..9f85aab 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -458,11 +458,11 @@ abstract class AppLocalizations { /// **'Auf Standard zurücksetzen'** String get reset_to_default; - /// No description provided for @data. + /// No description provided for @game_data. /// /// In de, this message translates to: - /// **'Daten'** - String get data; + /// **'Spieldaten'** + String get game_data; /// No description provided for @import_data. /// @@ -554,6 +554,12 @@ abstract class AppLocalizations { /// **'Issue erstellen'** String get create_issue; + /// No description provided for @wiki. + /// + /// In de, this message translates to: + /// **'Wiki'** + String get wiki; + /// No description provided for @app_version. /// /// In de, this message translates to: @@ -563,7 +569,7 @@ abstract class AppLocalizations { /// No description provided for @build. /// /// In de, this message translates to: - /// **'Build'** + /// **'Build-Nr.'** String get build; /// No description provided for @loading. diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index f4a2bbd..23b41ac 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -200,7 +200,7 @@ class AppLocalizationsDe extends AppLocalizations { String get reset_to_default => 'Auf Standard zurücksetzen'; @override - String get data => 'Daten'; + String get game_data => 'Spieldaten'; @override String get import_data => 'Spieldaten importieren'; @@ -250,11 +250,14 @@ class AppLocalizationsDe extends AppLocalizations { @override String get create_issue => 'Issue erstellen'; + @override + String get wiki => 'Wiki'; + @override String get app_version => 'App-Version'; @override - String get build => 'Build'; + String get build => 'Build-Nr.'; @override String get loading => 'Lädt...'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index aac55f2..eea3896 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -197,7 +197,7 @@ class AppLocalizationsEn extends AppLocalizations { String get reset_to_default => 'Reset to Default'; @override - String get data => 'Data'; + String get game_data => 'Game Data'; @override String get import_data => 'Import Data'; @@ -247,11 +247,14 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_issue => 'Create Issue'; + @override + String get wiki => 'Wiki'; + @override String get app_version => 'App Version'; @override - String get build => 'Build'; + String get build => 'Build No.'; @override String get loading => 'Loading...'; diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 2ed422f..0d32971 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -101,7 +101,7 @@ class _SettingsViewState extends State { Padding( padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), child: Text( - AppLocalizations.of(context).data, + AppLocalizations.of(context).game_data, style: CustomTheme.rowTitle, ), ), @@ -145,16 +145,24 @@ class _SettingsViewState extends State { suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( - prefixText: 'App-Version', - prefixIcon: CupertinoIcons.info, + prefixText: AppLocalizations.of(context).wiki, + prefixIcon: CupertinoIcons.book, + onPressed: () => launchUrl(Uri.parse( + 'https://github.com/flixcoo/Cabo-Counter/wiki')), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: + AppLocalizations.of(context).app_version, + prefixIcon: CupertinoIcons.number, onPressed: null, suffixWidget: Text(VersionService.getVersion(), style: TextStyle( color: CustomTheme.primaryColor, ))), CustomFormRow( - prefixText: 'Build-Nr.', - prefixIcon: CupertinoIcons.info, + prefixText: AppLocalizations.of(context).build, + prefixIcon: CupertinoIcons.number, onPressed: null, suffixWidget: Text(VersionService.getBuildNumber(), style: TextStyle( diff --git a/pubspec.yaml b/pubspec.yaml index 414389d..a692f21 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,8 @@ name: cabo_counter -description: "Mobile app for the card game Cabo" +description: "Mobile game_data for the card game Cabo" publish_to: 'none' -version: 0.4.0+376 +version: 0.4.0+378 environment: sdk: ^3.5.4 From 17072fb5844c263048f87f849a6eb308b1cb4b8e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 17:42:18 +0200 Subject: [PATCH 016/108] Corrected replaced string --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index a692f21..ce04a55 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: cabo_counter -description: "Mobile game_data for the card game Cabo" +description: "Mobile app for the card game Cabo" publish_to: 'none' version: 0.4.0+378 From fd8efb2a56464d8e818bee4891cecb93b63c6a48 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 18:46:58 +0200 Subject: [PATCH 017/108] Added import dialog feedback (got lost in refactoring) --- lib/presentation/views/settings_view.dart | 6 +++++- pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 0d32971..9b4f91a 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -114,7 +114,11 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: AppLocalizations.of(context).import_data, prefixIcon: CupertinoIcons.square_arrow_down, - onPressed: () => LocalStorageService.importJsonFile(), + onPressed: () async { + final status = + await LocalStorageService.importJsonFile(); + showFeedbackDialog(status); + }, suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( diff --git a/pubspec.yaml b/pubspec.yaml index ce04a55..e393fb9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+378 +version: 0.4.0+380 environment: sdk: ^3.5.4 From 42e51a092b7436d4737b9ad45f193395820d1ba0 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 18:49:13 +0200 Subject: [PATCH 018/108] Corrected function duplication --- lib/l10n/app_de.arb | 2 +- lib/l10n/app_en.arb | 1 + lib/presentation/views/settings_view.dart | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 9c88d6a..0a76696 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -93,7 +93,7 @@ "import_validation_error_message": "Es wurden keine Cabo-Counter Spieldaten gefunden. Bitte stellen Sie sicher, dass es sich um eine gültige Cabo-Counter Exportdatei handelt.", "import_format_error_title": "Falsches Format", "import_format_error_message": "Die Datei ist kein gültiges JSON-Format oder enthält ungültige Daten.", - "import_generic_error_title": "Import fehlgeschlagen", + "import_generic_error_title": "Import fehlgeschlagen", "import_generic_error_message": "Der Import ist fehlgeschlagen.", "export_error_title": "Fehler", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 60fc593..edca935 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -98,6 +98,7 @@ "export_error_title": "Export failed", "export_error_message": "Could not export file", + "error_found": "Found a bug?", "create_issue": "Create Issue", "wiki": "Wiki", diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 9b4f91a..7c1df2e 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -124,7 +124,7 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: AppLocalizations.of(context).export_data, prefixIcon: CupertinoIcons.square_arrow_up, - onPressed: () => LocalStorageService.importJsonFile(), + onPressed: () => LocalStorageService.exportGameData(), suffixWidget: const CupertinoListTileChevron(), ), ])), From 74a27aea24b6fda2979bb72db6c55e9aa4d425c9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 18:51:34 +0200 Subject: [PATCH 019/108] changed suffixWidget assignment and moved stepperKeys --- lib/presentation/views/settings_view.dart | 4 ++-- lib/presentation/widgets/custom_form_row.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 7c1df2e..72365ea 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -51,10 +51,10 @@ class _SettingsViewState extends State { margin: EdgeInsets.zero, children: [ CustomFormRow( - key: _stepperKey1, prefixText: 'Cabo-Strafe', prefixIcon: CupertinoIcons.minus_square, suffixWidget: Stepper( + key: _stepperKey1, initialValue: ConfigService.caboPenalty, minValue: 0, maxValue: 50, @@ -68,10 +68,10 @@ class _SettingsViewState extends State { ), ), CustomFormRow( - key: _stepperKey2, prefixText: 'Punkte-Limit', prefixIcon: FontAwesomeIcons.bullseye, suffixWidget: Stepper( + key: _stepperKey2, initialValue: ConfigService.pointLimit, minValue: 30, maxValue: 1000, diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart index f03b453..7a266ae 100644 --- a/lib/presentation/widgets/custom_form_row.dart +++ b/lib/presentation/widgets/custom_form_row.dart @@ -24,11 +24,11 @@ class _CustomFormRowState extends State { @override void initState() { super.initState(); - suffixWidget = widget.suffixWidget ?? const SizedBox.shrink(); } @override Widget build(BuildContext context) { + suffixWidget = widget.suffixWidget ?? const SizedBox.shrink(); return CupertinoButton( padding: EdgeInsets.zero, onPressed: widget.onPressed, diff --git a/pubspec.yaml b/pubspec.yaml index e393fb9..7b8621b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+380 +version: 0.4.0+381 environment: sdk: ^3.5.4 From d1ad5511f4682709d901b06a5792c0cac4a6b5f7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 19:01:12 +0200 Subject: [PATCH 020/108] Changed icons --- lib/presentation/views/settings_view.dart | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 72365ea..84916c2 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -52,7 +52,7 @@ class _SettingsViewState extends State { children: [ CustomFormRow( prefixText: 'Cabo-Strafe', - prefixIcon: CupertinoIcons.minus_square, + prefixIcon: CupertinoIcons.bolt_fill, suffixWidget: Stepper( key: _stepperKey1, initialValue: ConfigService.caboPenalty, @@ -158,7 +158,7 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: AppLocalizations.of(context).app_version, - prefixIcon: CupertinoIcons.number, + prefixIcon: CupertinoIcons.tag, onPressed: null, suffixWidget: Text(VersionService.getVersion(), style: TextStyle( diff --git a/pubspec.yaml b/pubspec.yaml index 7b8621b..5edeba0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+381 +version: 0.4.0+382 environment: sdk: ^3.5.4 From 68dbed1e560123882c05c4d725c6d4b7c555830c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 21:04:47 +0200 Subject: [PATCH 021/108] Added rate_my_app package --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 5edeba0..175eebe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: intl: any syncfusion_flutter_charts: ^30.1.37 uuid: ^4.5.1 + rate_my_app: ^2.3.2 dev_dependencies: flutter_test: From 61d844c289bd253c448bf21835c91ef4c265945e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 21:06:55 +0200 Subject: [PATCH 022/108] Renamed folder --- lib/{utility => core}/custom_theme.dart | 0 lib/{utility => core}/globals.dart | 0 lib/main.dart | 2 +- lib/presentation/views/active_game_view.dart | 2 +- lib/presentation/views/create_game_view.dart | 2 +- lib/presentation/views/main_menu_view.dart | 2 +- lib/presentation/views/mode_selection_view.dart | 2 +- lib/presentation/views/round_view.dart | 2 +- lib/presentation/views/settings_view.dart | 2 +- lib/presentation/views/tab_view.dart | 2 +- lib/presentation/widgets/custom_form_row.dart | 2 +- lib/presentation/widgets/stepper.dart | 2 +- lib/services/version_service.dart | 2 +- 13 files changed, 11 insertions(+), 11 deletions(-) rename lib/{utility => core}/custom_theme.dart (100%) rename lib/{utility => core}/globals.dart (100%) diff --git a/lib/utility/custom_theme.dart b/lib/core/custom_theme.dart similarity index 100% rename from lib/utility/custom_theme.dart rename to lib/core/custom_theme.dart diff --git a/lib/utility/globals.dart b/lib/core/globals.dart similarity index 100% rename from lib/utility/globals.dart rename to lib/core/globals.dart diff --git a/lib/main.dart b/lib/main.dart index b826072..2b6a163 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,9 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/services/version_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index ddc0299..37fd652 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; @@ -5,7 +6,6 @@ import 'package:cabo_counter/presentation/views/create_game_view.dart'; import 'package:cabo_counter/presentation/views/graph_view.dart'; import 'package:cabo_counter/presentation/views/round_view.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/presentation/views/create_game_view.dart b/lib/presentation/views/create_game_view.dart index 2c04e74..37123ca 100644 --- a/lib/presentation/views/create_game_view.dart +++ b/lib/presentation/views/create_game_view.dart @@ -1,10 +1,10 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; import 'package:cabo_counter/presentation/views/mode_selection_view.dart'; import 'package:cabo_counter/services/config_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; enum CreateStatus { diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 230c4de..425cf46 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,3 +1,4 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; @@ -5,7 +6,6 @@ import 'package:cabo_counter/presentation/views/create_game_view.dart'; import 'package:cabo_counter/presentation/views/settings_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; diff --git a/lib/presentation/views/mode_selection_view.dart b/lib/presentation/views/mode_selection_view.dart index 93cdc7a..cc2ac52 100644 --- a/lib/presentation/views/mode_selection_view.dart +++ b/lib/presentation/views/mode_selection_view.dart @@ -1,5 +1,5 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; class ModeSelectionMenu extends StatelessWidget { diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 4e114fe..4dfb45c 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -1,7 +1,7 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 84916c2..20c97ec 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,10 +1,10 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/services/version_service.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/presentation/views/tab_view.dart b/lib/presentation/views/tab_view.dart index efa4311..a3d06de 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,7 +1,7 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/information_view.dart'; import 'package:cabo_counter/presentation/views/main_menu_view.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; class TabView extends StatefulWidget { diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart index 7a266ae..96067c3 100644 --- a/lib/presentation/widgets/custom_form_row.dart +++ b/lib/presentation/widgets/custom_form_row.dart @@ -1,5 +1,5 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; class CustomFormRow extends StatefulWidget { diff --git a/lib/presentation/widgets/stepper.dart b/lib/presentation/widgets/stepper.dart index 8ca2635..5d0bce8 100644 --- a/lib/presentation/widgets/stepper.dart +++ b/lib/presentation/widgets/stepper.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/utility/custom_theme.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:flutter/cupertino.dart'; // Für iOS-Style class Stepper extends StatefulWidget { diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart index c23d187..b116d9b 100644 --- a/lib/services/version_service.dart +++ b/lib/services/version_service.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/core/globals.dart'; import 'package:package_info_plus/package_info_plus.dart'; class VersionService { From ceb0be123e239a2b88e25463d200e409fce20d52 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 22:04:54 +0200 Subject: [PATCH 023/108] Implement native rating dialog --- lib/core/globals.dart | 8 ++++++++ lib/presentation/views/main_menu_view.dart | 8 ++++++++ pubspec.yaml | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/core/globals.dart b/lib/core/globals.dart index e11a118..6bfd077 100644 --- a/lib/core/globals.dart +++ b/lib/core/globals.dart @@ -1,3 +1,11 @@ +import 'package:rate_my_app/rate_my_app.dart'; + class Globals { static String appDevPhase = 'Beta'; + static RateMyApp rateMyApp = RateMyApp( + appStoreIdentifier: '6747105718', + minDays: 15, + remindDays: 45, + minLaunches: 15, + remindLaunches: 40); } diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 425cf46..05665ce 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,4 +1,5 @@ import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/core/globals.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; @@ -29,6 +30,13 @@ class _MainMenuViewState extends State { }); }); gameManager.addListener(_updateView); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await Globals.rateMyApp.init(); + if (mounted && Globals.rateMyApp.shouldOpenDialog) { + Globals.rateMyApp.showStarRateDialog(context); + } + }); } void _updateView() { diff --git a/pubspec.yaml b/pubspec.yaml index 175eebe..92ed456 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+382 +version: 0.4.0+411 environment: sdk: ^3.5.4 From f66b725f791c18fad7588880c06d5ff03480c90c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 23:11:06 +0200 Subject: [PATCH 024/108] Implemented logic for pre rating and refactored rating dialog --- analysis_options.yaml | 3 +- lib/presentation/views/main_menu_view.dart | 68 +++++++++++++++++++++- pubspec.yaml | 2 +- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 2ce6b52..f9adae0 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -11,6 +11,5 @@ linter: prefer_const_literals_to_create_immutables: true unnecessary_const: true lines_longer_than_80_chars: false + constant_identifier_names: false -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 05665ce..ea330e5 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -9,6 +9,7 @@ import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart' as url; class MainMenuView extends StatefulWidget { const MainMenuView({super.key}); @@ -20,6 +21,9 @@ class MainMenuView extends StatefulWidget { class _MainMenuViewState extends State { bool _isLoading = true; + static const int RATING_DIALOG_YES = 1; + static const int RATING_DIALOG_NO = 0; + static const int RATING_DIALOG_CANCEL = -1; @override initState() { @@ -33,8 +37,11 @@ class _MainMenuViewState extends State { WidgetsBinding.instance.addPostFrameCallback((_) async { await Globals.rateMyApp.init(); - if (mounted && Globals.rateMyApp.shouldOpenDialog) { - Globals.rateMyApp.showStarRateDialog(context); + + if (Globals.rateMyApp.shouldOpenDialog) { + await Future.delayed(const Duration(milliseconds: 600)); + if (!mounted) return; + _handleFeedbackDialog(context); } }); } @@ -248,6 +255,63 @@ class _MainMenuViewState extends State { return shouldDelete; } + Future _showPreRatingDialog(BuildContext context) async { + int? answer = await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: const Text('Gefällt dir die App?'), + content: const Text('Dein Feedback hilft uns weiter.'), + actions: [ + CupertinoDialogAction( + child: const Text('Ja'), + onPressed: () { + Navigator.of(context).pop(RATING_DIALOG_YES); + }, + ), + CupertinoDialogAction( + child: const Text('Nein'), + onPressed: () { + Navigator.of(context).pop(RATING_DIALOG_NO); + }, + ), + CupertinoDialogAction( + isDestructiveAction: true, + onPressed: () { + Navigator.of(context).pop(RATING_DIALOG_CANCEL); + }, + child: Text(AppLocalizations.of(context).cancel), + ), + ], + ); + }, + ); + return answer ?? RATING_DIALOG_CANCEL; + } + + /// Handles the feedback dialog when the conditions for rating are met. + /// It shows a dialog asking the user if they like the app, + /// and based on their response, it either opens the rating dialog or an email client for feedback. + Future _handleFeedbackDialog(BuildContext context) async { + int decision = await _showPreRatingDialog(context); + + final Uri emailUri = Uri( + scheme: 'mailto', + path: 'cabo-counter@felixkirchner.de', + query: 'subject=Feedback: Cabo Counter App' + '&body=Ich habe folgendes Feedback...', + ); + + switch (decision) { + case RATING_DIALOG_YES: + Globals.rateMyApp.showStarRateDialog(context); + case RATING_DIALOG_NO: + url.launchUrl(emailUri, mode: url.LaunchMode.externalApplication); + case RATING_DIALOG_CANCEL: + break; + } + } + @override void dispose() { gameManager.removeListener(_updateView); diff --git a/pubspec.yaml b/pubspec.yaml index 92ed456..bb9ffcf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+411 +version: 0.4.0+437 environment: sdk: ^3.5.4 From 158e687f9f023ce5746377771ef5d33dc356dc06 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 23:40:22 +0200 Subject: [PATCH 025/108] updated launch mode --- lib/presentation/views/information_view.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/information_view.dart b/lib/presentation/views/information_view.dart index 1d0918b..2c5c739 100644 --- a/lib/presentation/views/information_view.dart +++ b/lib/presentation/views/information_view.dart @@ -53,16 +53,19 @@ class InformationView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( - onPressed: () => - launchUrl(Uri.parse('https://www.instagram.com/fx.kr')), + onPressed: () => launchUrl( + Uri.parse('https://www.instagram.com/flixcoo'), + mode: LaunchMode.externalApplication), icon: const Icon(FontAwesomeIcons.instagram)), IconButton( onPressed: () => launchUrl( - Uri.parse('mailto:felix.kirchner.fk@gmail.com')), + Uri.parse('mailto:felix.kirchner.fk@gmail.com'), + mode: LaunchMode.externalApplication), icon: const Icon(CupertinoIcons.envelope)), IconButton( - onPressed: () => - launchUrl(Uri.parse('https://www.github.com/flixcoo')), + onPressed: () => launchUrl( + Uri.parse('https://www.github.com/flixcoo'), + mode: LaunchMode.externalApplication), icon: const Icon(FontAwesomeIcons.github)), ], ), From 33c4b773dd991d35303a4c4a1e7150c9a98126e0 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 23:40:35 +0200 Subject: [PATCH 026/108] Small changes --- lib/presentation/views/main_menu_view.dart | 7 ++++--- pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index ea330e5..17813df 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -9,7 +9,7 @@ import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart' as url; +import 'package:url_launcher/url_launcher.dart'; class MainMenuView extends StatefulWidget { const MainMenuView({super.key}); @@ -304,9 +304,10 @@ class _MainMenuViewState extends State { switch (decision) { case RATING_DIALOG_YES: - Globals.rateMyApp.showStarRateDialog(context); + if (context.mounted) Globals.rateMyApp.showStarRateDialog(context); + break; case RATING_DIALOG_NO: - url.launchUrl(emailUri, mode: url.LaunchMode.externalApplication); + launchUrl(emailUri, mode: LaunchMode.externalApplication); case RATING_DIALOG_CANCEL: break; } diff --git a/pubspec.yaml b/pubspec.yaml index bb9ffcf..71d1689 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+437 +version: 0.4.0+438 environment: sdk: ^3.5.4 From 37c539607f30603b57ec02cb2e543bd97f0907c3 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 23:40:40 +0200 Subject: [PATCH 027/108] Updated launch mode --- lib/presentation/views/settings_view.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 20c97ec..5b2a331 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -144,15 +144,19 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: AppLocalizations.of(context).create_issue, prefixIcon: FontAwesomeIcons.github, - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/issues')), + onPressed: () => launchUrl( + Uri.parse( + 'https://github.com/flixcoo/Cabo-Counter/issues'), + mode: LaunchMode.externalApplication), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( prefixText: AppLocalizations.of(context).wiki, prefixIcon: CupertinoIcons.book, - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/wiki')), + onPressed: () => launchUrl( + Uri.parse( + 'https://github.com/flixcoo/Cabo-Counter/wiki'), + mode: LaunchMode.externalApplication), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( From bb6d718c9e7925d9a1d4a7f5fe98cefab734991e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 16:37:56 +0200 Subject: [PATCH 028/108] Updated linting rules --- analysis_options.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 2ce6b52..46e2dbe 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -11,6 +11,4 @@ linter: prefer_const_literals_to_create_immutables: true unnecessary_const: true lines_longer_than_80_chars: false - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + constant_identifier_names: false From e4dba146427c392d9db2a3d7009867980487c020 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 16:38:41 +0200 Subject: [PATCH 029/108] Renamed folders --- lib/main.dart | 2 +- lib/presentation/views/active_game_view.dart | 2 +- lib/presentation/views/create_game_view.dart | 2 +- lib/presentation/views/graph_view.dart | 2 +- lib/presentation/views/main_menu_view.dart | 2 +- .../views/mode_selection_view.dart | 2 +- lib/presentation/views/round_view.dart | 2 +- lib/presentation/views/settings_view.dart | 28 ++++++++++++------- lib/presentation/views/tab_view.dart | 2 +- lib/services/version_service.dart | 4 +-- lib/utility/constants.dart | 15 ++++++++++ lib/utility/globals.dart | 3 -- 12 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 lib/utility/constants.dart delete mode 100644 lib/utility/globals.dart diff --git a/lib/main.dart b/lib/main.dart index b826072..b714d58 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index ddc0299..effea40 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -1,6 +1,6 @@ import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/create_game_view.dart'; import 'package:cabo_counter/presentation/views/graph_view.dart'; import 'package:cabo_counter/presentation/views/round_view.dart'; diff --git a/lib/presentation/views/create_game_view.dart b/lib/presentation/views/create_game_view.dart index 2c04e74..6a19222 100644 --- a/lib/presentation/views/create_game_view.dart +++ b/lib/presentation/views/create_game_view.dart @@ -1,6 +1,6 @@ import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; import 'package:cabo_counter/presentation/views/mode_selection_view.dart'; import 'package:cabo_counter/services/config_service.dart'; diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 345c670..1007007 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -1,5 +1,5 @@ import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 230c4de..c1c14a9 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,5 +1,5 @@ import 'package:cabo_counter/data/game_manager.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; import 'package:cabo_counter/presentation/views/create_game_view.dart'; import 'package:cabo_counter/presentation/views/settings_view.dart'; diff --git a/lib/presentation/views/mode_selection_view.dart b/lib/presentation/views/mode_selection_view.dart index 93cdc7a..547b0c7 100644 --- a/lib/presentation/views/mode_selection_view.dart +++ b/lib/presentation/views/mode_selection_view.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 4e114fe..a989139 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -1,5 +1,5 @@ import 'package:cabo_counter/data/game_session.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 84916c2..41532f3 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,9 +1,10 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/services/version_service.dart'; +import 'package:cabo_counter/utility/constants.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -93,7 +94,6 @@ class _SettingsViewState extends State { setState(() { _stepperKey1 = UniqueKey(); _stepperKey2 = UniqueKey(); - print('Config reset to default'); }); }, ) @@ -142,17 +142,25 @@ class _SettingsViewState extends State { margin: EdgeInsets.zero, children: [ CustomFormRow( - prefixText: AppLocalizations.of(context).create_issue, - prefixIcon: FontAwesomeIcons.github, - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/issues')), + prefixText: AppLocalizations.of(context).wiki, + prefixIcon: CupertinoIcons.book, + onPressed: () => + launchUrl(Uri.parse(Constants.GITHUB_WIKI_LINK)), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( - prefixText: AppLocalizations.of(context).wiki, - prefixIcon: CupertinoIcons.book, - onPressed: () => launchUrl(Uri.parse( - 'https://github.com/flixcoo/Cabo-Counter/wiki')), + prefixText: + AppLocalizations.of(context).privacy_policy, + prefixIcon: CupertinoIcons.doc_append, + onPressed: () => launchUrl( + Uri.parse(Constants.PRIVACY_POLICY_LINK)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).error_found, + prefixIcon: FontAwesomeIcons.github, + onPressed: () => launchUrl( + Uri.parse(Constants.GITHUB_ISSUES_LINK)), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( diff --git a/lib/presentation/views/tab_view.dart b/lib/presentation/views/tab_view.dart index efa4311..999a90e 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/information_view.dart'; import 'package:cabo_counter/presentation/views/main_menu_view.dart'; import 'package:cabo_counter/utility/custom_theme.dart'; diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart index c23d187..7c306e3 100644 --- a/lib/services/version_service.dart +++ b/lib/services/version_service.dart @@ -1,4 +1,4 @@ -import 'package:cabo_counter/utility/globals.dart'; +import 'package:cabo_counter/utility/constants.dart'; import 'package:package_info_plus/package_info_plus.dart'; class VersionService { @@ -19,7 +19,7 @@ class VersionService { if (_version == '-.-.-') { return getVersionNumber(); } - return '${Globals.appDevPhase} $_version'; + return '${Constants.appDevPhase} $_version'; } static String getBuildNumber() { diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart new file mode 100644 index 0000000..215de73 --- /dev/null +++ b/lib/utility/constants.dart @@ -0,0 +1,15 @@ +import 'dart:core'; + +class Constants { + static String appDevPhase = 'Beta'; + static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; + static const String GITHUB_LINK = 'https://github.felixkirchner.de'; + static const String GITHUB_REPO_LINK = 'https://github.felixkirchner.de'; + static const String GITHUB_ISSUES_LINK = + 'https://github.com/flixcoo/Cabo-Counter/issues'; + static const String GITHUB_WIKI_LINK = + 'https://github.com/flixcoo/Cabo-Counter/issues'; + static const String EMAIL = 'cabocounter@felixkirchner.de'; + static const String PRIVACY_POLICY_LINK = + 'https://github.felixkirchner.de/Cabo-Counter/wiki/Privacy-Policy'; +} diff --git a/lib/utility/globals.dart b/lib/utility/globals.dart deleted file mode 100644 index e11a118..0000000 --- a/lib/utility/globals.dart +++ /dev/null @@ -1,3 +0,0 @@ -class Globals { - static String appDevPhase = 'Beta'; -} From b98b5b0ac99cd7bf80b1e67c3edf4bd5dae2f7fc Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 16:38:56 +0200 Subject: [PATCH 030/108] Changed l10n files location --- l10n.yaml | 7 ++++--- lib/l10n/{ => arb}/untranslated_messages.json | 0 lib/l10n/{ => generated}/app_localizations.dart | 8 +++++++- lib/l10n/{ => generated}/app_localizations_de.dart | 3 +++ lib/l10n/{ => generated}/app_localizations_en.dart | 3 +++ 5 files changed, 17 insertions(+), 4 deletions(-) rename lib/l10n/{ => arb}/untranslated_messages.json (100%) rename lib/l10n/{ => generated}/app_localizations.dart (98%) rename lib/l10n/{ => generated}/app_localizations_de.dart (98%) rename lib/l10n/{ => generated}/app_localizations_en.dart (98%) diff --git a/l10n.yaml b/l10n.yaml index 239fdc6..f69305d 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,5 +1,6 @@ -arb-dir: lib/l10n +arb-dir: lib/l10n/arb template-arb-file: app_de.arb -untranslated-messages-file: lib/l10n/untranslated_messages.json +untranslated-messages-file: lib/l10n/arb/untranslated_messages.json nullable-getter: false -output-localization-file: app_localizations.dart \ No newline at end of file +output-localization-file: app_localizations.dart +output-dir: lib/l10n/generated \ No newline at end of file diff --git a/lib/l10n/untranslated_messages.json b/lib/l10n/arb/untranslated_messages.json similarity index 100% rename from lib/l10n/untranslated_messages.json rename to lib/l10n/arb/untranslated_messages.json diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/generated/app_localizations.dart similarity index 98% rename from lib/l10n/app_localizations.dart rename to lib/l10n/generated/app_localizations.dart index 9f85aab..ff14253 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -18,7 +18,7 @@ import 'app_localizations_en.dart'; /// `supportedLocales` list. For example: /// /// ```dart -/// import 'l10n/app_localizations.dart'; +/// import 'generated/app_localizations.dart'; /// /// return MaterialApp( /// localizationsDelegates: AppLocalizations.localizationsDelegates, @@ -566,6 +566,12 @@ abstract class AppLocalizations { /// **'App-Version'** String get app_version; + /// No description provided for @privacy_policy. + /// + /// In de, this message translates to: + /// **'Datenschutzerklärung'** + String get privacy_policy; + /// No description provided for @build. /// /// In de, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart similarity index 98% rename from lib/l10n/app_localizations_de.dart rename to lib/l10n/generated/app_localizations_de.dart index 23b41ac..898df64 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -256,6 +256,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get app_version => 'App-Version'; + @override + String get privacy_policy => 'Datenschutzerklärung'; + @override String get build => 'Build-Nr.'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart similarity index 98% rename from lib/l10n/app_localizations_en.dart rename to lib/l10n/generated/app_localizations_en.dart index eea3896..c057432 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -253,6 +253,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get app_version => 'App Version'; + @override + String get privacy_policy => 'Privacy Policy'; + @override String get build => 'Build No.'; From 38d5151a3e86b38be678a8f4f026daade49b60b5 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 16:39:12 +0200 Subject: [PATCH 031/108] Implemented new link constants --- lib/l10n/{ => arb}/app_de.arb | 1 + lib/l10n/{ => arb}/app_en.arb | 1 + lib/presentation/views/information_view.dart | 11 ++++++----- pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) rename lib/l10n/{ => arb}/app_de.arb (99%) rename lib/l10n/{ => arb}/app_en.arb (99%) diff --git a/lib/l10n/app_de.arb b/lib/l10n/arb/app_de.arb similarity index 99% rename from lib/l10n/app_de.arb rename to lib/l10n/arb/app_de.arb index 0a76696..7615b27 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -103,6 +103,7 @@ "create_issue": "Issue erstellen", "wiki": "Wiki", "app_version": "App-Version", + "privacy_policy": "Datenschutzerklärung", "build": "Build-Nr.", "loading": "Lädt...", diff --git a/lib/l10n/app_en.arb b/lib/l10n/arb/app_en.arb similarity index 99% rename from lib/l10n/app_en.arb rename to lib/l10n/arb/app_en.arb index edca935..ee01699 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -103,6 +103,7 @@ "create_issue": "Create Issue", "wiki": "Wiki", "app_version": "App Version", + "privacy_policy": "Privacy Policy", "loading": "Loading...", "build": "Build No.", diff --git a/lib/presentation/views/information_view.dart b/lib/presentation/views/information_view.dart index 1d0918b..d31d7f6 100644 --- a/lib/presentation/views/information_view.dart +++ b/lib/presentation/views/information_view.dart @@ -1,4 +1,5 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/utility/constants.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -54,15 +55,15 @@ class InformationView extends StatelessWidget { children: [ IconButton( onPressed: () => - launchUrl(Uri.parse('https://www.instagram.com/fx.kr')), + launchUrl(Uri.parse(Constants.INSTAGRAM_LINK)), icon: const Icon(FontAwesomeIcons.instagram)), IconButton( - onPressed: () => launchUrl( - Uri.parse('mailto:felix.kirchner.fk@gmail.com')), + onPressed: () => + launchUrl(Uri.parse('mailto:${Constants.EMAIL}')), icon: const Icon(CupertinoIcons.envelope)), IconButton( onPressed: () => - launchUrl(Uri.parse('https://www.github.com/flixcoo')), + launchUrl(Uri.parse(Constants.GITHUB_LINK)), icon: const Icon(FontAwesomeIcons.github)), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 5edeba0..c24fb24 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+382 +version: 0.4.0+386 environment: sdk: ^3.5.4 From 08917f18b0a5f475385f49ae2c839a17b33f990d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 16:40:09 +0200 Subject: [PATCH 032/108] Changed privacy policy link --- lib/utility/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart index 215de73..f4f0c0f 100644 --- a/lib/utility/constants.dart +++ b/lib/utility/constants.dart @@ -11,5 +11,5 @@ class Constants { 'https://github.com/flixcoo/Cabo-Counter/issues'; static const String EMAIL = 'cabocounter@felixkirchner.de'; static const String PRIVACY_POLICY_LINK = - 'https://github.felixkirchner.de/Cabo-Counter/wiki/Privacy-Policy'; + 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; } From ef2c9065155ecf79e74b47aa8607639d15deb57a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:15:38 +0200 Subject: [PATCH 033/108] Corrected wiki link --- lib/utility/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart index f4f0c0f..d8562d5 100644 --- a/lib/utility/constants.dart +++ b/lib/utility/constants.dart @@ -8,7 +8,7 @@ class Constants { static const String GITHUB_ISSUES_LINK = 'https://github.com/flixcoo/Cabo-Counter/issues'; static const String GITHUB_WIKI_LINK = - 'https://github.com/flixcoo/Cabo-Counter/issues'; + 'https://github.com/flixcoo/Cabo-Counter/wiki'; static const String EMAIL = 'cabocounter@felixkirchner.de'; static const String PRIVACY_POLICY_LINK = 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; From 3d63fd13a8c42be2f72b7ca702763f340dc8af26 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:15:58 +0200 Subject: [PATCH 034/108] Removed import --- lib/utility/constants.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart index d8562d5..329642e 100644 --- a/lib/utility/constants.dart +++ b/lib/utility/constants.dart @@ -1,5 +1,3 @@ -import 'dart:core'; - class Constants { static String appDevPhase = 'Beta'; static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; From 5b3b84069d66e5e9563f63169c5ca86f064c301a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:23:08 +0200 Subject: [PATCH 035/108] Updated links --- lib/utility/constants.dart | 3 +-- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart index 329642e..fd6ebbc 100644 --- a/lib/utility/constants.dart +++ b/lib/utility/constants.dart @@ -1,8 +1,7 @@ class Constants { static String appDevPhase = 'Beta'; static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; - static const String GITHUB_LINK = 'https://github.felixkirchner.de'; - static const String GITHUB_REPO_LINK = 'https://github.felixkirchner.de'; + static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; static const String GITHUB_ISSUES_LINK = 'https://github.com/flixcoo/Cabo-Counter/issues'; static const String GITHUB_WIKI_LINK = diff --git a/pubspec.yaml b/pubspec.yaml index c24fb24..5faec22 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+386 +version: 0.4.0+387 environment: sdk: ^3.5.4 From 16b6dbb150ca5448a14a0f4f5d1f9131bcda5c43 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:24:50 +0200 Subject: [PATCH 036/108] Updated links to subdomains --- lib/utility/constants.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/utility/constants.dart b/lib/utility/constants.dart index fd6ebbc..4a79399 100644 --- a/lib/utility/constants.dart +++ b/lib/utility/constants.dart @@ -3,9 +3,8 @@ class Constants { static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; static const String GITHUB_ISSUES_LINK = - 'https://github.com/flixcoo/Cabo-Counter/issues'; - static const String GITHUB_WIKI_LINK = - 'https://github.com/flixcoo/Cabo-Counter/wiki'; + 'cabocounter-issues.felixkirchner.de'; + static const String GITHUB_WIKI_LINK = 'cabocounter-wiki.felixkirchner.de '; static const String EMAIL = 'cabocounter@felixkirchner.de'; static const String PRIVACY_POLICY_LINK = 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; From 7b787a6a4f4b1b2bdedf65c62ed25e43868b8cee Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:31:07 +0200 Subject: [PATCH 037/108] Updated file paths --- lib/core/constants.dart | 10 ++++++++++ lib/core/globals.dart | 11 ----------- lib/main.dart | 1 - lib/presentation/views/information_view.dart | 2 +- lib/presentation/views/main_menu_view.dart | 8 ++++---- lib/presentation/views/mode_selection_view.dart | 2 -- lib/presentation/views/settings_view.dart | 4 +--- lib/presentation/views/tab_view.dart | 1 - lib/services/version_service.dart | 3 +-- 9 files changed, 17 insertions(+), 25 deletions(-) delete mode 100644 lib/core/globals.dart diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 4a79399..ce8c30a 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -1,5 +1,8 @@ +import 'package:rate_my_app/rate_my_app.dart'; + class Constants { static String appDevPhase = 'Beta'; + static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; static const String GITHUB_ISSUES_LINK = @@ -8,4 +11,11 @@ class Constants { static const String EMAIL = 'cabocounter@felixkirchner.de'; static const String PRIVACY_POLICY_LINK = 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; + + static RateMyApp rateMyApp = RateMyApp( + appStoreIdentifier: '6747105718', + minDays: 15, + remindDays: 45, + minLaunches: 15, + remindLaunches: 40); } diff --git a/lib/core/globals.dart b/lib/core/globals.dart deleted file mode 100644 index 6bfd077..0000000 --- a/lib/core/globals.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:rate_my_app/rate_my_app.dart'; - -class Globals { - static String appDevPhase = 'Beta'; - static RateMyApp rateMyApp = RateMyApp( - appStoreIdentifier: '6747105718', - minDays: 15, - remindDays: 45, - minLaunches: 15, - remindLaunches: 40); -} diff --git a/lib/main.dart b/lib/main.dart index 4002dda..9279426 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,4 @@ import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/tab_view.dart'; import 'package:cabo_counter/services/config_service.dart'; diff --git a/lib/presentation/views/information_view.dart b/lib/presentation/views/information_view.dart index d31d7f6..712d709 100644 --- a/lib/presentation/views/information_view.dart +++ b/lib/presentation/views/information_view.dart @@ -1,5 +1,5 @@ +import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; -import 'package:cabo_counter/utility/constants.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 925b9ed..fee5c72 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,5 +1,5 @@ +import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/core/globals.dart'; import 'package:cabo_counter/data/game_manager.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/active_game_view.dart'; @@ -36,9 +36,9 @@ class _MainMenuViewState extends State { gameManager.addListener(_updateView); WidgetsBinding.instance.addPostFrameCallback((_) async { - await Globals.rateMyApp.init(); + await Constants.rateMyApp.init(); - if (Globals.rateMyApp.shouldOpenDialog) { + if (Constants.rateMyApp.shouldOpenDialog) { await Future.delayed(const Duration(milliseconds: 600)); if (!mounted) return; _handleFeedbackDialog(context); @@ -304,7 +304,7 @@ class _MainMenuViewState extends State { switch (decision) { case RATING_DIALOG_YES: - if (context.mounted) Globals.rateMyApp.showStarRateDialog(context); + if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); break; case RATING_DIALOG_NO: launchUrl(emailUri, mode: LaunchMode.externalApplication); diff --git a/lib/presentation/views/mode_selection_view.dart b/lib/presentation/views/mode_selection_view.dart index 67ab5fb..a7d3ce7 100644 --- a/lib/presentation/views/mode_selection_view.dart +++ b/lib/presentation/views/mode_selection_view.dart @@ -1,7 +1,5 @@ import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; class ModeSelectionMenu extends StatelessWidget { diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 2f55ba3..c9a409d 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,13 +1,11 @@ +import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; import 'package:cabo_counter/presentation/widgets/stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/services/version_service.dart'; -import 'package:cabo_counter/utility/constants.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/lib/presentation/views/tab_view.dart b/lib/presentation/views/tab_view.dart index 1fd84d2..15d4637 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,5 +1,4 @@ import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/l10n/app_localizations.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/views/information_view.dart'; import 'package:cabo_counter/presentation/views/main_menu_view.dart'; diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart index ade934d..6511c69 100644 --- a/lib/services/version_service.dart +++ b/lib/services/version_service.dart @@ -1,5 +1,4 @@ -import 'package:cabo_counter/core/globals.dart'; -import 'package:cabo_counter/utility/constants.dart'; +import 'package:cabo_counter/core/constants.dart'; import 'package:package_info_plus/package_info_plus.dart'; class VersionService { From d5d318929e2b601d20095c4d1b1730c6b4611ed9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:42:05 +0200 Subject: [PATCH 038/108] Updated strings --- lib/l10n/arb/app_de.arb | 5 ++++ lib/l10n/arb/app_en.arb | 5 ++++ lib/l10n/generated/app_localizations.dart | 24 ++++++++++++++++++++ lib/l10n/generated/app_localizations_de.dart | 13 +++++++++++ lib/l10n/generated/app_localizations_en.dart | 13 +++++++++++ lib/presentation/views/main_menu_view.dart | 8 +++---- pubspec.yaml | 2 +- 7 files changed, 65 insertions(+), 5 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 7615b27..64a6d02 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -30,6 +30,11 @@ } } }, + "rating_title": "Gefällt dir die App?", + "rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!", + "yes": "Ja", + "no": "Nein", + "overview": "Übersicht", "new_game": "Neues Spiel", "game_title": "Titel des Spiels", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index ee01699..805a178 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -30,6 +30,11 @@ } } }, + "rating_title": "Do you like the app?", + "rating_message": "Feedback helps me to continuously improve the app. Thank you!", + "yes": "Yes", + "no": "No", + "overview": "Overview", "new_game": "New Game", "game_title": "Game Title", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index ff14253..c349078 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -218,6 +218,30 @@ abstract class AppLocalizations { /// **'Bist du sicher, dass du das Spiel \"{gameTitle}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** String delete_game_message(String gameTitle); + /// No description provided for @rating_title. + /// + /// In de, this message translates to: + /// **'Gefällt dir die App?'** + String get rating_title; + + /// No description provided for @rating_message. + /// + /// In de, this message translates to: + /// **'Feedback hilft mir, die App zu verbessern. Vielen Dank!'** + String get rating_message; + + /// No description provided for @yes. + /// + /// In de, this message translates to: + /// **'Ja'** + String get yes; + + /// No description provided for @no. + /// + /// In de, this message translates to: + /// **'Nein'** + String get no; + /// No description provided for @overview. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 898df64..d6fcf72 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -71,6 +71,19 @@ class AppLocalizationsDe extends AppLocalizations { return 'Bist du sicher, dass du das Spiel \"$gameTitle\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; } + @override + String get rating_title => 'Gefällt dir die App?'; + + @override + String get rating_message => + 'Feedback hilft mir, die App zu verbessern. Vielen Dank!'; + + @override + String get yes => 'Ja'; + + @override + String get no => 'Nein'; + @override String get overview => 'Übersicht'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index c057432..4adaa4b 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -71,6 +71,19 @@ class AppLocalizationsEn extends AppLocalizations { return 'Are you sure you want to delete the game \"$gameTitle\"? This action cannot be undone.'; } + @override + String get rating_title => 'Do you like the app?'; + + @override + String get rating_message => + 'Feedback helps me to continuously improve the app. Thank you!'; + + @override + String get yes => 'Yes'; + + @override + String get no => 'No'; + @override String get overview => 'Overview'; diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index fee5c72..bf417f7 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -260,17 +260,17 @@ class _MainMenuViewState extends State { context: context, builder: (BuildContext context) { return CupertinoAlertDialog( - title: const Text('Gefällt dir die App?'), - content: const Text('Dein Feedback hilft uns weiter.'), + title: Text(AppLocalizations.of(context).rating_title), + content: Text(AppLocalizations.of(context).rating_message), actions: [ CupertinoDialogAction( - child: const Text('Ja'), + child: Text(AppLocalizations.of(context).yes), onPressed: () { Navigator.of(context).pop(RATING_DIALOG_YES); }, ), CupertinoDialogAction( - child: const Text('Nein'), + child: Text(AppLocalizations.of(context).no), onPressed: () { Navigator.of(context).pop(RATING_DIALOG_NO); }, diff --git a/pubspec.yaml b/pubspec.yaml index 35eac0b..f0d4ba4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+443 +version: 0.4.0+451 environment: sdk: ^3.5.4 From 08c2b1d91a1d91f20b63ec4db506cd6a10fdbc9e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:42:47 +0200 Subject: [PATCH 039/108] Updated identifiers --- lib/l10n/arb/app_de.arb | 4 ++-- lib/l10n/arb/app_en.arb | 4 ++-- lib/l10n/generated/app_localizations.dart | 8 ++++---- lib/l10n/generated/app_localizations_de.dart | 4 ++-- lib/l10n/generated/app_localizations_en.dart | 4 ++-- lib/presentation/views/main_menu_view.dart | 4 ++-- pubspec.yaml | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 64a6d02..c4f5692 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -30,8 +30,8 @@ } } }, - "rating_title": "Gefällt dir die App?", - "rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!", + "pre_rating_title": "Gefällt dir die App?", + "pre_rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!", "yes": "Ja", "no": "Nein", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 805a178..d5ccd1a 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -30,8 +30,8 @@ } } }, - "rating_title": "Do you like the app?", - "rating_message": "Feedback helps me to continuously improve the app. Thank you!", + "pre_rating_title": "Do you like the app?", + "pre_rating_message": "Feedback helps me to continuously improve the app. Thank you!", "yes": "Yes", "no": "No", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index c349078..74df468 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -218,17 +218,17 @@ abstract class AppLocalizations { /// **'Bist du sicher, dass du das Spiel \"{gameTitle}\" löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** String delete_game_message(String gameTitle); - /// No description provided for @rating_title. + /// No description provided for @pre_rating_title. /// /// In de, this message translates to: /// **'Gefällt dir die App?'** - String get rating_title; + String get pre_rating_title; - /// No description provided for @rating_message. + /// No description provided for @pre_rating_message. /// /// In de, this message translates to: /// **'Feedback hilft mir, die App zu verbessern. Vielen Dank!'** - String get rating_message; + String get pre_rating_message; /// No description provided for @yes. /// diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index d6fcf72..ef180b1 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -72,10 +72,10 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get rating_title => 'Gefällt dir die App?'; + String get pre_rating_title => 'Gefällt dir die App?'; @override - String get rating_message => + String get pre_rating_message => 'Feedback hilft mir, die App zu verbessern. Vielen Dank!'; @override diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 4adaa4b..12d45bc 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -72,10 +72,10 @@ class AppLocalizationsEn extends AppLocalizations { } @override - String get rating_title => 'Do you like the app?'; + String get pre_rating_title => 'Do you like the app?'; @override - String get rating_message => + String get pre_rating_message => 'Feedback helps me to continuously improve the app. Thank you!'; @override diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index bf417f7..166c48e 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -260,8 +260,8 @@ class _MainMenuViewState extends State { context: context, builder: (BuildContext context) { return CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).rating_title), - content: Text(AppLocalizations.of(context).rating_message), + title: Text(AppLocalizations.of(context).pre_rating_title), + content: Text(AppLocalizations.of(context).pre_rating_message), actions: [ CupertinoDialogAction( child: Text(AppLocalizations.of(context).yes), diff --git a/pubspec.yaml b/pubspec.yaml index f0d4ba4..795551a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+451 +version: 0.4.0+452 environment: sdk: ^3.5.4 From 4a918fbbdb04514280fb0712ecab9f38de05b344 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 17:43:09 +0200 Subject: [PATCH 040/108] Added break in switch case --- lib/presentation/views/main_menu_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 166c48e..085a209 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -308,6 +308,7 @@ class _MainMenuViewState extends State { break; case RATING_DIALOG_NO: launchUrl(emailUri, mode: LaunchMode.externalApplication); + break; case RATING_DIALOG_CANCEL: break; } From 9519a6a531c0fedcee1761f4b81c63194f18f017 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 18:51:37 +0200 Subject: [PATCH 041/108] Updated strings --- lib/l10n/arb/app_de.arb | 5 ++++ lib/l10n/arb/app_en.arb | 5 ++++ lib/l10n/generated/app_localizations.dart | 30 ++++++++++++++++++++ lib/l10n/generated/app_localizations_de.dart | 16 +++++++++++ lib/l10n/generated/app_localizations_en.dart | 16 +++++++++++ 5 files changed, 72 insertions(+) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index c4f5692..d89399b 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -34,6 +34,11 @@ "pre_rating_message": "Feedback hilft mir, die App zu verbessern. Vielen Dank!", "yes": "Ja", "no": "Nein", + "bad_rating_title": "Unzufrieden mit der App?", + "bad_rating_message": "Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!", + "contact_email": "E-Mail schreiben", + "email_subject": "Feedback: Cabo Counter App", + "email_body": "Ich habe folgendes Feedback...", "overview": "Übersicht", "new_game": "Neues Spiel", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index d5ccd1a..3f24635 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -34,6 +34,11 @@ "pre_rating_message": "Feedback helps me to continuously improve the app. Thank you!", "yes": "Yes", "no": "No", + "bad_rating_title": "Not satisfied?", + "bad_rating_message": "If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.", + "contact_email": "Contac vía E-Mail", + "email_subject": "Feedback: Cabo Counter App", + "email_body": "I have the following feedback...", "overview": "Overview", "new_game": "New Game", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 74df468..af778ae 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -242,6 +242,36 @@ abstract class AppLocalizations { /// **'Nein'** String get no; + /// No description provided for @bad_rating_title. + /// + /// In de, this message translates to: + /// **'Unzufrieden mit der App?'** + String get bad_rating_title; + + /// No description provided for @bad_rating_message. + /// + /// In de, this message translates to: + /// **'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!'** + String get bad_rating_message; + + /// No description provided for @contact_email. + /// + /// In de, this message translates to: + /// **'E-Mail schreiben'** + String get contact_email; + + /// No description provided for @email_subject. + /// + /// In de, this message translates to: + /// **'Feedback: Cabo Counter App'** + String get email_subject; + + /// No description provided for @email_body. + /// + /// In de, this message translates to: + /// **'Ich habe folgendes Feedback...'** + String get email_body; + /// No description provided for @overview. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index ef180b1..afe558f 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -84,6 +84,22 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no => 'Nein'; + @override + String get bad_rating_title => 'Unzufrieden mit der App?'; + + @override + String get bad_rating_message => + 'Schreib mir gerne direkt eine E-Mail, damit wir dein Problem lösen können!'; + + @override + String get contact_email => 'E-Mail schreiben'; + + @override + String get email_subject => 'Feedback: Cabo Counter App'; + + @override + String get email_body => 'Ich habe folgendes Feedback...'; + @override String get overview => 'Übersicht'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 12d45bc..d3e433b 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -84,6 +84,22 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no => 'No'; + @override + String get bad_rating_title => 'Not satisfied?'; + + @override + String get bad_rating_message => + 'If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.'; + + @override + String get contact_email => 'Contac vía E-Mail'; + + @override + String get email_subject => 'Feedback: Cabo Counter App'; + + @override + String get email_body => 'I have the following feedback...'; + @override String get overview => 'Overview'; From 6ca7f2d9780ed279f09826ad44b7e4e5ff785627 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 18:51:52 +0200 Subject: [PATCH 042/108] Implemented new popup --- lib/presentation/views/main_menu_view.dart | 117 +++++++++++++++------ 1 file changed, 86 insertions(+), 31 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 085a209..05c4a07 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -21,9 +21,11 @@ class MainMenuView extends StatefulWidget { class _MainMenuViewState extends State { bool _isLoading = true; - static const int RATING_DIALOG_YES = 1; - static const int RATING_DIALOG_NO = 0; - static const int RATING_DIALOG_CANCEL = -1; + static const int PRE_RATING_DIALOG_YES = 10; + static const int PRE_RATING_DIALOG_NO = 11; + static const int PRE_RATING_DIALOG_CANCEL = 12; + static const int BAD_RATING_DIALOG_EMAIL = 20; + static const int BAD_RATING_DIALOG_CANCEL = 21; @override initState() { @@ -94,13 +96,15 @@ class _MainMenuViewState extends State { const SizedBox(height: 30), // Abstand von oben Center( child: GestureDetector( - onTap: () => Navigator.push( + onTap: () => _handleFeedbackDialog( + context) /*Navigator.push( context, CupertinoPageRoute( builder: (context) => const CreateGameView(), ), - ), + )*/ + , child: Icon( CupertinoIcons.plus, size: 60, @@ -255,6 +259,48 @@ class _MainMenuViewState extends State { return shouldDelete; } + /// Handles the feedback dialog when the conditions for rating are met. + /// It shows a dialog asking the user if they like the app, + /// and based on their response, it either opens the rating dialog or an email client for feedback. + Future _handleFeedbackDialog(BuildContext context) async { + final String emailSubject = AppLocalizations.of(context).email_subject; + final String emailBody = AppLocalizations.of(context).email_body; + + final Uri emailUri = Uri( + scheme: 'mailto', + path: Constants.EMAIL, + query: 'subject=$emailSubject' + '&body=$emailBody', + ); + + int decision = await _showPreRatingDialog(context); + int decision2 = 0; + + // so that the bad rating dialog is not shown immediately + await Future.delayed(const Duration(milliseconds: 300)); + + switch (decision) { + case PRE_RATING_DIALOG_YES: + if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); + break; + case PRE_RATING_DIALOG_NO: + if (context.mounted) decision2 = await _showBadRatingDialog(context); + if (decision2 == BAD_RATING_DIALOG_EMAIL) { + if (context.mounted) { + launchUrl(emailUri); + } + } + break; + case PRE_RATING_DIALOG_CANCEL: + break; + } + } + + /// Shows a dialog asking the user if they like the app. + /// Returns the user's decision as an integer. + /// - PRE_RATING_DIALOG_YES: User likes the app and wants to rate it. + /// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback. + /// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog. Future _showPreRatingDialog(BuildContext context) async { int? answer = await showCupertinoDialog( context: context, @@ -266,19 +312,19 @@ class _MainMenuViewState extends State { CupertinoDialogAction( child: Text(AppLocalizations.of(context).yes), onPressed: () { - Navigator.of(context).pop(RATING_DIALOG_YES); + Navigator.of(context).pop(PRE_RATING_DIALOG_YES); }, ), CupertinoDialogAction( child: Text(AppLocalizations.of(context).no), onPressed: () { - Navigator.of(context).pop(RATING_DIALOG_NO); + Navigator.of(context).pop(PRE_RATING_DIALOG_NO); }, ), CupertinoDialogAction( isDestructiveAction: true, onPressed: () { - Navigator.of(context).pop(RATING_DIALOG_CANCEL); + Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL); }, child: Text(AppLocalizations.of(context).cancel), ), @@ -286,32 +332,41 @@ class _MainMenuViewState extends State { ); }, ); - return answer ?? RATING_DIALOG_CANCEL; + return answer ?? PRE_RATING_DIALOG_CANCEL; } - /// Handles the feedback dialog when the conditions for rating are met. - /// It shows a dialog asking the user if they like the app, - /// and based on their response, it either opens the rating dialog or an email client for feedback. - Future _handleFeedbackDialog(BuildContext context) async { - int decision = await _showPreRatingDialog(context); - - final Uri emailUri = Uri( - scheme: 'mailto', - path: 'cabo-counter@felixkirchner.de', - query: 'subject=Feedback: Cabo Counter App' - '&body=Ich habe folgendes Feedback...', + /// Shows a dialog asking the user for feedback if they do not like the app. + /// Returns the user's decision as an integer. + /// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback. + /// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog. + Future _showBadRatingDialog(BuildContext context) async { + int? answer = await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).bad_rating_title), + content: Text(AppLocalizations.of(context).bad_rating_message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).contact_email, + style: const TextStyle(fontWeight: FontWeight.bold)), + onPressed: () { + Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL); + }, + ), + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel, + style: + const TextStyle(color: CupertinoColors.destructiveRed)), + onPressed: () { + Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL); + }, + ), + ], + ); + }, ); - - switch (decision) { - case RATING_DIALOG_YES: - if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); - break; - case RATING_DIALOG_NO: - launchUrl(emailUri, mode: LaunchMode.externalApplication); - break; - case RATING_DIALOG_CANCEL: - break; - } + return answer ?? PRE_RATING_DIALOG_CANCEL; } @override From ca9c3a15f3ad7b0c3dd91fcd9e8f887a566c8292 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 18:51:59 +0200 Subject: [PATCH 043/108] Corrected links --- lib/core/constants.dart | 5 +++-- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/core/constants.dart b/lib/core/constants.dart index ce8c30a..10b062d 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -6,8 +6,9 @@ class Constants { static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; static const String GITHUB_ISSUES_LINK = - 'cabocounter-issues.felixkirchner.de'; - static const String GITHUB_WIKI_LINK = 'cabocounter-wiki.felixkirchner.de '; + 'https://cabocounter-issues.felixkirchner.de'; + static const String GITHUB_WIKI_LINK = + 'https://cabocounter-wiki.felixkirchner.de'; static const String EMAIL = 'cabocounter@felixkirchner.de'; static const String PRIVACY_POLICY_LINK = 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; diff --git a/pubspec.yaml b/pubspec.yaml index 795551a..4625269 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+452 +version: 0.4.0+456 environment: sdk: ^3.5.4 From 51a2a789e7615549eeced90c274c03eaac6799a7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 18:52:03 +0200 Subject: [PATCH 044/108] Changed color --- lib/presentation/views/active_game_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 35404bb..9ecae1b 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -235,7 +235,8 @@ class _ActiveGameViewState extends State { child: Text( AppLocalizations.of(context).end_game, style: const TextStyle( - fontWeight: FontWeight.bold, color: Colors.red), + fontWeight: FontWeight.bold, + color: CupertinoColors.destructiveRed), ), onPressed: () { setState(() { From 89cdb4f824b65bb4f184d9cf7d82289ecbe8f1a9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 18:55:24 +0200 Subject: [PATCH 045/108] Ensured rating dialog wont show in Beta --- lib/presentation/views/main_menu_view.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 05c4a07..61ec010 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -40,7 +40,8 @@ class _MainMenuViewState extends State { WidgetsBinding.instance.addPostFrameCallback((_) async { await Constants.rateMyApp.init(); - if (Constants.rateMyApp.shouldOpenDialog) { + if (Constants.rateMyApp.shouldOpenDialog && + Constants.appDevPhase != 'Beta') { await Future.delayed(const Duration(milliseconds: 600)); if (!mounted) return; _handleFeedbackDialog(context); @@ -96,15 +97,13 @@ class _MainMenuViewState extends State { const SizedBox(height: 30), // Abstand von oben Center( child: GestureDetector( - onTap: () => _handleFeedbackDialog( - context) /*Navigator.push( + onTap: () => Navigator.push( context, CupertinoPageRoute( builder: (context) => const CreateGameView(), ), - )*/ - , + ), child: Icon( CupertinoIcons.plus, size: 60, From 5fc951844bd985de8110ab278a21b76cfca6a3e0 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:04:35 +0200 Subject: [PATCH 046/108] Refactoring --- lib/presentation/views/main_menu_view.dart | 171 ++++++++++----------- 1 file changed, 79 insertions(+), 92 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 61ec010..fac1d7c 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -21,11 +21,11 @@ class MainMenuView extends StatefulWidget { class _MainMenuViewState extends State { bool _isLoading = true; - static const int PRE_RATING_DIALOG_YES = 10; - static const int PRE_RATING_DIALOG_NO = 11; - static const int PRE_RATING_DIALOG_CANCEL = 12; - static const int BAD_RATING_DIALOG_EMAIL = 20; - static const int BAD_RATING_DIALOG_CANCEL = 21; + static const int PRE_RATING_DIALOG_YES = 1; + static const int PRE_RATING_DIALOG_NO = 0; + static const int PRE_RATING_DIALOG_CANCEL = -1; + static const int BAD_RATING_DIALOG_EMAIL = 1; + static const int BAD_RATING_DIALOG_CANCEL = 0; @override initState() { @@ -222,42 +222,6 @@ class _MainMenuViewState extends State { return AppLocalizations.of(context).unlimited; } - /// Shows a confirmation dialog to delete all game sessions. - /// Returns true if the user confirms the deletion, false otherwise. - /// [gameTitle] is the title of the game session to be deleted. - Future _showDeleteGamePopup(String gameTitle) async { - bool? shouldDelete = await showCupertinoDialog( - context: context, - builder: (context) { - return CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).delete_game_title), - content: Text( - AppLocalizations.of(context).delete_game_message(gameTitle)), - actions: [ - CupertinoDialogAction( - onPressed: () { - Navigator.pop(context, false); - }, - child: Text(AppLocalizations.of(context).cancel), - ), - CupertinoDialogAction( - onPressed: () { - Navigator.pop(context, true); - }, - child: Text( - AppLocalizations.of(context).delete, - style: const TextStyle( - fontWeight: FontWeight.bold, color: Colors.red), - ), - ), - ], - ); - }, - ) ?? - false; - return shouldDelete; - } - /// Handles the feedback dialog when the conditions for rating are met. /// It shows a dialog asking the user if they like the app, /// and based on their response, it either opens the rating dialog or an email client for feedback. @@ -295,43 +259,76 @@ class _MainMenuViewState extends State { } } + /// Shows a Cupertino dialog with a title, content, and a list of actions. + Future _showCupertinoChoiceDialog({ + required String title, + required String content, + required List<({String label, VoidCallback onPressed})> actions, + }) { + return showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text(title), + content: Text(content), + actions: actions + .map((action) => CupertinoDialogAction( + onPressed: action.onPressed, + child: Text(action.label), + )) + .toList(), + ); + }, + ); + } + + /// Shows a confirmation dialog to delete all game sessions. + /// Returns true if the user confirms the deletion, false otherwise. + /// [gameTitle] is the title of the game session to be deleted. + Future _showDeleteGamePopup(String gameTitle) async { + return await _showCupertinoChoiceDialog( + title: AppLocalizations.of(context).delete_game_title, + content: AppLocalizations.of(context).delete_game_message(gameTitle), + actions: [ + ( + label: AppLocalizations.of(context).cancel, + onPressed: () => Navigator.of(context).pop(false) + ), + ( + label: AppLocalizations.of(context).delete, + onPressed: () => Navigator.of(context).pop(true) + ), + ], + ) ?? + false; + } + /// Shows a dialog asking the user if they like the app. /// Returns the user's decision as an integer. /// - PRE_RATING_DIALOG_YES: User likes the app and wants to rate it. /// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback. /// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog. Future _showPreRatingDialog(BuildContext context) async { - int? answer = await showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).pre_rating_title), - content: Text(AppLocalizations.of(context).pre_rating_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).yes), - onPressed: () { - Navigator.of(context).pop(PRE_RATING_DIALOG_YES); - }, + return await _showCupertinoChoiceDialog( + title: AppLocalizations.of(context).pre_rating_title, + content: AppLocalizations.of(context).pre_rating_message, + actions: [ + ( + label: AppLocalizations.of(context).yes, + onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_YES) ), - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).no), - onPressed: () { - Navigator.of(context).pop(PRE_RATING_DIALOG_NO); - }, + ( + label: AppLocalizations.of(context).no, + onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_NO) ), - CupertinoDialogAction( - isDestructiveAction: true, - onPressed: () { - Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL); - }, - child: Text(AppLocalizations.of(context).cancel), + ( + label: AppLocalizations.of(context).cancel, + onPressed: () => + Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL) ), ], - ); - }, - ); - return answer ?? PRE_RATING_DIALOG_CANCEL; + ) ?? + PRE_RATING_DIALOG_CANCEL; } /// Shows a dialog asking the user for feedback if they do not like the app. @@ -339,33 +336,23 @@ class _MainMenuViewState extends State { /// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback. /// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog. Future _showBadRatingDialog(BuildContext context) async { - int? answer = await showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Text(AppLocalizations.of(context).bad_rating_title), - content: Text(AppLocalizations.of(context).bad_rating_message), - actions: [ - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).contact_email, - style: const TextStyle(fontWeight: FontWeight.bold)), - onPressed: () { - Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL); - }, + return await _showCupertinoChoiceDialog( + title: AppLocalizations.of(context).bad_rating_title, + content: AppLocalizations.of(context).bad_rating_message, + actions: [ + ( + label: AppLocalizations.of(context).contact_email, + onPressed: () => + Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL) ), - CupertinoDialogAction( - child: Text(AppLocalizations.of(context).cancel, - style: - const TextStyle(color: CupertinoColors.destructiveRed)), - onPressed: () { - Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL); - }, + ( + label: AppLocalizations.of(context).cancel, + onPressed: () => + Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL) ), ], - ); - }, - ); - return answer ?? PRE_RATING_DIALOG_CANCEL; + ) ?? + BAD_RATING_DIALOG_CANCEL; } @override From a9584a531ddb9ede73bd2f181066574b3cba2cee Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:08:44 +0200 Subject: [PATCH 047/108] Adding const --- lib/core/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 10b062d..0d32ceb 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -1,7 +1,7 @@ import 'package:rate_my_app/rate_my_app.dart'; class Constants { - static String appDevPhase = 'Beta'; + static const String appDevPhase = 'Beta'; static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; From 7e76af7a2068ee82f35f4969ad5dc66106154918 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:10:04 +0200 Subject: [PATCH 048/108] Renamed variables --- lib/presentation/views/main_menu_view.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index fac1d7c..979c854 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -236,19 +236,21 @@ class _MainMenuViewState extends State { '&body=$emailBody', ); - int decision = await _showPreRatingDialog(context); - int decision2 = 0; + int preRatingDecision = await _showPreRatingDialog(context); + int badRatingDecision = BAD_RATING_DIALOG_CANCEL; // so that the bad rating dialog is not shown immediately await Future.delayed(const Duration(milliseconds: 300)); - switch (decision) { + switch (preRatingDecision) { case PRE_RATING_DIALOG_YES: if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); break; case PRE_RATING_DIALOG_NO: - if (context.mounted) decision2 = await _showBadRatingDialog(context); - if (decision2 == BAD_RATING_DIALOG_EMAIL) { + if (context.mounted) { + badRatingDecision = await _showBadRatingDialog(context); + } + if (badRatingDecision == BAD_RATING_DIALOG_EMAIL) { if (context.mounted) { launchUrl(emailUri); } From d3693c20f12f9e5fa031116c33adad25768537b1 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:10:49 +0200 Subject: [PATCH 049/108] Corrected links --- lib/core/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 0d32ceb..5b6e90e 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -4,7 +4,7 @@ class Constants { static const String appDevPhase = 'Beta'; static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; - static const String GITHUB_LINK = 'https://github1.felixkirchner.de'; + static const String GITHUB_LINK = 'https://github.felixkirchner.de'; static const String GITHUB_ISSUES_LINK = 'https://cabocounter-issues.felixkirchner.de'; static const String GITHUB_WIKI_LINK = From b5ef2df0c6880f919af9511e370cef03586e7101 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:16:57 +0200 Subject: [PATCH 050/108] updated Dialog function --- lib/presentation/views/main_menu_view.dart | 26 +++++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 979c854..09e4c26 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -265,7 +265,7 @@ class _MainMenuViewState extends State { Future _showCupertinoChoiceDialog({ required String title, required String content, - required List<({String label, VoidCallback onPressed})> actions, + required List<({Widget content, VoidCallback onPressed})> actions, }) { return showCupertinoDialog( context: context, @@ -276,7 +276,7 @@ class _MainMenuViewState extends State { actions: actions .map((action) => CupertinoDialogAction( onPressed: action.onPressed, - child: Text(action.label), + child: action.content, )) .toList(), ); @@ -293,13 +293,17 @@ class _MainMenuViewState extends State { content: AppLocalizations.of(context).delete_game_message(gameTitle), actions: [ ( - label: AppLocalizations.of(context).cancel, + content: Text(AppLocalizations.of(context).cancel), onPressed: () => Navigator.of(context).pop(false) ), ( - label: AppLocalizations.of(context).delete, - onPressed: () => Navigator.of(context).pop(true) - ), + content: Text(AppLocalizations.of(context).delete, + style: const TextStyle( + color: CupertinoColors.destructiveRed, + fontWeight: FontWeight.bold, + )), + onPressed: () => Navigator.of(context).pop(false) + ) ], ) ?? false; @@ -316,15 +320,15 @@ class _MainMenuViewState extends State { content: AppLocalizations.of(context).pre_rating_message, actions: [ ( - label: AppLocalizations.of(context).yes, + content: Text(AppLocalizations.of(context).yes), onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_YES) ), ( - label: AppLocalizations.of(context).no, + content: Text(AppLocalizations.of(context).no), onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_NO) ), ( - label: AppLocalizations.of(context).cancel, + content: Text(AppLocalizations.of(context).cancel), onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL) ), @@ -343,12 +347,12 @@ class _MainMenuViewState extends State { content: AppLocalizations.of(context).bad_rating_message, actions: [ ( - label: AppLocalizations.of(context).contact_email, + content: Text(AppLocalizations.of(context).contact_email), onPressed: () => Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL) ), ( - label: AppLocalizations.of(context).cancel, + content: Text(AppLocalizations.of(context).cancel), onPressed: () => Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL) ), From ac6db7fdd90d13792059c730a2e1d928b97744ea Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:26:58 +0200 Subject: [PATCH 051/108] Added version number in about view --- .../views/{information_view.dart => about_view.dart} | 11 ++++++++--- lib/presentation/views/tab_view.dart | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) rename lib/presentation/views/{information_view.dart => about_view.dart} (87%) diff --git a/lib/presentation/views/information_view.dart b/lib/presentation/views/about_view.dart similarity index 87% rename from lib/presentation/views/information_view.dart rename to lib/presentation/views/about_view.dart index 712d709..128f91f 100644 --- a/lib/presentation/views/information_view.dart +++ b/lib/presentation/views/about_view.dart @@ -1,12 +1,13 @@ import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/services/version_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; -class InformationView extends StatelessWidget { - const InformationView({super.key}); +class AboutView extends StatelessWidget { + const AboutView({super.key}); @override Widget build(BuildContext context) { @@ -29,9 +30,13 @@ class InformationView extends StatelessWidget { ), ), ), + Text( + '${AppLocalizations.of(context).app_version} ${VersionService.getVersionWithBuild()}', + style: TextStyle(fontSize: 15, color: Colors.grey[300]), + ), Padding( padding: - const EdgeInsets.symmetric(horizontal: 20, vertical: 30), + const EdgeInsets.symmetric(horizontal: 20, vertical: 15), child: SizedBox( height: 200, child: Image.asset('assets/cabo_counter-logo_rounded.png'), diff --git a/lib/presentation/views/tab_view.dart b/lib/presentation/views/tab_view.dart index 15d4637..0c98cc7 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,6 +1,6 @@ import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; -import 'package:cabo_counter/presentation/views/information_view.dart'; +import 'package:cabo_counter/presentation/views/about_view.dart'; import 'package:cabo_counter/presentation/views/main_menu_view.dart'; import 'package:flutter/cupertino.dart'; @@ -39,7 +39,7 @@ class _TabViewState extends State { if (index == 0) { return const MainMenuView(); } else { - return const InformationView(); + return const AboutView(); } }); }, diff --git a/pubspec.yaml b/pubspec.yaml index 4625269..52ae1fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+456 +version: 0.4.0+459 environment: sdk: ^3.5.4 From 034ce19c60dbca5425f1c28549561dc4d3fa3258 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:36:17 +0200 Subject: [PATCH 052/108] Changed order and corrected return --- lib/presentation/views/main_menu_view.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 09e4c26..1e8a67a 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -292,16 +292,16 @@ class _MainMenuViewState extends State { title: AppLocalizations.of(context).delete_game_title, content: AppLocalizations.of(context).delete_game_message(gameTitle), actions: [ - ( - content: Text(AppLocalizations.of(context).cancel), - onPressed: () => Navigator.of(context).pop(false) - ), ( content: Text(AppLocalizations.of(context).delete, style: const TextStyle( color: CupertinoColors.destructiveRed, fontWeight: FontWeight.bold, )), + onPressed: () => Navigator.of(context).pop(true) + ), + ( + content: Text(AppLocalizations.of(context).cancel), onPressed: () => Navigator.of(context).pop(false) ) ], From 3a74ca0d9801bfe69e174d4224063d5fcf2f8d02 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 19:37:53 +0200 Subject: [PATCH 053/108] Changed translation --- lib/l10n/arb/app_en.arb | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 3f24635..467135e 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -36,7 +36,7 @@ "no": "No", "bad_rating_title": "Not satisfied?", "bad_rating_message": "If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.", - "contact_email": "Contac vía E-Mail", + "contact_email": "Contac via E-Mail", "email_subject": "Feedback: Cabo Counter App", "email_body": "I have the following feedback...", diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index d3e433b..a986058 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -92,7 +92,7 @@ class AppLocalizationsEn extends AppLocalizations { 'If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.'; @override - String get contact_email => 'Contac vía E-Mail'; + String get contact_email => 'Contac via E-Mail'; @override String get email_subject => 'Feedback: Cabo Counter App'; diff --git a/pubspec.yaml b/pubspec.yaml index 52ae1fc..33ef0fe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+459 +version: 0.4.0+461 environment: sdk: ^3.5.4 From 5ede7e92780efa001326b6422e335f0c04225915 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:03:58 +0200 Subject: [PATCH 054/108] Changed popups because of unmounted context errors --- lib/presentation/views/main_menu_view.dart | 166 ++++++++++----------- pubspec.yaml | 2 +- 2 files changed, 82 insertions(+), 86 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 1e8a67a..2a95346 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -75,14 +75,12 @@ class _MainMenuViewState extends State { icon: const Icon(CupertinoIcons.settings, size: 30)), middle: const Text('Cabo Counter'), trailing: IconButton( - onPressed: () => { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => const CreateGameView(), - ), - ) - }, + onPressed: () => Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => const CreateGameView(), + ), + ), icon: const Icon(CupertinoIcons.add)), ), child: CupertinoPageScaffold( @@ -146,7 +144,7 @@ class _MainMenuViewState extends State { final String gameTitle = gameManager .gameList[index].gameTitle; return await _showDeleteGamePopup( - gameTitle); + gameTitle, context); }, onDismissed: (direction) { gameManager @@ -261,50 +259,37 @@ class _MainMenuViewState extends State { } } - /// Shows a Cupertino dialog with a title, content, and a list of actions. - Future _showCupertinoChoiceDialog({ - required String title, - required String content, - required List<({Widget content, VoidCallback onPressed})> actions, - }) { - return showCupertinoDialog( - context: context, - builder: (BuildContext context) { - return CupertinoAlertDialog( - title: Text(title), - content: Text(content), - actions: actions - .map((action) => CupertinoDialogAction( - onPressed: action.onPressed, - child: action.content, - )) - .toList(), - ); - }, - ); - } - /// Shows a confirmation dialog to delete all game sessions. /// Returns true if the user confirms the deletion, false otherwise. /// [gameTitle] is the title of the game session to be deleted. - Future _showDeleteGamePopup(String gameTitle) async { - return await _showCupertinoChoiceDialog( - title: AppLocalizations.of(context).delete_game_title, - content: AppLocalizations.of(context).delete_game_message(gameTitle), - actions: [ - ( - content: Text(AppLocalizations.of(context).delete, - style: const TextStyle( - color: CupertinoColors.destructiveRed, - fontWeight: FontWeight.bold, - )), - onPressed: () => Navigator.of(context).pop(true) - ), - ( - content: Text(AppLocalizations.of(context).cancel), - onPressed: () => Navigator.of(context).pop(false) - ) - ], + Future _showDeleteGamePopup( + String gameTitle, BuildContext context) async { + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) { + return CupertinoAlertDialog( + title: Text( + AppLocalizations.of(context).delete_game_title, + ), + content: Text(AppLocalizations.of(context) + .delete_game_message(gameTitle)), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel), + onPressed: () { + Navigator.of(context).pop(false); + }), + CupertinoDialogAction( + isDestructiveAction: true, + isDefaultAction: true, + child: Text( + AppLocalizations.of(context).delete, + ), + onPressed: () { + Navigator.of(context).pop(true); + }) + ]); + }, ) ?? false; } @@ -315,25 +300,32 @@ class _MainMenuViewState extends State { /// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback. /// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog. Future _showPreRatingDialog(BuildContext context) async { - return await _showCupertinoChoiceDialog( - title: AppLocalizations.of(context).pre_rating_title, - content: AppLocalizations.of(context).pre_rating_message, - actions: [ - ( - content: Text(AppLocalizations.of(context).yes), - onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_YES) - ), - ( - content: Text(AppLocalizations.of(context).no), - onPressed: () => Navigator.of(context).pop(PRE_RATING_DIALOG_NO) - ), - ( - content: Text(AppLocalizations.of(context).cancel), - onPressed: () => - Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL) - ), - ], - ) ?? + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).pre_rating_title), + content: + Text(AppLocalizations.of(context).pre_rating_message), + actions: [ + CupertinoDialogAction( + onPressed: () => + Navigator.of(context).pop(PRE_RATING_DIALOG_YES), + isDefaultAction: true, + child: Text(AppLocalizations.of(context).yes), + ), + CupertinoDialogAction( + onPressed: () => + Navigator.of(context).pop(PRE_RATING_DIALOG_NO), + child: Text(AppLocalizations.of(context).no), + ), + CupertinoDialogAction( + onPressed: () => + Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL), + isDestructiveAction: true, + child: Text(AppLocalizations.of(context).cancel), + ) + ], + )) ?? PRE_RATING_DIALOG_CANCEL; } @@ -342,22 +334,26 @@ class _MainMenuViewState extends State { /// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback. /// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog. Future _showBadRatingDialog(BuildContext context) async { - return await _showCupertinoChoiceDialog( - title: AppLocalizations.of(context).bad_rating_title, - content: AppLocalizations.of(context).bad_rating_message, - actions: [ - ( - content: Text(AppLocalizations.of(context).contact_email), - onPressed: () => - Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL) - ), - ( - content: Text(AppLocalizations.of(context).cancel), - onPressed: () => - Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL) - ), - ], - ) ?? + return await showCupertinoDialog( + context: context, + builder: (BuildContext context) => CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).bad_rating_title), + content: + Text(AppLocalizations.of(context).bad_rating_message), + actions: [ + CupertinoDialogAction( + isDefaultAction: true, + onPressed: () => + Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL), + child: Text(AppLocalizations.of(context).contact_email), + ), + CupertinoDialogAction( + isDestructiveAction: true, + onPressed: () => + Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL), + child: Text(AppLocalizations.of(context).cancel)) + ], + )) ?? BAD_RATING_DIALOG_CANCEL; } diff --git a/pubspec.yaml b/pubspec.yaml index 33ef0fe..31146e6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+461 +version: 0.4.0+467 environment: sdk: ^3.5.4 From f3114c6c32a3d1633b44a6d330a8471d89ed541e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:04:11 +0200 Subject: [PATCH 055/108] corrected string typo --- lib/l10n/arb/app_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 467135e..e01e242 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -36,7 +36,7 @@ "no": "No", "bad_rating_title": "Not satisfied?", "bad_rating_message": "If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.", - "contact_email": "Contac via E-Mail", + "contact_email": "Contact via E-Mail", "email_subject": "Feedback: Cabo Counter App", "email_body": "I have the following feedback...", From a8ca853c24c24226bc6475da9c99b1fdc0ee0049 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:12:58 +0200 Subject: [PATCH 056/108] Replaced int constants with enums --- lib/presentation/views/main_menu_view.dart | 54 +++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 2a95346..ee36d1f 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -11,6 +11,10 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; +enum PreRatingDialogDecision { yes, no, cancel } + +enum BadRatingDialogDecision { email, cancel } + class MainMenuView extends StatefulWidget { const MainMenuView({super.key}); @@ -21,11 +25,6 @@ class MainMenuView extends StatefulWidget { class _MainMenuViewState extends State { bool _isLoading = true; - static const int PRE_RATING_DIALOG_YES = 1; - static const int PRE_RATING_DIALOG_NO = 0; - static const int PRE_RATING_DIALOG_CANCEL = -1; - static const int BAD_RATING_DIALOG_EMAIL = 1; - static const int BAD_RATING_DIALOG_CANCEL = 0; @override initState() { @@ -234,27 +233,28 @@ class _MainMenuViewState extends State { '&body=$emailBody', ); - int preRatingDecision = await _showPreRatingDialog(context); - int badRatingDecision = BAD_RATING_DIALOG_CANCEL; + PreRatingDialogDecision preRatingDecision = + await _showPreRatingDialog(context); + BadRatingDialogDecision badRatingDecision = BadRatingDialogDecision.cancel; // so that the bad rating dialog is not shown immediately await Future.delayed(const Duration(milliseconds: 300)); switch (preRatingDecision) { - case PRE_RATING_DIALOG_YES: + case PreRatingDialogDecision.yes: if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); break; - case PRE_RATING_DIALOG_NO: + case PreRatingDialogDecision.no: if (context.mounted) { badRatingDecision = await _showBadRatingDialog(context); } - if (badRatingDecision == BAD_RATING_DIALOG_EMAIL) { + if (badRatingDecision == BadRatingDialogDecision.email) { if (context.mounted) { launchUrl(emailUri); } } break; - case PRE_RATING_DIALOG_CANCEL: + case PreRatingDialogDecision.cancel: break; } } @@ -262,7 +262,7 @@ class _MainMenuViewState extends State { /// Shows a confirmation dialog to delete all game sessions. /// Returns true if the user confirms the deletion, false otherwise. /// [gameTitle] is the title of the game session to be deleted. - Future _showDeleteGamePopup( + Future _showDeleteGamePopup( String gameTitle, BuildContext context) async { return await showCupertinoDialog( context: context, @@ -299,8 +299,9 @@ class _MainMenuViewState extends State { /// - PRE_RATING_DIALOG_YES: User likes the app and wants to rate it. /// - PRE_RATING_DIALOG_NO: User does not like the app and wants to provide feedback. /// - PRE_RATING_DIALOG_CANCEL: User cancels the dialog. - Future _showPreRatingDialog(BuildContext context) async { - return await showCupertinoDialog( + Future _showPreRatingDialog( + BuildContext context) async { + return await showCupertinoDialog( context: context, builder: (BuildContext context) => CupertinoAlertDialog( title: Text(AppLocalizations.of(context).pre_rating_title), @@ -308,33 +309,33 @@ class _MainMenuViewState extends State { Text(AppLocalizations.of(context).pre_rating_message), actions: [ CupertinoDialogAction( - onPressed: () => - Navigator.of(context).pop(PRE_RATING_DIALOG_YES), + onPressed: () => Navigator.of(context) + .pop(PreRatingDialogDecision.yes), isDefaultAction: true, child: Text(AppLocalizations.of(context).yes), ), CupertinoDialogAction( onPressed: () => - Navigator.of(context).pop(PRE_RATING_DIALOG_NO), + Navigator.of(context).pop(PreRatingDialogDecision.no), child: Text(AppLocalizations.of(context).no), ), CupertinoDialogAction( - onPressed: () => - Navigator.of(context).pop(PRE_RATING_DIALOG_CANCEL), + onPressed: () => Navigator.of(context).pop(), isDestructiveAction: true, child: Text(AppLocalizations.of(context).cancel), ) ], )) ?? - PRE_RATING_DIALOG_CANCEL; + PreRatingDialogDecision.cancel; } /// Shows a dialog asking the user for feedback if they do not like the app. /// Returns the user's decision as an integer. /// - BAD_RATING_DIALOG_EMAIL: User wants to send an email with feedback. /// - BAD_RATING_DIALOG_CANCEL: User cancels the dialog. - Future _showBadRatingDialog(BuildContext context) async { - return await showCupertinoDialog( + Future _showBadRatingDialog( + BuildContext context) async { + return await showCupertinoDialog( context: context, builder: (BuildContext context) => CupertinoAlertDialog( title: Text(AppLocalizations.of(context).bad_rating_title), @@ -343,18 +344,17 @@ class _MainMenuViewState extends State { actions: [ CupertinoDialogAction( isDefaultAction: true, - onPressed: () => - Navigator.of(context).pop(BAD_RATING_DIALOG_EMAIL), + onPressed: () => Navigator.of(context) + .pop(BadRatingDialogDecision.email), child: Text(AppLocalizations.of(context).contact_email), ), CupertinoDialogAction( isDestructiveAction: true, - onPressed: () => - Navigator.of(context).pop(BAD_RATING_DIALOG_CANCEL), + onPressed: () => Navigator.of(context).pop(), child: Text(AppLocalizations.of(context).cancel)) ], )) ?? - BAD_RATING_DIALOG_CANCEL; + BadRatingDialogDecision.cancel; } @override From 2f225c9b23412245a94aac5996b4964b34ea1979 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:13:54 +0200 Subject: [PATCH 057/108] Renamed Stepper to CustomStepper --- lib/presentation/views/settings_view.dart | 6 +++--- lib/presentation/widgets/custom_form_row.dart | 4 ++-- .../widgets/{stepper.dart => custom_stepper.dart} | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) rename lib/presentation/widgets/{stepper.dart => custom_stepper.dart} (90%) diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index c9a409d..9bed7fc 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -2,7 +2,7 @@ import 'package:cabo_counter/core/constants.dart'; import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:cabo_counter/presentation/widgets/custom_form_row.dart'; -import 'package:cabo_counter/presentation/widgets/stepper.dart'; +import 'package:cabo_counter/presentation/widgets/custom_stepper.dart'; import 'package:cabo_counter/services/config_service.dart'; import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:cabo_counter/services/version_service.dart'; @@ -54,7 +54,7 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: 'Cabo-Strafe', prefixIcon: CupertinoIcons.bolt_fill, - suffixWidget: Stepper( + suffixWidget: CustomStepper( key: _stepperKey1, initialValue: ConfigService.caboPenalty, minValue: 0, @@ -71,7 +71,7 @@ class _SettingsViewState extends State { CustomFormRow( prefixText: 'Punkte-Limit', prefixIcon: FontAwesomeIcons.bullseye, - suffixWidget: Stepper( + suffixWidget: CustomStepper( key: _stepperKey2, initialValue: ConfigService.pointLimit, minValue: 30, diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart index 96067c3..7a2526b 100644 --- a/lib/presentation/widgets/custom_form_row.dart +++ b/lib/presentation/widgets/custom_form_row.dart @@ -1,5 +1,5 @@ import 'package:cabo_counter/core/custom_theme.dart'; -import 'package:cabo_counter/presentation/widgets/stepper.dart'; +import 'package:cabo_counter/presentation/widgets/custom_stepper.dart'; import 'package:flutter/cupertino.dart'; class CustomFormRow extends StatefulWidget { @@ -43,7 +43,7 @@ class _CustomFormRowState extends State { Text(widget.prefixText), ], ), - padding: suffixWidget is Stepper + padding: suffixWidget is CustomStepper ? const EdgeInsets.fromLTRB(15, 0, 0, 0) : const EdgeInsets.symmetric(vertical: 10, horizontal: 15), child: suffixWidget, diff --git a/lib/presentation/widgets/stepper.dart b/lib/presentation/widgets/custom_stepper.dart similarity index 90% rename from lib/presentation/widgets/stepper.dart rename to lib/presentation/widgets/custom_stepper.dart index 5d0bce8..a05a4cb 100644 --- a/lib/presentation/widgets/stepper.dart +++ b/lib/presentation/widgets/custom_stepper.dart @@ -1,13 +1,13 @@ import 'package:cabo_counter/core/custom_theme.dart'; import 'package:flutter/cupertino.dart'; // Für iOS-Style -class Stepper extends StatefulWidget { +class CustomStepper extends StatefulWidget { final int minValue; final int maxValue; final int? initialValue; final int step; final ValueChanged onChanged; - const Stepper({ + const CustomStepper({ super.key, required this.minValue, required this.maxValue, @@ -18,10 +18,10 @@ class Stepper extends StatefulWidget { @override // ignore: library_private_types_in_public_api - _StepperState createState() => _StepperState(); + _CustomStepperState createState() => _CustomStepperState(); } -class _StepperState extends State { +class _CustomStepperState extends State { late int _value; @override From 1c505916f3a42a2a3fda44af57b8047ed6c3788c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:14:50 +0200 Subject: [PATCH 058/108] Changed argument order --- lib/presentation/views/main_menu_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index ee36d1f..f6c1a32 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -143,7 +143,7 @@ class _MainMenuViewState extends State { final String gameTitle = gameManager .gameList[index].gameTitle; return await _showDeleteGamePopup( - gameTitle, context); + context, gameTitle); }, onDismissed: (direction) { gameManager @@ -263,7 +263,7 @@ class _MainMenuViewState extends State { /// Returns true if the user confirms the deletion, false otherwise. /// [gameTitle] is the title of the game session to be deleted. Future _showDeleteGamePopup( - String gameTitle, BuildContext context) async { + BuildContext context, String gameTitle) async { return await showCupertinoDialog( context: context, builder: (BuildContext context) { From 964c179e6929d6274b4a22de83d67cd9bf84dd9c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Thu, 10 Jul 2025 20:46:11 +0200 Subject: [PATCH 059/108] Reordered properties --- lib/presentation/views/main_menu_view.dart | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index f6c1a32..1f8b5d0 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -275,19 +275,21 @@ class _MainMenuViewState extends State { .delete_game_message(gameTitle)), actions: [ CupertinoDialogAction( - child: Text(AppLocalizations.of(context).cancel), - onPressed: () { - Navigator.of(context).pop(false); - }), + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(AppLocalizations.of(context).cancel), + ), CupertinoDialogAction( - isDestructiveAction: true, - isDefaultAction: true, - child: Text( - AppLocalizations.of(context).delete, - ), - onPressed: () { - Navigator.of(context).pop(true); - }) + isDestructiveAction: true, + isDefaultAction: true, + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text( + AppLocalizations.of(context).delete, + ), + ) ]); }, ) ?? From b3c70f711ad98be4f8a37c4aadfdb99c55d93c66 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:18:29 +0200 Subject: [PATCH 060/108] Implemented empty builder for GraphView --- lib/l10n/arb/app_de.arb | 1 + lib/l10n/arb/app_en.arb | 6 ++- lib/l10n/generated/app_localizations.dart | 6 +++ lib/l10n/generated/app_localizations_de.dart | 4 ++ lib/l10n/generated/app_localizations_en.dart | 8 +++- lib/presentation/views/graph_view.dart | 46 ++++++++++++++------ pubspec.yaml | 2 +- 7 files changed, 54 insertions(+), 19 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index d89399b..acff3c1 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -85,6 +85,7 @@ "end_game_message": "Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.", "game_process": "Spielverlauf", + "empty_graph_text": "Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.", "settings": "Einstellungen", "cabo_penalty": "Cabo-Strafe", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index e01e242..4a0735a 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -74,18 +74,20 @@ "done": "Done", "next_round": "Next Round", + "statistics": "Statistics", "end_game": "End Game", "delete_game": "Delete Game", "new_game_same_settings": "New Game with same Settings", "export_game": "Export Game", - - "game_process": "Spielverlauf", "id_error_title": "ID Error", "id_error_message": "The game has not yet been assigned an ID. If you want to delete the game, please do so via the main menu. All newly created games have an ID.", "end_game_title": "End the game?", "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", + "game_process": "Scoring History", + "empty_graph_text": "You must play at least two rounds for the game progress graph to be displayed.", + "settings": "Settings", "cabo_penalty": "Cabo Penalty", "cabo_penalty_subtitle": "... for falsely calling Cabo.", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index af778ae..c139891 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -476,6 +476,12 @@ abstract class AppLocalizations { /// **'Spielverlauf'** String get game_process; + /// No description provided for @empty_graph_text. + /// + /// In de, this message translates to: + /// **'Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'** + String get empty_graph_text; + /// No description provided for @settings. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index afe558f..b61feb0 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -210,6 +210,10 @@ class AppLocalizationsDe extends AppLocalizations { @override String get game_process => 'Spielverlauf'; + @override + String get empty_graph_text => + 'Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'; + @override String get settings => 'Einstellungen'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index a986058..fcfd494 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -92,7 +92,7 @@ class AppLocalizationsEn extends AppLocalizations { 'If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.'; @override - String get contact_email => 'Contac via E-Mail'; + String get contact_email => 'Contact via E-Mail'; @override String get email_subject => 'Feedback: Cabo Counter App'; @@ -205,7 +205,11 @@ class AppLocalizationsEn extends AppLocalizations { 'Do you want to end the game? The game gets marked as finished and cannot be continued.'; @override - String get game_process => 'Spielverlauf'; + String get game_process => 'Scoring History'; + + @override + String get empty_graph_text => + 'You must play at least two rounds for the game progress graph to be displayed.'; @override String get settings => 'Settings'; diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 1007007..9ee0bbc 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -26,21 +26,39 @@ class _GraphViewState extends State { @override Widget build(BuildContext context) { return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text(AppLocalizations.of(context).game_process), - previousPageTitle: AppLocalizations.of(context).back, - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), - child: SfCartesianChart( - legend: - const Legend(isVisible: true, position: LegendPosition.bottom), - primaryXAxis: const NumericAxis(), - primaryYAxis: const NumericAxis(), - series: getCumulativeScores(), + navigationBar: CupertinoNavigationBar( + middle: Text(AppLocalizations.of(context).game_process), + previousPageTitle: AppLocalizations.of(context).back, ), - ), - ); + child: widget.gameSession.roundNumber > 2 + ? Padding( + padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), + child: SfCartesianChart( + legend: const Legend( + isVisible: true, position: LegendPosition.bottom), + primaryXAxis: const NumericAxis(), + primaryYAxis: const NumericAxis(), + series: getCumulativeScores(), + ), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Center( + child: Icon(CupertinoIcons.chart_bar_alt_fill, size: 60), + ), + const SizedBox(height: 10), // Abstand von oben + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + AppLocalizations.of(context).empty_graph_text, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + ), + ], + )); } /// Returns a list of LineSeries representing the cumulative scores of each player. diff --git a/pubspec.yaml b/pubspec.yaml index 31146e6..802d487 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+467 +version: 0.4.0+470 environment: sdk: ^3.5.4 From 668328300ad4c4821529b1b17da7320af3ee91bc Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:34:39 +0200 Subject: [PATCH 061/108] Added jitterStip to prevent the graphs overlaying each other --- lib/core/custom_theme.dart | 7 ++++++ lib/presentation/views/graph_view.dart | 34 ++++++++++++++++---------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index 77a2f5b..259d5fd 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -6,6 +6,13 @@ class CustomTheme { static Color backgroundColor = const Color(0xFF101010); static Color backgroundTintColor = CupertinoColors.darkBackgroundGray; + // graph colors + static const Color graphColor1 = Color(0xFFF44336); + static const Color graphColor2 = Color(0xFF2196F3); //FF2196F3 + static const Color graphColor3 = Color(0xFFFFA726); //FFFFA726 + static const Color graphColor4 = Color(0xFF9C27B0); //9C27B0 + static final Color graphColor5 = primaryColor; + static TextStyle modeTitle = TextStyle( color: primaryColor, fontSize: 20, diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 9ee0bbc..1e91a98 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -1,7 +1,7 @@ +import 'package:cabo_counter/core/custom_theme.dart'; import 'package:cabo_counter/data/game_session.dart'; import 'package:cabo_counter/l10n/generated/app_localizations.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; class GraphView extends StatefulWidget { @@ -15,12 +15,12 @@ class GraphView extends StatefulWidget { class _GraphViewState extends State { /// List of colors for the graph lines. - List lineColors = [ - Colors.red, - Colors.blue, - Colors.orange.shade400, - Colors.purple, - Colors.green, + final List lineColors = [ + CustomTheme.graphColor1, + CustomTheme.graphColor2, + CustomTheme.graphColor3, + CustomTheme.graphColor4, + CustomTheme.graphColor5 ]; @override @@ -36,7 +36,10 @@ class _GraphViewState extends State { child: SfCartesianChart( legend: const Legend( isVisible: true, position: LegendPosition.bottom), - primaryXAxis: const NumericAxis(), + primaryXAxis: const NumericAxis( + interval: 1, + decimalPlaces: 0, + ), primaryYAxis: const NumericAxis(), series: getCumulativeScores(), ), @@ -64,7 +67,7 @@ class _GraphViewState extends State { /// Returns a list of LineSeries representing the cumulative scores of each player. /// Each series contains data points for each round, showing the cumulative score up to that round. /// The x-axis represents the round number, and the y-axis represents the cumulative score. - List> getCumulativeScores() { + List> getCumulativeScores() { final rounds = widget.gameSession.roundList; final playerCount = widget.gameSession.players.length; final playerNames = widget.gameSession.players; @@ -79,21 +82,26 @@ class _GraphViewState extends State { } } + const double jitterStep = 0.15; + /// Create a list of LineSeries for each player /// Each series contains data points for each round return List.generate(playerCount, (i) { final data = List.generate( cumulativeScores[i].length, - (j) => (j + 1, cumulativeScores[i][j]), // (round, score) + (j) => ( + j + 1, + cumulativeScores[i][j] + (i - playerCount ~/ 2) * jitterStep + ), ); /// Create a LineSeries for the player /// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score. - return LineSeries<(int, int), int>( + return LineSeries<(int, num), int>( name: playerNames[i], dataSource: data, - xValueMapper: (record, _) => record.$1, // Runde - yValueMapper: (record, _) => record.$2, // Punktestand + xValueMapper: (record, _) => record.$1, + yValueMapper: (record, _) => record.$2, markerSettings: const MarkerSettings(isVisible: true), color: lineColors[i], ); From 4448fd2c39df7a01f3f50f5a8e54351de8bf8580 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:44:21 +0200 Subject: [PATCH 062/108] Removed german comments --- lib/presentation/views/graph_view.dart | 2 +- lib/presentation/views/main_menu_view.dart | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 1e91a98..50fb12a 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -51,7 +51,7 @@ class _GraphViewState extends State { const Center( child: Icon(CupertinoIcons.chart_bar_alt_fill, size: 60), ), - const SizedBox(height: 10), // Abstand von oben + const SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Text( diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index 1f8b5d0..fa537c6 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -88,10 +88,9 @@ class _MainMenuViewState extends State { ? const Center(child: CupertinoActivityIndicator()) : gameManager.gameList.isEmpty ? Column( - mainAxisAlignment: - MainAxisAlignment.center, // Oben ausrichten + mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(height: 30), // Abstand von oben + const SizedBox(height: 30), Center( child: GestureDetector( onTap: () => Navigator.push( @@ -107,7 +106,7 @@ class _MainMenuViewState extends State { color: CustomTheme.primaryColor, ), )), - const SizedBox(height: 10), // Abstand von oben + const SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 70), From 5fa8d1fa3de4ce8fb186a65c1dc8255b6f5d77d7 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:49:19 +0200 Subject: [PATCH 063/108] Added comment to jitter calculation --- lib/presentation/views/graph_view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 50fb12a..7736bb1 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -91,6 +91,9 @@ class _GraphViewState extends State { cumulativeScores[i].length, (j) => ( j + 1, + + // Add a small jitter to the cumulative scores to prevent overlapping data points in the graph. + // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. cumulativeScores[i][j] + (i - playerCount ~/ 2) * jitterStep ), ); From 1494cb08a5943af1e36c2918d94798a1d901a18e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:50:11 +0200 Subject: [PATCH 064/108] Overhauled comments in CustomTheme --- lib/core/custom_theme.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/custom_theme.dart b/lib/core/custom_theme.dart index 259d5fd..a00340b 100644 --- a/lib/core/custom_theme.dart +++ b/lib/core/custom_theme.dart @@ -6,11 +6,11 @@ class CustomTheme { static Color backgroundColor = const Color(0xFF101010); static Color backgroundTintColor = CupertinoColors.darkBackgroundGray; - // graph colors + // Line Colors for GraphView static const Color graphColor1 = Color(0xFFF44336); - static const Color graphColor2 = Color(0xFF2196F3); //FF2196F3 - static const Color graphColor3 = Color(0xFFFFA726); //FFFFA726 - static const Color graphColor4 = Color(0xFF9C27B0); //9C27B0 + static const Color graphColor2 = Color(0xFF2196F3); + static const Color graphColor3 = Color(0xFFFFA726); + static const Color graphColor4 = Color(0xFF9C27B0); static final Color graphColor5 = primaryColor; static TextStyle modeTitle = TextStyle( From ebe3dd6954506cc25f06e0dd2b73198f2a269b44 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 10:52:47 +0200 Subject: [PATCH 065/108] Updated version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 802d487..fbacd1e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+470 +version: 0.4.1+470 environment: sdk: ^3.5.4 From 55a61be44b2a3876a7b7fbee5ad244cf8f95d5ff Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:10:06 +0200 Subject: [PATCH 066/108] Added Delete all games button to Settings --- lib/l10n/arb/app_de.arb | 3 + lib/l10n/arb/app_en.arb | 3 + lib/l10n/generated/app_localizations.dart | 18 ++ lib/l10n/generated/app_localizations_de.dart | 10 + lib/l10n/generated/app_localizations_en.dart | 12 +- lib/presentation/views/settings_view.dart | 321 ++++++++++--------- pubspec.yaml | 2 +- 7 files changed, 222 insertions(+), 147 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index d89399b..113cce1 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -95,6 +95,9 @@ "game_data": "Spieldaten", "import_data": "Spieldaten importieren", "export_data": "Spieldaten exportieren", + "delete_data": "Alle Spieldaten löschen", + "delete_data_title": "Spieldaten löschen?", + "delete_data_message": "Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", "app": "App", "import_success_title": "Import erfolgreich", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index e01e242..1f5fe3c 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -95,6 +95,9 @@ "game_data": "Game Data", "import_data": "Import Data", "export_data": "Export Data", + "delete_data": "Delete all Game Data", + "delete_data_title": "Delete game data?", + "delete_data_message": "Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", "app": "App", "import_success_title": "Import successful", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index af778ae..979d46b 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -530,6 +530,24 @@ abstract class AppLocalizations { /// **'Spieldaten exportieren'** String get export_data; + /// No description provided for @delete_data. + /// + /// In de, this message translates to: + /// **'Alle Spieldaten löschen'** + String get delete_data; + + /// No description provided for @delete_data_title. + /// + /// In de, this message translates to: + /// **'Spieldaten löschen?'** + String get delete_data_title; + + /// No description provided for @delete_data_message. + /// + /// In de, this message translates to: + /// **'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'** + String get delete_data_message; + /// No description provided for @app. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index afe558f..c4b2491 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -237,6 +237,16 @@ class AppLocalizationsDe extends AppLocalizations { @override String get export_data => 'Spieldaten exportieren'; + @override + String get delete_data => 'Alle Spieldaten löschen'; + + @override + String get delete_data_title => 'Spieldaten löschen?'; + + @override + String get delete_data_message => + 'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; + @override String get app => 'App'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index a986058..3cbde9c 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -92,7 +92,7 @@ class AppLocalizationsEn extends AppLocalizations { 'If you are not satisfied with the app, please let me know before leaving a bad rating. I will try to fix the issue as soon as possible.'; @override - String get contact_email => 'Contac via E-Mail'; + String get contact_email => 'Contact via E-Mail'; @override String get email_subject => 'Feedback: Cabo Counter App'; @@ -234,6 +234,16 @@ class AppLocalizationsEn extends AppLocalizations { @override String get export_data => 'Export Data'; + @override + String get delete_data => 'Delete all Game Data'; + + @override + String get delete_data_title => 'Delete game data?'; + + @override + String get delete_data_message => + 'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; + @override String get app => 'App'; diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index 9bed7fc..ab8ec7d 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -32,162 +32,193 @@ class _SettingsViewState extends State { middle: Text(AppLocalizations.of(context).settings), ), child: SafeArea( - child: Stack( - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).points, - style: CustomTheme.rowTitle, - ), + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).points, + style: CustomTheme.rowTitle, ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), - child: CupertinoFormSection.insetGrouped( - backgroundColor: CustomTheme.backgroundColor, - margin: EdgeInsets.zero, - children: [ - CustomFormRow( - prefixText: 'Cabo-Strafe', - prefixIcon: CupertinoIcons.bolt_fill, - suffixWidget: CustomStepper( - key: _stepperKey1, - initialValue: ConfigService.caboPenalty, - minValue: 0, - maxValue: 50, - step: 1, - onChanged: (newCaboPenalty) { - setState(() { - ConfigService.setCaboPenalty(newCaboPenalty); - ConfigService.caboPenalty = newCaboPenalty; - }); - }, - ), - ), - CustomFormRow( - prefixText: 'Punkte-Limit', - prefixIcon: FontAwesomeIcons.bullseye, - suffixWidget: CustomStepper( - key: _stepperKey2, - initialValue: ConfigService.pointLimit, - minValue: 30, - maxValue: 1000, - step: 10, - onChanged: (newPointLimit) { - setState(() { - ConfigService.setPointLimit(newPointLimit); - ConfigService.pointLimit = newPointLimit; - }); - }, - ), - ), - CustomFormRow( - prefixText: - AppLocalizations.of(context).reset_to_default, - prefixIcon: CupertinoIcons.arrow_counterclockwise, - onPressed: () { - ConfigService.resetConfig(); + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).cabo_penalty, + prefixIcon: CupertinoIcons.bolt_fill, + suffixWidget: CustomStepper( + key: _stepperKey1, + initialValue: ConfigService.caboPenalty, + minValue: 0, + maxValue: 50, + step: 1, + onChanged: (newCaboPenalty) { setState(() { - _stepperKey1 = UniqueKey(); - _stepperKey2 = UniqueKey(); + ConfigService.setCaboPenalty(newCaboPenalty); + ConfigService.caboPenalty = newCaboPenalty; }); }, - ) - ])), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).game_data, - style: CustomTheme.rowTitle, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), - child: CupertinoFormSection.insetGrouped( - backgroundColor: CustomTheme.backgroundColor, - margin: EdgeInsets.zero, - children: [ - CustomFormRow( - prefixText: AppLocalizations.of(context).import_data, - prefixIcon: CupertinoIcons.square_arrow_down, - onPressed: () async { - final status = - await LocalStorageService.importJsonFile(); - showFeedbackDialog(status); + ), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).point_limit, + prefixIcon: FontAwesomeIcons.bullseye, + suffixWidget: CustomStepper( + key: _stepperKey2, + initialValue: ConfigService.pointLimit, + minValue: 30, + maxValue: 1000, + step: 10, + onChanged: (newPointLimit) { + setState(() { + ConfigService.setPointLimit(newPointLimit); + ConfigService.pointLimit = newPointLimit; + }); }, - suffixWidget: const CupertinoListTileChevron(), ), - CustomFormRow( - prefixText: AppLocalizations.of(context).export_data, - prefixIcon: CupertinoIcons.square_arrow_up, - onPressed: () => LocalStorageService.exportGameData(), - suffixWidget: const CupertinoListTileChevron(), - ), - ])), - Padding( - padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), - child: Text( - AppLocalizations.of(context).app, - style: CustomTheme.rowTitle, - ), + ), + CustomFormRow( + prefixText: + AppLocalizations.of(context).reset_to_default, + prefixIcon: CupertinoIcons.arrow_counterclockwise, + onPressed: () { + ConfigService.resetConfig(); + setState(() { + _stepperKey1 = UniqueKey(); + _stepperKey2 = UniqueKey(); + }); + }, + ) + ])), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).game_data, + style: CustomTheme.rowTitle, ), - Padding( - padding: const EdgeInsets.fromLTRB(10, 15, 10, 0), - child: CupertinoFormSection.insetGrouped( - backgroundColor: CustomTheme.backgroundColor, - margin: EdgeInsets.zero, - children: [ - CustomFormRow( - prefixText: AppLocalizations.of(context).wiki, - prefixIcon: CupertinoIcons.book, - onPressed: () => - launchUrl(Uri.parse(Constants.GITHUB_WIKI_LINK)), - suffixWidget: const CupertinoListTileChevron(), - ), - CustomFormRow( - prefixText: - AppLocalizations.of(context).privacy_policy, - prefixIcon: CupertinoIcons.doc_append, - onPressed: () => launchUrl( - Uri.parse(Constants.PRIVACY_POLICY_LINK)), - suffixWidget: const CupertinoListTileChevron(), - ), - CustomFormRow( - prefixText: AppLocalizations.of(context).error_found, - prefixIcon: FontAwesomeIcons.github, - onPressed: () => launchUrl( - Uri.parse(Constants.GITHUB_ISSUES_LINK)), - suffixWidget: const CupertinoListTileChevron(), - ), - CustomFormRow( - prefixText: - AppLocalizations.of(context).app_version, - prefixIcon: CupertinoIcons.tag, - onPressed: null, - suffixWidget: Text(VersionService.getVersion(), - style: TextStyle( - color: CustomTheme.primaryColor, - ))), - CustomFormRow( - prefixText: AppLocalizations.of(context).build, - prefixIcon: CupertinoIcons.number, - onPressed: null, - suffixWidget: Text(VersionService.getBuildNumber(), - style: TextStyle( - color: CustomTheme.primaryColor, - ))), - ])), - ], - ), - ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 10), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).import_data, + prefixIcon: CupertinoIcons.square_arrow_down, + onPressed: () async { + final status = + await LocalStorageService.importJsonFile(); + showFeedbackDialog(status); + }, + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).export_data, + prefixIcon: CupertinoIcons.square_arrow_up, + onPressed: () => LocalStorageService.exportGameData(), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).delete_data, + prefixIcon: CupertinoIcons.trash, + onPressed: () => _deleteAllGames(), + ), + ])), + Padding( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 0), + child: Text( + AppLocalizations.of(context).app, + style: CustomTheme.rowTitle, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(10, 15, 10, 0), + child: CupertinoFormSection.insetGrouped( + backgroundColor: CustomTheme.backgroundColor, + margin: EdgeInsets.zero, + children: [ + CustomFormRow( + prefixText: AppLocalizations.of(context).wiki, + prefixIcon: CupertinoIcons.book, + onPressed: () => + launchUrl(Uri.parse(Constants.GITHUB_WIKI_LINK)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).privacy_policy, + prefixIcon: CupertinoIcons.doc_append, + onPressed: () => + launchUrl(Uri.parse(Constants.PRIVACY_POLICY_LINK)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).error_found, + prefixIcon: FontAwesomeIcons.github, + onPressed: () => + launchUrl(Uri.parse(Constants.GITHUB_ISSUES_LINK)), + suffixWidget: const CupertinoListTileChevron(), + ), + CustomFormRow( + prefixText: AppLocalizations.of(context).app_version, + prefixIcon: CupertinoIcons.tag, + onPressed: null, + suffixWidget: Text(VersionService.getVersion(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), + CustomFormRow( + prefixText: AppLocalizations.of(context).build, + prefixIcon: CupertinoIcons.number, + onPressed: null, + suffixWidget: Text(VersionService.getBuildNumber(), + style: TextStyle( + color: CustomTheme.primaryColor, + ))), + ])), + const SizedBox(height: 50) + ], + ), )), ); } + /// Shows a dialog to confirm the deletion of all game data. + /// When confirmed, it deletes all game data from local storage. + void _deleteAllGames() { + showCupertinoDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text(AppLocalizations.of(context).delete_data_title), + content: Text(AppLocalizations.of(context).delete_data_message), + actions: [ + CupertinoDialogAction( + child: Text(AppLocalizations.of(context).cancel), + onPressed: () => Navigator.pop(context), + ), + CupertinoDialogAction( + isDestructiveAction: true, + isDefaultAction: true, + child: Text(AppLocalizations.of(context).delete), + onPressed: () { + LocalStorageService.deleteAllGames(); + Navigator.pop(context); + }, + ), + ], + ); + }, + ); + } + void showFeedbackDialog(ImportStatus status) { if (status == ImportStatus.canceled) return; final (title, message) = _getDialogContent(status); diff --git a/pubspec.yaml b/pubspec.yaml index 31146e6..ca5ef95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.0+467 +version: 0.4.2+470 environment: sdk: ^3.5.4 From 9e0b8f937420588bfc212e915b58c7503bcfe3c6 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:12:44 +0200 Subject: [PATCH 067/108] Updated version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 1f8766a..12dae39 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.1+473 +version: 0.4.2+473 environment: sdk: ^3.5.4 From 00b9ac62f73b3226e8b21de265e2ba921d2e5a81 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:16:12 +0200 Subject: [PATCH 068/108] Updated en string --- lib/l10n/arb/app_en.arb | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 27efba1..782008a 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -99,7 +99,7 @@ "export_data": "Export Data", "delete_data": "Delete all Game Data", "delete_data_title": "Delete game data?", - "delete_data_message": "Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.", + "delete_data_message": "Are you sure you want to delete all game data? This action cannot be undone.", "app": "App", "import_success_title": "Import successful", diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 983a90e..138c633 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -246,7 +246,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get delete_data_message => - 'Bist du sicher, dass du alle Spieldaten löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'; + 'Are you sure you want to delete all game data? This action cannot be undone.'; @override String get app => 'App'; From 340a02207086e73ddf64375f1815b5dff701d586 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:40:26 +0200 Subject: [PATCH 069/108] Updated RoundView buttons when game is finished --- lib/presentation/views/round_view.dart | 33 ++++++++++++++------------ pubspec.yaml | 2 +- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 1e02cc3..6a31c2a 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -73,7 +73,8 @@ class _RoundViewState extends State { resizeToAvoidBottomInset: false, navigationBar: CupertinoNavigationBar( transitionBetweenRoutes: true, - middle: Text(AppLocalizations.of(context).results), + middle: Text( + '${AppLocalizations.of(context).results}${gameSession.isGameFinished ? ' \u{1F512}' : ''}'), leading: CupertinoButton( padding: EdgeInsets.zero, onPressed: () => @@ -293,21 +294,23 @@ class _RoundViewState extends State { : null, child: Text(AppLocalizations.of(context).done), ), - CupertinoButton( - onPressed: _areRoundInputsValid() - ? () { - _finishRound(); - LocalStorageService.saveGameSessions(); - if (widget.gameSession.isGameFinished == true) { - Navigator.pop(context); - } else { - Navigator.pop( - context, widget.roundNumber + 1); + if (!widget.gameSession.isGameFinished) + CupertinoButton( + onPressed: _areRoundInputsValid() + ? () { + _finishRound(); + LocalStorageService.saveGameSessions(); + if (widget.gameSession.isGameFinished == + true) { + Navigator.pop(context); + } else { + Navigator.pop( + context, widget.roundNumber + 1); + } } - } - : null, - child: Text(AppLocalizations.of(context).next_round), - ), + : null, + child: Text(AppLocalizations.of(context).next_round), + ), ], ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 12dae39..71d96fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.2+473 +version: 0.4.2+474 environment: sdk: ^3.5.4 From 5a1c3ef09aaaabaa3a28af138a67656c2b2595ff Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:51:21 +0200 Subject: [PATCH 070/108] Changed lock emoji to CuperinoIcons.lock and placed it in trailing of app bar --- lib/presentation/views/round_view.dart | 9 +++++++-- pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 6a31c2a..0f4e809 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -73,14 +73,19 @@ class _RoundViewState extends State { resizeToAvoidBottomInset: false, navigationBar: CupertinoNavigationBar( transitionBetweenRoutes: true, - middle: Text( - '${AppLocalizations.of(context).results}${gameSession.isGameFinished ? ' \u{1F512}' : ''}'), leading: CupertinoButton( padding: EdgeInsets.zero, onPressed: () => {LocalStorageService.saveGameSessions(), Navigator.pop(context)}, child: Text(AppLocalizations.of(context).cancel), ), + middle: Text(AppLocalizations.of(context).results), + trailing: widget.gameSession.isGameFinished + ? const Icon( + CupertinoIcons.lock, + size: 25, + ) + : null, ), child: Stack( children: [ diff --git a/pubspec.yaml b/pubspec.yaml index 71d96fb..553ec0f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.2+474 +version: 0.4.2+476 environment: sdk: ^3.5.4 From df3a16151d39200ce1aa338d4c264af2b64667c8 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:51:54 +0200 Subject: [PATCH 071/108] Simplified comparison --- lib/presentation/views/round_view.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 0f4e809..b4d2212 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -305,8 +305,7 @@ class _RoundViewState extends State { ? () { _finishRound(); LocalStorageService.saveGameSessions(); - if (widget.gameSession.isGameFinished == - true) { + if (widget.gameSession.isGameFinished) { Navigator.pop(context); } else { Navigator.pop( From 1e1601e431920f3a5b2958c8d493aa0424cc1720 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 11:52:23 +0200 Subject: [PATCH 072/108] Updated version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 553ec0f..8e8a264 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.2+476 +version: 0.4.3+476 environment: sdk: ^3.5.4 From cf0d7af343ced4affd85b195b0e5b46fb46fb6e9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 13:56:02 +0200 Subject: [PATCH 073/108] Corrected scaling --- lib/presentation/views/round_view.dart | 36 +++----------------------- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index b4d2212..39e5cc8 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -67,7 +67,6 @@ class _RoundViewState extends State { @override Widget build(BuildContext context) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; - final maxLength = widget.gameSession.getMaxLengthOfPlayerNames(); return CupertinoPageScaffold( resizeToAvoidBottomInset: false, @@ -126,9 +125,8 @@ class _RoundViewState extends State { return MapEntry( index, Padding( - padding: EdgeInsets.symmetric( - horizontal: 4 + - _getSegmentedControlPadding(maxLength), + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 6, ), child: FittedBox( @@ -137,10 +135,8 @@ class _RoundViewState extends State { name, textAlign: TextAlign.center, maxLines: 1, - style: TextStyle( + style: const TextStyle( fontWeight: FontWeight.bold, - fontSize: _getSegmentedControlFontSize( - maxLength), ), ), ), @@ -393,32 +389,6 @@ class _RoundViewState extends State { } } - double _getSegmentedControlFontSize(int maxLength) { - if (maxLength > 8) { - // 9 - 12 characters - return 9.0; - } else if (maxLength > 4) { - // 5 - 8 characters - return 15.0; - } else { - // 0 - 4 characters - return 18.0; - } - } - - double _getSegmentedControlPadding(int maxLength) { - if (maxLength > 8) { - // 9 - 12 characters - return 0.0; - } else if (maxLength > 4) { - // 5 - 8 characters - return 5.0; - } else { - // 0 - 4 characters - return 8.0; - } - } - @override void dispose() { for (final controller in _scoreControllerList) { diff --git a/pubspec.yaml b/pubspec.yaml index 8e8a264..ab51129 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.3+476 +version: 0.4.3+477 environment: sdk: ^3.5.4 From 546ac1d9967e7d289eb9af82bd8f2ca697d09d40 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 11 Jul 2025 14:06:06 +0200 Subject: [PATCH 074/108] Updates constant names and lint rule --- analysis_options.yaml | 1 - lib/core/constants.dart | 12 ++++++------ lib/presentation/views/about_view.dart | 6 +++--- lib/presentation/views/main_menu_view.dart | 2 +- lib/presentation/views/settings_view.dart | 6 +++--- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 46e2dbe..b3623a1 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -11,4 +11,3 @@ linter: prefer_const_literals_to_create_immutables: true unnecessary_const: true lines_longer_than_80_chars: false - constant_identifier_names: false diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 5b6e90e..e716464 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -3,14 +3,14 @@ import 'package:rate_my_app/rate_my_app.dart'; class Constants { static const String appDevPhase = 'Beta'; - static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; - static const String GITHUB_LINK = 'https://github.felixkirchner.de'; - static const String GITHUB_ISSUES_LINK = + static const String kInstagramLink = 'https://instagram.felixkirchner.de'; + static const String kGithubLink = 'https://github.felixkirchner.de'; + static const String kGithubIssuesLink = 'https://cabocounter-issues.felixkirchner.de'; - static const String GITHUB_WIKI_LINK = + static const String kGithubWikiLink = 'https://cabocounter-wiki.felixkirchner.de'; - static const String EMAIL = 'cabocounter@felixkirchner.de'; - static const String PRIVACY_POLICY_LINK = + static const String kEmail = 'cabocounter@felixkirchner.de'; + static const String kPrivacyPolicyLink = 'https://www.privacypolicies.com/live/1b3759d4-b2f1-4511-8e3b-21bb1626be68'; static RateMyApp rateMyApp = RateMyApp( diff --git a/lib/presentation/views/about_view.dart b/lib/presentation/views/about_view.dart index 128f91f..481e294 100644 --- a/lib/presentation/views/about_view.dart +++ b/lib/presentation/views/about_view.dart @@ -60,15 +60,15 @@ class AboutView extends StatelessWidget { children: [ IconButton( onPressed: () => - launchUrl(Uri.parse(Constants.INSTAGRAM_LINK)), + launchUrl(Uri.parse(Constants.kInstagramLink)), icon: const Icon(FontAwesomeIcons.instagram)), IconButton( onPressed: () => - launchUrl(Uri.parse('mailto:${Constants.EMAIL}')), + launchUrl(Uri.parse('mailto:${Constants.kEmail}')), icon: const Icon(CupertinoIcons.envelope)), IconButton( onPressed: () => - launchUrl(Uri.parse(Constants.GITHUB_LINK)), + launchUrl(Uri.parse(Constants.kGithubLink)), icon: const Icon(FontAwesomeIcons.github)), ], ), diff --git a/lib/presentation/views/main_menu_view.dart b/lib/presentation/views/main_menu_view.dart index fa537c6..86ff208 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -227,7 +227,7 @@ class _MainMenuViewState extends State { final Uri emailUri = Uri( scheme: 'mailto', - path: Constants.EMAIL, + path: Constants.kEmail, query: 'subject=$emailSubject' '&body=$emailBody', ); diff --git a/lib/presentation/views/settings_view.dart b/lib/presentation/views/settings_view.dart index ab8ec7d..d6f0833 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -149,21 +149,21 @@ class _SettingsViewState extends State { prefixText: AppLocalizations.of(context).wiki, prefixIcon: CupertinoIcons.book, onPressed: () => - launchUrl(Uri.parse(Constants.GITHUB_WIKI_LINK)), + launchUrl(Uri.parse(Constants.kGithubWikiLink)), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( prefixText: AppLocalizations.of(context).privacy_policy, prefixIcon: CupertinoIcons.doc_append, onPressed: () => - launchUrl(Uri.parse(Constants.PRIVACY_POLICY_LINK)), + launchUrl(Uri.parse(Constants.kPrivacyPolicyLink)), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( prefixText: AppLocalizations.of(context).error_found, prefixIcon: FontAwesomeIcons.github, onPressed: () => - launchUrl(Uri.parse(Constants.GITHUB_ISSUES_LINK)), + launchUrl(Uri.parse(Constants.kGithubIssuesLink)), suffixWidget: const CupertinoListTileChevron(), ), CustomFormRow( From c155d8c5cadec5cadda27d925dfa7472d2d84b6f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 12 Jul 2025 20:46:32 +0200 Subject: [PATCH 075/108] HOTFIX: Graph showed wrong data --- lib/presentation/views/graph_view.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 7736bb1..e9ed4d1 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -77,7 +77,7 @@ class _GraphViewState extends State { for (var round in rounds) { for (int i = 0; i < playerCount; i++) { - runningTotals[i] += round.scores[i]; + runningTotals[i] += round.scoreUpdates[i]; cumulativeScores[i].add(runningTotals[i]); } } diff --git a/pubspec.yaml b/pubspec.yaml index ab51129..2f82cdf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.3+477 +version: 0.4.3+479 environment: sdk: ^3.5.4 From b9c3e1dd4e70ba142569637b23f76b78867acc19 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 12 Jul 2025 21:03:27 +0200 Subject: [PATCH 076/108] Graph starts at round 0 now where all players have 0 points --- lib/presentation/views/graph_view.dart | 25 +++++++++++++++++++------ pubspec.yaml | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index e9ed4d1..4c03dd4 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -88,15 +88,28 @@ class _GraphViewState extends State { /// Each series contains data points for each round return List.generate(playerCount, (i) { final data = List.generate( + cumulativeScores[i].length + 1, + (j) => ( + j, + j == 0 + ? 0 // 0 Points at at the start of the game + + // Adds a small jitter to the cumulative scores to prevent overlapping data points in the graph. + // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. + : cumulativeScores[i][j - 1] + (i - playerCount ~/ 2) * jitterStep + ), + ); /*List.generate( cumulativeScores[i].length, (j) => ( - j + 1, - - // Add a small jitter to the cumulative scores to prevent overlapping data points in the graph. - // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. - cumulativeScores[i][j] + (i - playerCount ~/ 2) * jitterStep + j, + j == 0 + ? 0 // In Runde 0 immer 0 Punkte + : + // Add a small jitter to the cumulative scores to prevent overlapping data points in the graph. + // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. + cumulativeScores[i][j] + (i - playerCount ~/ 2) * jitterStep ), - ); + );*/ /// Create a LineSeries for the player /// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score. diff --git a/pubspec.yaml b/pubspec.yaml index 2f82cdf..1440d14 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.3+479 +version: 0.4.3+481 environment: sdk: ^3.5.4 From 5780155954c386d11ff81ab411b8fd5290a4b574 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sat, 12 Jul 2025 21:03:51 +0200 Subject: [PATCH 077/108] Adjusted jitterStep --- lib/presentation/views/graph_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 4c03dd4..9fa4e0a 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -82,7 +82,7 @@ class _GraphViewState extends State { } } - const double jitterStep = 0.15; + const double jitterStep = 0.05; /// Create a list of LineSeries for each player /// Each series contains data points for each round From 97da4cadffa6efa410cf41a034529ccc14acd038 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:21:37 +0200 Subject: [PATCH 078/108] Removed dead code --- lib/presentation/views/graph_view.dart | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 9fa4e0a..a6e6042 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -98,18 +98,7 @@ class _GraphViewState extends State { // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. : cumulativeScores[i][j - 1] + (i - playerCount ~/ 2) * jitterStep ), - ); /*List.generate( - cumulativeScores[i].length, - (j) => ( - j, - j == 0 - ? 0 // In Runde 0 immer 0 Punkte - : - // Add a small jitter to the cumulative scores to prevent overlapping data points in the graph. - // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. - cumulativeScores[i][j] + (i - playerCount ~/ 2) * jitterStep - ), - );*/ + ); /// Create a LineSeries for the player /// The xValueMapper maps the round number, and the yValueMapper maps the cumulative score. From 0b1b71e227f7785a00a41e1dfd383ab72ae5dd62 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:37:16 +0200 Subject: [PATCH 079/108] Updated Y-Axis and removed values under y = 0 --- lib/presentation/views/graph_view.dart | 11 +++++++---- pubspec.yaml | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index a6e6042..21f37d8 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -40,7 +40,10 @@ class _GraphViewState extends State { interval: 1, decimalPlaces: 0, ), - primaryYAxis: const NumericAxis(), + primaryYAxis: const NumericAxis( + interval: 1, + decimalPlaces: 0, + ), series: getCumulativeScores(), ), ) @@ -82,7 +85,7 @@ class _GraphViewState extends State { } } - const double jitterStep = 0.05; + const double jitterStep = 0.03; /// Create a list of LineSeries for each player /// Each series contains data points for each round @@ -91,8 +94,8 @@ class _GraphViewState extends State { cumulativeScores[i].length + 1, (j) => ( j, - j == 0 - ? 0 // 0 Points at at the start of the game + j == 0 || cumulativeScores[i][j - 1] == 0 + ? 0 // 0 Points at at the start of the game OR value is 0 (dont subtract jitter step) // Adds a small jitter to the cumulative scores to prevent overlapping data points in the graph. // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. diff --git a/pubspec.yaml b/pubspec.yaml index 1440d14..c793316 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.3+481 +version: 0.4.3+483 environment: sdk: ^3.5.4 From af630539dbf31edd8d6298f6ba20106c8cfe0d21 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:46:32 +0200 Subject: [PATCH 080/108] Changed overflow mode --- lib/presentation/views/graph_view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 21f37d8..09aba16 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -35,7 +35,9 @@ class _GraphViewState extends State { padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), child: SfCartesianChart( legend: const Legend( - isVisible: true, position: LegendPosition.bottom), + overflowMode: LegendItemOverflowMode.wrap, + isVisible: true, + position: LegendPosition.bottom), primaryXAxis: const NumericAxis( interval: 1, decimalPlaces: 0, From 7733d3bd5c8ba7f4c8f11dd4b3dca22ad52fac65 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:55:28 +0200 Subject: [PATCH 081/108] Replaced string & if statement with visibility widget --- lib/presentation/views/active_game_view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 9ecae1b..704952a 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -121,7 +121,7 @@ class _ActiveGameViewState extends State { children: [ CupertinoListTile( title: Text( - AppLocalizations.of(context).statistics, + AppLocalizations.of(context).game_process, ), backgroundColorActivated: CustomTheme.backgroundColor, @@ -131,8 +131,9 @@ class _ActiveGameViewState extends State { builder: (_) => GraphView( gameSession: gameSession, )))), - if (!gameSession.isPointsLimitEnabled) - CupertinoListTile( + Visibility( + visible: !gameSession.isPointsLimitEnabled, + child: CupertinoListTile( title: Text( AppLocalizations.of(context).end_game, style: gameSession.roundNumber > 1 && @@ -148,6 +149,7 @@ class _ActiveGameViewState extends State { _showEndGameDialog(); } }), + ), CupertinoListTile( title: Text( AppLocalizations.of(context).delete_game, From fee6cc3a339f45c503255627f3c8bd01b5a77716 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:56:32 +0200 Subject: [PATCH 082/108] updated accessability of graph view --- lib/presentation/views/graph_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index 09aba16..fe36de9 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -30,7 +30,7 @@ class _GraphViewState extends State { middle: Text(AppLocalizations.of(context).game_process), previousPageTitle: AppLocalizations.of(context).back, ), - child: widget.gameSession.roundNumber > 2 + child: widget.gameSession.roundNumber > 1 ? Padding( padding: const EdgeInsets.fromLTRB(0, 100, 0, 0), child: SfCartesianChart( From ee5319533a9e6f16b755e1469169bfb8617e5a90 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 00:57:08 +0200 Subject: [PATCH 083/108] Changed string for GraphView title --- lib/l10n/arb/app_de.arb | 3 +-- lib/l10n/arb/app_en.arb | 4 +--- lib/l10n/generated/app_localizations.dart | 2 +- lib/l10n/generated/app_localizations_de.dart | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- pubspec.yaml | 2 +- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 4b55a8d..b072d63 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -74,7 +74,6 @@ "done": "Fertig", "next_round": "Nächste Runde", - "statistics": "Statistiken", "end_game": "Spiel beenden", "delete_game": "Spiel löschen", "new_game_same_settings": "Neues Spiel mit gleichen Einstellungen", @@ -85,7 +84,7 @@ "end_game_message": "Möchtest du das Spiel beenden? Das Spiel wird als beendet markiert und kann nicht fortgeführt werden.", "game_process": "Spielverlauf", - "empty_graph_text": "Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.", + "empty_graph_text": "Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.", "settings": "Einstellungen", "cabo_penalty": "Cabo-Strafe", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 782008a..a649362 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -74,8 +74,6 @@ "done": "Done", "next_round": "Next Round", - - "statistics": "Statistics", "end_game": "End Game", "delete_game": "Delete Game", "new_game_same_settings": "New Game with same Settings", @@ -86,7 +84,7 @@ "end_game_message": "Do you want to end the game? The game gets marked as finished and cannot be continued.", "game_process": "Scoring History", - "empty_graph_text": "You must play at least two rounds for the game progress graph to be displayed.", + "empty_graph_text": "You must play at least one round for the game progress graph to be displayed.", "settings": "Settings", "cabo_penalty": "Cabo Penalty", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index d805b45..c54cca0 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -479,7 +479,7 @@ abstract class AppLocalizations { /// No description provided for @empty_graph_text. /// /// In de, this message translates to: - /// **'Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'** + /// **'Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'** String get empty_graph_text; /// No description provided for @settings. diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 4acdb1c..7bd7af0 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -212,7 +212,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get empty_graph_text => - 'Du musst mindestens zwei Runden spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'; + 'Du musst mindestens eine Runde spielen, damit der Graph des Spielverlaufes angezeigt werden kann.'; @override String get settings => 'Einstellungen'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 138c633..5403a95 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -209,7 +209,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get empty_graph_text => - 'You must play at least two rounds for the game progress graph to be displayed.'; + 'You must play at least one round for the game progress graph to be displayed.'; @override String get settings => 'Settings'; diff --git a/pubspec.yaml b/pubspec.yaml index c793316..8241466 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.3+483 +version: 0.4.4+484 environment: sdk: ^3.5.4 From 1157ab70ec41a30e93500c1a7143ff57c1b7b27a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 11:49:47 +0200 Subject: [PATCH 084/108] Updated comment Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/presentation/views/graph_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/graph_view.dart b/lib/presentation/views/graph_view.dart index fe36de9..d322bd0 100644 --- a/lib/presentation/views/graph_view.dart +++ b/lib/presentation/views/graph_view.dart @@ -97,7 +97,7 @@ class _GraphViewState extends State { (j) => ( j, j == 0 || cumulativeScores[i][j - 1] == 0 - ? 0 // 0 Points at at the start of the game OR value is 0 (dont subtract jitter step) + ? 0 // 0 points at the start of the game or when the value is 0 (don't subtract jitter step) // Adds a small jitter to the cumulative scores to prevent overlapping data points in the graph. // The jitter is centered around zero by subtracting playerCount ~/ 2 from the player index i. From 75568a1a4bf3657bdd8bdf6ddfece0339dfcb85e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 11:55:46 +0200 Subject: [PATCH 085/108] Updated generated files --- lib/l10n/generated/app_localizations.dart | 6 ------ lib/l10n/generated/app_localizations_de.dart | 3 --- lib/l10n/generated/app_localizations_en.dart | 3 --- pubspec.yaml | 2 +- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index c54cca0..2059f1b 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -416,12 +416,6 @@ abstract class AppLocalizations { /// **'Nächste Runde'** String get next_round; - /// No description provided for @statistics. - /// - /// In de, this message translates to: - /// **'Statistiken'** - String get statistics; - /// No description provided for @end_game. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 7bd7af0..068711f 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -178,9 +178,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get next_round => 'Nächste Runde'; - @override - String get statistics => 'Statistiken'; - @override String get end_game => 'Spiel beenden'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 5403a95..06b5c03 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -175,9 +175,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get next_round => 'Next Round'; - @override - String get statistics => 'Statistics'; - @override String get end_game => 'End Game'; diff --git a/pubspec.yaml b/pubspec.yaml index 8241466..da2c087 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.4+484 +version: 0.4.4+485 environment: sdk: ^3.5.4 From 01e0c70ac89e52c07e0223a303915d5633265612 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 12:30:34 +0200 Subject: [PATCH 086/108] Updated version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20b0cae..23ec7d3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CABO Counter -![Version](https://img.shields.io/badge/Version-0.3.8-orange) +![Version](https://img.shields.io/badge/Version-0.4.4-orange) ![Flutter](https://img.shields.io/badge/Flutter-3.32.1-blue?logo=flutter) ![Dart](https://img.shields.io/badge/Dart-3.8.1-blue?logo=dart) ![iOS](https://img.shields.io/badge/iOS-18.5-white?logo=apple) From d296465b0422581620cabf0b34b41b8a2933d38d Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 19:56:31 +0200 Subject: [PATCH 087/108] Updated point reduction --- lib/data/game_session.dart | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/data/game_session.dart b/lib/data/game_session.dart index 36c4c4e..d40e105 100644 --- a/lib/data/game_session.dart +++ b/lib/data/game_session.dart @@ -282,8 +282,8 @@ class GameSession extends ChangeNotifier { for (int i = 0; i < players.length; i++) { if (playerScores[i] == pointLimit) { print('${players[i]} hat genau 100 Punkte erreicht und bekommt ' - 'deswegen 50 Punkte abgezogen'); - roundList[roundNumber - 1].scoreUpdates[i] -= 50; + 'deswegen ${(pointLimit / 2).round()} Punkte abgezogen'); + roundList[roundNumber - 1].scoreUpdates[i] -= (pointLimit / 2).round(); } } _sumPoints(); diff --git a/pubspec.yaml b/pubspec.yaml index da2c087..7946bbb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.4+485 +version: 0.4.4+486 environment: sdk: ^3.5.4 From 68477158e5768519f59a58788c7e2e54f1931b2f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Sun, 13 Jul 2025 23:57:15 +0200 Subject: [PATCH 088/108] Implemented bonus popup --- lib/data/game_session.dart | 11 +++-- lib/presentation/views/round_view.dart | 65 +++++++++++++++++++++++--- pubspec.yaml | 2 +- test/data/game_session_test.dart | 12 ++--- 4 files changed, 74 insertions(+), 16 deletions(-) diff --git a/lib/data/game_session.dart b/lib/data/game_session.dart index d40e105..54c095b 100644 --- a/lib/data/game_session.dart +++ b/lib/data/game_session.dart @@ -243,10 +243,11 @@ class GameSession extends ChangeNotifier { /// It then checks if any player has exceeded 100 points. If so, it sets /// isGameFinished to true and calls the _setWinner() method to determine /// the winner. - Future updatePoints() async { + List updatePoints() { + List bonusPlayers = []; _sumPoints(); if (isPointsLimitEnabled) { - _checkHundredPointsReached(); + bonusPlayers = _checkHundredPointsReached(); for (int i = 0; i < playerScores.length; i++) { if (playerScores[i] > pointLimit) { @@ -258,6 +259,7 @@ class GameSession extends ChangeNotifier { } } notifyListeners(); + return bonusPlayers; } @visibleForTesting @@ -278,15 +280,18 @@ class GameSession extends ChangeNotifier { /// Checks if a player has reached 100 points in the current round. /// If so, it updates the [scoreUpdate] List by subtracting 50 points from /// the corresponding round update. - void _checkHundredPointsReached() { + List _checkHundredPointsReached() { + List bonusPlayers = []; for (int i = 0; i < players.length; i++) { if (playerScores[i] == pointLimit) { + bonusPlayers.add(i); print('${players[i]} hat genau 100 Punkte erreicht und bekommt ' 'deswegen ${(pointLimit / 2).round()} Punkte abgezogen'); roundList[roundNumber - 1].scoreUpdates[i] -= (pointLimit / 2).round(); } } _sumPoints(); + return bonusPlayers; } /// Determines the winner of the game session. diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 39e5cc8..602c702 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -287,8 +287,11 @@ class _RoundViewState extends State { children: [ CupertinoButton( onPressed: _areRoundInputsValid() - ? () { - _finishRound(); + ? () async { + List boni = _finishRound(); + if (boni.isNotEmpty) { + await _showBonusPopup(context, boni); + } LocalStorageService.saveGameSessions(); Navigator.pop(context); } @@ -298,8 +301,11 @@ class _RoundViewState extends State { if (!widget.gameSession.isGameFinished) CupertinoButton( onPressed: _areRoundInputsValid() - ? () { - _finishRound(); + ? () async { + List boni = _finishRound(); + if (boni.isNotEmpty) { + await _showBonusPopup(context, boni); + } LocalStorageService.saveGameSessions(); if (widget.gameSession.isGameFinished) { Navigator.pop(context); @@ -359,7 +365,7 @@ class _RoundViewState extends State { /// every player. If the round is the highest round played in this game, /// it expands the player score lists. At the end it updates the score /// array for the game. - void _finishRound() { + List _finishRound() { print('===================================='); print('Runde ${widget.roundNumber} beendet'); // The shown round is smaller than the newest round @@ -381,12 +387,59 @@ class _RoundViewState extends State { widget.gameSession.calculateScoredPoints( widget.roundNumber, roundScores, _caboPlayerIndex); } - widget.gameSession.updatePoints(); + List bonusPlayers = widget.gameSession.updatePoints(); if (widget.gameSession.isGameFinished == true) { print('Das Spiel ist beendet'); } else if (widget.roundNumber == widget.gameSession.roundNumber) { widget.gameSession.increaseRound(); } + return bonusPlayers; + } + + /// Shows a popup dialog with the bonus information. + Future _showBonusPopup( + BuildContext context, List bonusPlayers) async { + print('Bonus Popup wird angezeigt'); + int pointLimit = widget.gameSession.pointLimit; + int bonusPoints = (pointLimit / 2).round(); + + String resultText = _getPopupString(pointLimit, bonusPoints, bonusPlayers); + + await showCupertinoDialog( + context: context, + builder: (context) => CupertinoAlertDialog( + title: const Text('Bonus!'), + content: Text(resultText), + actions: [ + CupertinoDialogAction( + child: const Text('OK'), + onPressed: () => Navigator.of(context).pop(true), + ), + ], + ), + ); + return true; + } + + /// Generates the string for the bonus popup. + /// It takes the [pointLimit], [bonusPoints] and the list of [bonusPlayers] + /// and returns a formatted string. + String _getPopupString( + int pointLimit, int bonusPoints, List bonusPlayers) { + List nameList = + bonusPlayers.map((i) => widget.gameSession.players[i]).toList(); + String resultText = ''; + if (nameList.length == 1) { + resultText = + '${nameList.first} hat exakt das Punktelimit von $pointLimit Punkten erreicht und bekommt deshalb $bonusPoints Punkte abgezogen!'; + } else { + resultText = nameList.length == 2 + ? '${nameList[0]} & ${nameList[1]}' + : '${nameList.sublist(0, nameList.length - 1).join(', ')} & ${nameList.last}'; + resultText += + ' haben exakt das Punktelimit von $pointLimit Punkten erreicht und bekommen deshalb $bonusPoints Punkte abgezogen!'; + } + return resultText; } @override diff --git a/pubspec.yaml b/pubspec.yaml index 7946bbb..f2b4949 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.4+486 +version: 0.4.4+488 environment: sdk: ^3.5.4 diff --git a/test/data/game_session_test.dart b/test/data/game_session_test.dart index de4e284..4ca2158 100644 --- a/test/data/game_session_test.dart +++ b/test/data/game_session_test.dart @@ -114,15 +114,15 @@ void main() { expect(session.roundList[0].caboPlayerIndex, 0); }); - test('updatePoints - game not finished', () async { + test('updatePoints - game not finished', () { session.addRoundScoresToList(1, [10, 20, 30], [10, 20, 30], 0); - await session.updatePoints(); + session.updatePoints(); expect(session.isGameFinished, isFalse); }); - test('updatePoints - game finished', () async { + test('updatePoints - game finished', () { session.addRoundScoresToList(1, [101, 20, 30], [101, 20, 30], 0); - await session.updatePoints(); + session.updatePoints(); expect(session.isGameFinished, isTrue); }); @@ -154,9 +154,9 @@ void main() { expect(session.playerScores, equals([50, 0, 30])); }); - test('_setWinner via updatePoints', () async { + test('_setWinner via updatePoints', () { session.addRoundScoresToList(1, [101, 20, 30], [101, 0, 30], 1); - await session.updatePoints(); + session.updatePoints(); expect(session.winner, 'Bob'); // Bob has lowest score (20) }); }); From 0a0da96a3f68b939177c403825077e8e4b06052c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:01:11 +0200 Subject: [PATCH 089/108] Created Strings for popup --- lib/l10n/arb/app_de.arb | 16 +++++++++++++++ lib/l10n/arb/app_en.arb | 16 +++++++++++++++ lib/l10n/generated/app_localizations.dart | 13 ++++++++++++ lib/l10n/generated/app_localizations_de.dart | 17 ++++++++++++++++ lib/l10n/generated/app_localizations_en.dart | 17 ++++++++++++++++ lib/presentation/views/round_view.dart | 21 ++++++++++---------- pubspec.yaml | 2 +- 7 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index b072d63..355ab43 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -73,6 +73,22 @@ "kamikaze": "Kamikaze", "done": "Fertig", "next_round": "Nächste Runde", + "bonus_points_title": "Bonus-Punkte!", + "bonus_points_message": "{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}", + "@bonus_points_message": { + "placeholders": { + "names": { + "type": "String" + }, + "pointLimit": { + "type": "int" + }, + "bonusPoints": { + "type": "int" + } + } + }, + "end_game": "Spiel beenden", "delete_game": "Spiel löschen", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index a649362..08072f1 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -73,6 +73,22 @@ "kamikaze": "Kamikaze", "done": "Done", "next_round": "Next Round", + "bonus_points_title": "Bonus-Points!", + "bonus_points_message": "{playerCount, plural, =1{{names} has reached exactly the point limit of {pointLimit} points and therefore gets {bonusPoints} points deducted!} other{{names} have reached exactly the point limit of {pointLimit} points and therefore get {bonusPoints} points deducted!}}", + "@bonus_points_message": { + "placeholders": { + "names": { + "type": "String" + }, + "pointLimit": { + "type": "int" + }, + "bonusPoints": { + "type": "int" + } + } + }, + "end_game": "End Game", "delete_game": "Delete Game", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 2059f1b..5c91414 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -416,6 +416,19 @@ abstract class AppLocalizations { /// **'Nächste Runde'** String get next_round; + /// No description provided for @bonus_points_title. + /// + /// In de, this message translates to: + /// **'Bonus-Punkte!'** + String get bonus_points_title; + + /// No description provided for @bonus_points_message. + /// + /// In de, this message translates to: + /// **'{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}'** + String bonus_points_message( + String names, int pointLimit, int bonusPoints, num playerCount); + /// No description provided for @end_game. /// /// In de, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 068711f..2007e48 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -178,6 +178,23 @@ class AppLocalizationsDe extends AppLocalizations { @override String get next_round => 'Nächste Runde'; + @override + String get bonus_points_title => 'Bonus-Punkte!'; + + @override + String bonus_points_message( + String names, int pointLimit, int bonusPoints, num playerCount) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names haben exakt das Punktelimit von $pointLimit Punkten erreicht und bekommen deshalb jeweils $bonusPoints Punkte abgezogen!', + one: + '$names hat exakt das Punktelimit von $pointLimit Punkten erreicht und bekommt deshalb $bonusPoints Punkte abgezogen!', + ); + return '$_temp0'; + } + @override String get end_game => 'Spiel beenden'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 06b5c03..0830f23 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -175,6 +175,23 @@ class AppLocalizationsEn extends AppLocalizations { @override String get next_round => 'Next Round'; + @override + String get bonus_points_title => 'Bonus-Points!'; + + @override + String bonus_points_message( + String names, int pointLimit, int bonusPoints, num playerCount) { + String _temp0 = intl.Intl.pluralLogic( + playerCount, + locale: localeName, + other: + '$names have reached exactly the point limit of $pointLimit points and therefore get $bonusPoints points deducted!', + one: + '$names has reached exactly the point limit of $pointLimit points and therefore gets $bonusPoints points deducted!', + ); + return '$_temp0'; + } + @override String get end_game => 'End Game'; diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 602c702..fdf2b13 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -403,17 +403,18 @@ class _RoundViewState extends State { int pointLimit = widget.gameSession.pointLimit; int bonusPoints = (pointLimit / 2).round(); - String resultText = _getPopupString(pointLimit, bonusPoints, bonusPlayers); + String resultText = + _getBonusPopupMessageString(pointLimit, bonusPoints, bonusPlayers); await showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( - title: const Text('Bonus!'), + title: Text(AppLocalizations.of(context).bonus_points_title), content: Text(resultText), actions: [ CupertinoDialogAction( - child: const Text('OK'), - onPressed: () => Navigator.of(context).pop(true), + child: Text(AppLocalizations.of(context).ok), + onPressed: () => Navigator.of(context).pop(), ), ], ), @@ -421,23 +422,23 @@ class _RoundViewState extends State { return true; } - /// Generates the string for the bonus popup. + /// Generates the message string for the bonus popup. /// It takes the [pointLimit], [bonusPoints] and the list of [bonusPlayers] /// and returns a formatted string. - String _getPopupString( + String _getBonusPopupMessageString( int pointLimit, int bonusPoints, List bonusPlayers) { List nameList = bonusPlayers.map((i) => widget.gameSession.players[i]).toList(); String resultText = ''; if (nameList.length == 1) { - resultText = - '${nameList.first} hat exakt das Punktelimit von $pointLimit Punkten erreicht und bekommt deshalb $bonusPoints Punkte abgezogen!'; + resultText = AppLocalizations.of(context).bonus_points_message( + nameList.first, pointLimit, bonusPoints, nameList.length); } else { resultText = nameList.length == 2 ? '${nameList[0]} & ${nameList[1]}' : '${nameList.sublist(0, nameList.length - 1).join(', ')} & ${nameList.last}'; - resultText += - ' haben exakt das Punktelimit von $pointLimit Punkten erreicht und bekommen deshalb $bonusPoints Punkte abgezogen!'; + resultText = AppLocalizations.of(context).bonus_points_message( + resultText, pointLimit, bonusPoints, nameList.length); } return resultText; } diff --git a/pubspec.yaml b/pubspec.yaml index f2b4949..4444d73 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.4+488 +version: 0.4.5+492 environment: sdk: ^3.5.4 From db6d4690cba5f863d8c0b0f3f1fa0b978407d53c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:09:12 +0200 Subject: [PATCH 090/108] Implemented mounted checks & changed return type --- lib/presentation/views/round_view.dart | 9 +++++---- pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index fdf2b13..2d1f785 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -293,6 +293,7 @@ class _RoundViewState extends State { await _showBonusPopup(context, boni); } LocalStorageService.saveGameSessions(); + if (!context.mounted) return; Navigator.pop(context); } : null, @@ -307,9 +308,10 @@ class _RoundViewState extends State { await _showBonusPopup(context, boni); } LocalStorageService.saveGameSessions(); - if (widget.gameSession.isGameFinished) { + if (widget.gameSession.isGameFinished && + context.mounted) { Navigator.pop(context); - } else { + } else if (context.mounted) { Navigator.pop( context, widget.roundNumber + 1); } @@ -397,7 +399,7 @@ class _RoundViewState extends State { } /// Shows a popup dialog with the bonus information. - Future _showBonusPopup( + Future _showBonusPopup( BuildContext context, List bonusPlayers) async { print('Bonus Popup wird angezeigt'); int pointLimit = widget.gameSession.pointLimit; @@ -419,7 +421,6 @@ class _RoundViewState extends State { ], ), ); - return true; } /// Generates the message string for the bonus popup. diff --git a/pubspec.yaml b/pubspec.yaml index 4444d73..4d0b0f6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.5+492 +version: 0.4.5+493 environment: sdk: ^3.5.4 From c24c271c8220b5fccb47daee2786d07a43c8c2a9 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:14:53 +0200 Subject: [PATCH 091/108] Updated comment --- lib/data/game_session.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/data/game_session.dart b/lib/data/game_session.dart index 54c095b..d1402e5 100644 --- a/lib/data/game_session.dart +++ b/lib/data/game_session.dart @@ -235,7 +235,7 @@ class GameSession extends ChangeNotifier { /// This method updates the points of each player after a round. /// It first uses the _sumPoints() method to calculate the total points of each player. - /// Then, it checks if any player has reached 100 points. If so, it marks + /// Then, it checks if any player has reached 100 points. If so, saves their indices and marks /// that player as having reached 100 points in that corresponding [Round] object. /// If the game has the point limit activated, it first applies the /// _subtractPointsForReachingHundred() method to subtract 50 points @@ -243,6 +243,8 @@ class GameSession extends ChangeNotifier { /// It then checks if any player has exceeded 100 points. If so, it sets /// isGameFinished to true and calls the _setWinner() method to determine /// the winner. + /// It returns a list of players indices who reached 100 points in the current + /// round for the [RoundView] to show a popup List updatePoints() { List bonusPlayers = []; _sumPoints(); From a8298dfa21d3d6df8f06f7812d0bce340971a536 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:16:42 +0200 Subject: [PATCH 092/108] Updated variable name --- lib/presentation/views/round_view.dart | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 2d1f785..45ce0ef 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -288,9 +288,10 @@ class _RoundViewState extends State { CupertinoButton( onPressed: _areRoundInputsValid() ? () async { - List boni = _finishRound(); - if (boni.isNotEmpty) { - await _showBonusPopup(context, boni); + List bonusPlayersIndices = _finishRound(); + if (bonusPlayersIndices.isNotEmpty) { + await _showBonusPopup( + context, bonusPlayersIndices); } LocalStorageService.saveGameSessions(); if (!context.mounted) return; @@ -303,9 +304,11 @@ class _RoundViewState extends State { CupertinoButton( onPressed: _areRoundInputsValid() ? () async { - List boni = _finishRound(); - if (boni.isNotEmpty) { - await _showBonusPopup(context, boni); + List bonusPlayersIndices = + _finishRound(); + if (bonusPlayersIndices.isNotEmpty) { + await _showBonusPopup( + context, bonusPlayersIndices); } LocalStorageService.saveGameSessions(); if (widget.gameSession.isGameFinished && From 047acfecd8df9123676bc73053cb42f51e3fea6a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:17:51 +0200 Subject: [PATCH 093/108] Updated string placeholders --- lib/l10n/arb/app_de.arb | 3 +++ lib/l10n/arb/app_en.arb | 3 +++ lib/presentation/views/round_view.dart | 8 ++++++-- pubspec.yaml | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 355ab43..93215ed 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -77,6 +77,9 @@ "bonus_points_message": "{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}", "@bonus_points_message": { "placeholders": { + "playerCount": { + "type": "int" + }, "names": { "type": "String" }, diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 08072f1..19695a5 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -77,6 +77,9 @@ "bonus_points_message": "{playerCount, plural, =1{{names} has reached exactly the point limit of {pointLimit} points and therefore gets {bonusPoints} points deducted!} other{{names} have reached exactly the point limit of {pointLimit} points and therefore get {bonusPoints} points deducted!}}", "@bonus_points_message": { "placeholders": { + "playerCount": { + "type": "int" + }, "names": { "type": "String" }, diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 45ce0ef..a821fb5 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -436,13 +436,17 @@ class _RoundViewState extends State { String resultText = ''; if (nameList.length == 1) { resultText = AppLocalizations.of(context).bonus_points_message( - nameList.first, pointLimit, bonusPoints, nameList.length); + nameList.length, nameList.first, pointLimit, bonusPoints); } else { resultText = nameList.length == 2 ? '${nameList[0]} & ${nameList[1]}' : '${nameList.sublist(0, nameList.length - 1).join(', ')} & ${nameList.last}'; resultText = AppLocalizations.of(context).bonus_points_message( - resultText, pointLimit, bonusPoints, nameList.length); + nameList.length, + resultText, + pointLimit, + bonusPoints, + ); } return resultText; } diff --git a/pubspec.yaml b/pubspec.yaml index 4d0b0f6..9db5d4b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.5+493 +version: 0.4.5+494 environment: sdk: ^3.5.4 From 79d0bdd19b7341a3a33dc7552f1f4d5e102a19cb Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 10:19:51 +0200 Subject: [PATCH 094/108] Generated files update --- lib/l10n/generated/app_localizations.dart | 2 +- lib/l10n/generated/app_localizations_de.dart | 2 +- lib/l10n/generated/app_localizations_en.dart | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 5c91414..0a902f6 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -427,7 +427,7 @@ abstract class AppLocalizations { /// In de, this message translates to: /// **'{playerCount, plural, =1{{names} hat exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommt deshalb {bonusPoints} Punkte abgezogen!} other{{names} haben exakt das Punktelimit von {pointLimit} Punkten erreicht und bekommen deshalb jeweils {bonusPoints} Punkte abgezogen!}}'** String bonus_points_message( - String names, int pointLimit, int bonusPoints, num playerCount); + int playerCount, String names, int pointLimit, int bonusPoints); /// No description provided for @end_game. /// diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 2007e48..7a71d00 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -183,7 +183,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String bonus_points_message( - String names, int pointLimit, int bonusPoints, num playerCount) { + int playerCount, String names, int pointLimit, int bonusPoints) { String _temp0 = intl.Intl.pluralLogic( playerCount, locale: localeName, diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index 0830f23..4d4d663 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -180,7 +180,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String bonus_points_message( - String names, int pointLimit, int bonusPoints, num playerCount) { + int playerCount, String names, int pointLimit, int bonusPoints) { String _temp0 = intl.Intl.pluralLogic( playerCount, locale: localeName, From 16d1c017afbf2df8b3cc0a2f0e64fc8ee4bacc81 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:06:02 +0200 Subject: [PATCH 095/108] Updated _getPlacementPrefix method for dense ranks --- lib/presentation/views/active_game_view.dart | 65 ++++++++++++-------- pubspec.yaml | 2 +- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 704952a..c280e59 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -58,7 +58,8 @@ class _ActiveGameViewState extends State { return CupertinoListTile( title: Row( children: [ - _getPlacementPrefix(index), + _getPlacementPrefix( + index, gameSession.playerScores), const SizedBox(width: 5), Text( gameSession.players[playerIndex], @@ -266,36 +267,52 @@ class _ActiveGameViewState extends State { playerIndices.sort((a, b) { int scoreA = gameSession.playerScores[a]; int scoreB = gameSession.playerScores[b]; - return scoreA.compareTo(scoreB); + if (scoreA != scoreB) { + return scoreA.compareTo(scoreB); + } + return a.compareTo(b); }); return playerIndices; } - /// Returns a widget that displays the placement prefix based on the index. - /// First three places are represented by medals, and the rest are numbered. - /// [index] is the index of the player in the descending sorted list. - Widget _getPlacementPrefix(int index) { - switch (index) { - case 0: - return const Text( - '\u{1F947}', - style: TextStyle(fontSize: 22), - ); + /// Returns a widget representing the placement prefix for a player based on their index. + /// [index] is the index of the player in [players] list, + /// [playerScores] is a list of the players scores. + Widget _getPlacementPrefix(int index, playerScores) { + int placement = _calculateDenseRank(index, playerScores); + return _getPlacementTextWidget(placement); + } + + /// Calculates the dense rank for a player based on their index in the sorted list of players. + int _calculateDenseRank(int index, List playerScores) { + List sortedIndices = _getSortedPlayerIndices(); + List denseRanks = []; + int rank = 1; + for (int i = 0; i < sortedIndices.length; i++) { + if (i > 0) { + int prevScore = playerScores[sortedIndices[i - 1]]; + int currScore = playerScores[sortedIndices[i]]; + if (currScore != prevScore) { + rank++; + } + } + denseRanks.add(rank); + } + return denseRanks[index]; + } + + /// Returns a text widget representing the placement text based on the given placement number. + Text _getPlacementTextWidget(int placement) { + switch (placement) { case 1: - return const Text( - '\u{1F948}', - style: TextStyle(fontSize: 22), - ); + return const Text('\u{1F947}', style: TextStyle(fontSize: 22)); // 🥇 case 2: - return const Text( - '\u{1F949}', - style: TextStyle(fontSize: 22), - ); + return const Text('\u{1F948}', style: TextStyle(fontSize: 22)); // 🥈 + case 3: + return const Text('\u{1F949}', style: TextStyle(fontSize: 22)); // 🥉 default: - return Text( - ' ${index + 1}.', - style: const TextStyle(fontWeight: FontWeight.bold), - ); + return Text('$placement.', + style: const TextStyle(fontWeight: FontWeight.bold)); } } diff --git a/pubspec.yaml b/pubspec.yaml index 9db5d4b..d50b43a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.5+494 +version: 0.4.6+497 environment: sdk: ^3.5.4 From 48784fd29048c4b8bd246c95aa1b56600e1673fe Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:07:14 +0200 Subject: [PATCH 096/108] Updated comments --- lib/presentation/views/active_game_view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index c280e59..427fdc8 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -316,6 +316,7 @@ class _ActiveGameViewState extends State { } } + /// Shows a dialog to confirm deleting the game session. Future _showDeleteGameDialog() async { return await showCupertinoDialog( context: context, @@ -348,6 +349,8 @@ class _ActiveGameViewState extends State { false; } + /// Removes the game session in the game manager and navigates back to the previous screen. + /// If the game session does not exist in the game list, it shows an error dialog. Future _removeGameSession(GameSession gameSession) async { if (gameManager.gameExistsInGameList(gameSession.id)) { Navigator.pop(context); From f8bbbb04f3af4f08e571612bd1e8ca2061464e3a Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:12:32 +0200 Subject: [PATCH 097/108] Added type annotation --- lib/presentation/views/active_game_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 427fdc8..8698c62 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -278,7 +278,7 @@ class _ActiveGameViewState extends State { /// Returns a widget representing the placement prefix for a player based on their index. /// [index] is the index of the player in [players] list, /// [playerScores] is a list of the players scores. - Widget _getPlacementPrefix(int index, playerScores) { + Widget _getPlacementPrefix(int index, List playerScores) { int placement = _calculateDenseRank(index, playerScores); return _getPlacementTextWidget(placement); } From 4a0ce067b5f3a91056baf9cf61fe0ffe2a554b67 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:14:22 +0200 Subject: [PATCH 098/108] Updated function _getSortedPlayerIndices() --- lib/presentation/views/active_game_view.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 8698c62..6eaee45 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -32,7 +32,10 @@ class _ActiveGameViewState extends State { return ListenableBuilder( listenable: gameSession, builder: (context, _) { - List sortedPlayerIndices = _getSortedPlayerIndices(); + List playerIndices = + List.generate(gameSession.players.length, (index) => index); + List sortedPlayerIndices = + _getSortedPlayerIndices(playerIndices); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(gameSession.gameTitle), @@ -260,9 +263,7 @@ class _ActiveGameViewState extends State { /// Returns a list of player indices sorted by their scores in /// ascending order. - List _getSortedPlayerIndices() { - List playerIndices = - List.generate(gameSession.players.length, (index) => index); + List _getSortedPlayerIndices(List playerIndices) { // Sort the indices based on the summed points playerIndices.sort((a, b) { int scoreA = gameSession.playerScores[a]; @@ -285,7 +286,7 @@ class _ActiveGameViewState extends State { /// Calculates the dense rank for a player based on their index in the sorted list of players. int _calculateDenseRank(int index, List playerScores) { - List sortedIndices = _getSortedPlayerIndices(); + List sortedIndices = _getSortedPlayerIndices(playerScores); List denseRanks = []; int rank = 1; for (int i = 0; i < sortedIndices.length; i++) { From 84e6f3f28653e95bbd7486f27c89d0f867b4454e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:14:44 +0200 Subject: [PATCH 099/108] Updated build method --- lib/presentation/views/active_game_view.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 6eaee45..2153d1d 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -29,13 +29,13 @@ class _ActiveGameViewState extends State { @override Widget build(BuildContext context) { + List playerIndices = + List.generate(gameSession.players.length, (index) => index); + List sortedPlayerIndices = _getSortedPlayerIndices(playerIndices); + return ListenableBuilder( listenable: gameSession, builder: (context, _) { - List playerIndices = - List.generate(gameSession.players.length, (index) => index); - List sortedPlayerIndices = - _getSortedPlayerIndices(playerIndices); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(gameSession.gameTitle), From 8b4a8bb86be4523807cff3ce428fbf30258e0926 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:30:55 +0200 Subject: [PATCH 100/108] Refactored and simplified --- lib/presentation/views/active_game_view.dart | 33 +++++++++----------- pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 2153d1d..a4c52ee 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -20,6 +20,8 @@ class ActiveGameView extends StatefulWidget { class _ActiveGameViewState extends State { late final GameSession gameSession; + late final List denseRanks; + late List sortedPlayerIndices; @override void initState() { @@ -29,13 +31,11 @@ class _ActiveGameViewState extends State { @override Widget build(BuildContext context) { - List playerIndices = - List.generate(gameSession.players.length, (index) => index); - List sortedPlayerIndices = _getSortedPlayerIndices(playerIndices); - return ListenableBuilder( listenable: gameSession, builder: (context, _) { + sortedPlayerIndices = _getSortedPlayerIndices(); + denseRanks = _calculateDenseRank(gameSession.playerScores); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(gameSession.gameTitle), @@ -61,8 +61,7 @@ class _ActiveGameViewState extends State { return CupertinoListTile( title: Row( children: [ - _getPlacementPrefix( - index, gameSession.playerScores), + _getPlacementTextWidget(index), const SizedBox(width: 5), Text( gameSession.players[playerIndex], @@ -263,7 +262,9 @@ class _ActiveGameViewState extends State { /// Returns a list of player indices sorted by their scores in /// ascending order. - List _getSortedPlayerIndices(List playerIndices) { + List _getSortedPlayerIndices() { + List playerIndices = + List.generate(gameSession.players.length, (index) => index); // Sort the indices based on the summed points playerIndices.sort((a, b) { int scoreA = gameSession.playerScores[a]; @@ -276,17 +277,9 @@ class _ActiveGameViewState extends State { return playerIndices; } - /// Returns a widget representing the placement prefix for a player based on their index. - /// [index] is the index of the player in [players] list, - /// [playerScores] is a list of the players scores. - Widget _getPlacementPrefix(int index, List playerScores) { - int placement = _calculateDenseRank(index, playerScores); - return _getPlacementTextWidget(placement); - } - /// Calculates the dense rank for a player based on their index in the sorted list of players. - int _calculateDenseRank(int index, List playerScores) { - List sortedIndices = _getSortedPlayerIndices(playerScores); + List _calculateDenseRank(List playerScores) { + List sortedIndices = _getSortedPlayerIndices(); List denseRanks = []; int rank = 1; for (int i = 0; i < sortedIndices.length; i++) { @@ -299,11 +292,13 @@ class _ActiveGameViewState extends State { } denseRanks.add(rank); } - return denseRanks[index]; + return denseRanks; } /// Returns a text widget representing the placement text based on the given placement number. - Text _getPlacementTextWidget(int placement) { + /// [index] is the index of the player in [players] list, + Text _getPlacementTextWidget(int index) { + int placement = denseRanks[index]; switch (placement) { case 1: return const Text('\u{1F947}', style: TextStyle(fontSize: 22)); // 🥇 diff --git a/pubspec.yaml b/pubspec.yaml index d50b43a..95fea1d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.6+497 +version: 0.4.6+504 environment: sdk: ^3.5.4 From f4120d28a9948c20214d167fc367ef763bd5d5b1 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:38:28 +0200 Subject: [PATCH 101/108] Removed late --- lib/presentation/views/active_game_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index a4c52ee..1ea0a0d 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -20,7 +20,7 @@ class ActiveGameView extends StatefulWidget { class _ActiveGameViewState extends State { late final GameSession gameSession; - late final List denseRanks; + late List denseRanks; late List sortedPlayerIndices; @override From 5a939f4447e7bfb3d8ef651a5c04b372a7c8bf4e Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:39:32 +0200 Subject: [PATCH 102/108] Unnessecary code --- lib/presentation/views/active_game_view.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 1ea0a0d..9715f77 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -35,7 +35,8 @@ class _ActiveGameViewState extends State { listenable: gameSession, builder: (context, _) { sortedPlayerIndices = _getSortedPlayerIndices(); - denseRanks = _calculateDenseRank(gameSession.playerScores); + denseRanks = _calculateDenseRank( + gameSession.playerScores, sortedPlayerIndices); return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text(gameSession.gameTitle), @@ -278,8 +279,8 @@ class _ActiveGameViewState extends State { } /// Calculates the dense rank for a player based on their index in the sorted list of players. - List _calculateDenseRank(List playerScores) { - List sortedIndices = _getSortedPlayerIndices(); + List _calculateDenseRank( + List playerScores, List sortedIndices) { List denseRanks = []; int rank = 1; for (int i = 0; i < sortedIndices.length; i++) { From f7676da88d15f0d49d559645c488aff7e3deab0c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 11:40:03 +0200 Subject: [PATCH 103/108] Added white space --- lib/presentation/views/active_game_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index 9715f77..ab07804 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -308,7 +308,7 @@ class _ActiveGameViewState extends State { case 3: return const Text('\u{1F949}', style: TextStyle(fontSize: 22)); // 🥉 default: - return Text('$placement.', + return Text(' $placement.', style: const TextStyle(fontWeight: FontWeight.bold)); } } From 4cfbaa00c0280f95dc93e804dcb419fd0cdd9a3b Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 12:20:47 +0200 Subject: [PATCH 104/108] Implemented reordering --- lib/presentation/views/round_view.dart | 96 +++++++++++++++++++++----- pubspec.yaml | 2 +- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index a821fb5..0c5b0e4 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -5,6 +5,7 @@ import 'package:cabo_counter/services/local_storage_service.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; class RoundView extends StatefulWidget { final GameSession gameSession; @@ -67,6 +68,8 @@ class _RoundViewState extends State { @override Widget build(BuildContext context) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; + final rotatedPlayers = _getRotatedPlayers(); + final originalIndices = _getOriginalIndices(); return CupertinoPageScaffold( resizeToAvoidBottomInset: false, @@ -175,9 +178,10 @@ class _RoundViewState extends State { ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: widget.gameSession.players.length, + itemCount: rotatedPlayers.length, itemBuilder: (context, index) { - final name = widget.gameSession.players[index]; + final originalIndex = originalIndices[index]; + final name = rotatedPlayers[index]; return Padding( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 20), @@ -187,13 +191,23 @@ class _RoundViewState extends State { backgroundColor: CupertinoColors.secondaryLabel, title: Row(children: [ Expanded( - child: Text( - name, - overflow: TextOverflow.ellipsis, - )) + child: Row(children: [ + Text( + name, + overflow: TextOverflow.ellipsis, + ), + Visibility( + visible: index == 0, + child: const SizedBox(width: 10), + ), + Visibility( + visible: index == 0, + child: const Icon(FontAwesomeIcons.medal, + size: 15)) + ])) ]), subtitle: Text( - '${widget.gameSession.playerScores[index]}' + '${widget.gameSession.playerScores[originalIndex]}' ' ${AppLocalizations.of(context).points}'), trailing: Row( children: [ @@ -201,7 +215,7 @@ class _RoundViewState extends State { width: 100, child: CupertinoTextField( maxLength: 3, - focusNode: _focusNodeList[index], + focusNode: _focusNodeList[originalIndex], keyboardType: const TextInputType.numberWithOptions( signed: true, @@ -216,12 +230,13 @@ class _RoundViewState extends State { 1 ? TextInputAction.done : TextInputAction.next, - controller: _scoreControllerList[index], + controller: + _scoreControllerList[originalIndex], placeholder: AppLocalizations.of(context).points, textAlign: TextAlign.center, onSubmitted: (_) => - _focusNextTextfield(index), + _focusNextTextfield(originalIndex), onChanged: (_) => setState(() {}), ), ), @@ -230,7 +245,8 @@ class _RoundViewState extends State { onTap: () { setState(() { _kamikazePlayerIndex = - (_kamikazePlayerIndex == index) + (_kamikazePlayerIndex == + originalIndex) ? null : index; }); @@ -240,17 +256,20 @@ class _RoundViewState extends State { height: 24, decoration: BoxDecoration( shape: BoxShape.circle, - color: _kamikazePlayerIndex == index + color: _kamikazePlayerIndex == + originalIndex ? CupertinoColors.systemRed : CupertinoColors .tertiarySystemFill, border: Border.all( - color: _kamikazePlayerIndex == index + color: _kamikazePlayerIndex == + originalIndex ? CupertinoColors.systemRed : CupertinoColors.systemGrey, ), ), - child: _kamikazePlayerIndex == index + child: _kamikazePlayerIndex == + originalIndex ? const Icon( CupertinoIcons.exclamationmark, size: 16, @@ -338,8 +357,12 @@ class _RoundViewState extends State { /// Focuses the next text field in the list of text fields. /// [index] is the index of the current text field. void _focusNextTextfield(int index) { - if (index < widget.gameSession.players.length - 1) { - FocusScope.of(context).requestFocus(_focusNodeList[index + 1]); + final originalIndices = _getOriginalIndices(); + final currentPos = originalIndices.indexOf(index); + + if (currentPos < originalIndices.length - 1) { + FocusScope.of(context) + .requestFocus(_focusNodeList[originalIndices[currentPos + 1]]); } else { _focusNodeList[index].unfocus(); } @@ -451,6 +474,47 @@ class _RoundViewState extends State { return resultText; } + List _getRotatedPlayers() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + widget.gameSession.players[winnerIndex], + ...widget.gameSession.players.sublist(winnerIndex + 1), + ...widget.gameSession.players.sublist(0, winnerIndex) + ]; + } + + List _getOriginalIndices() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + winnerIndex, + ...List.generate(widget.gameSession.players.length - winnerIndex - 1, + (i) => winnerIndex + i + 1), + ...List.generate(winnerIndex, (i) => i) + ]; + } + + int _getPreviousRoundWinnerIndex() { + if (widget.roundNumber == 1) { + return 0; // In der ersten Runde einfach den ersten Spieler nehmen + } + + final previousRound = widget.gameSession.roundList[widget.roundNumber - 2]; + final scores = previousRound.scores; + + // Finde den niedrigsten Score (Gewinner) + int minScore = scores[0]; + int winnerIndex = 0; + + for (int i = 1; i < scores.length; i++) { + if (scores[i] < minScore) { + minScore = scores[i]; + winnerIndex = i; + } + } + + return winnerIndex; + } + @override void dispose() { for (final controller in _scoreControllerList) { diff --git a/pubspec.yaml b/pubspec.yaml index 95fea1d..ba369cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.6+504 +version: 0.4.6+505 environment: sdk: ^3.5.4 From 37338dca766f2ef9efa467c1529b5c7f38ab58b6 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 12:34:57 +0200 Subject: [PATCH 105/108] Refactoring --- lib/presentation/views/round_view.dart | 86 ++++++++++++++------------ 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 0c5b0e4..113791c 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -354,6 +354,51 @@ class _RoundViewState extends State { ); } + /// Gets the index of the player who won the previous round. + int _getPreviousRoundWinnerIndex() { + if (widget.roundNumber == 1) { + return 0; // If it's the first round, there's no previous round, so return 0. + } + + final previousRound = widget.gameSession.roundList[widget.roundNumber - 2]; + final scores = previousRound.scoreUpdates; + + // Find the index of the player with the minimum score + int minScore = scores[0]; + int winnerIndex = 0; + + // Iterate through the scores to find the player with the minimum score + for (int i = 1; i < scores.length; i++) { + if (scores[i] < minScore) { + minScore = scores[i]; + winnerIndex = i; + } + } + + return winnerIndex; + } + + /// Rotates the players list based on the previous round's winner. + List _getRotatedPlayers() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + widget.gameSession.players[winnerIndex], + ...widget.gameSession.players.sublist(winnerIndex + 1), + ...widget.gameSession.players.sublist(0, winnerIndex) + ]; + } + + /// Gets the original indices of the players by recalculating it from the rotated list. + List _getOriginalIndices() { + final winnerIndex = _getPreviousRoundWinnerIndex(); + return [ + winnerIndex, + ...List.generate(widget.gameSession.players.length - winnerIndex - 1, + (i) => winnerIndex + i + 1), + ...List.generate(winnerIndex, (i) => i) + ]; + } + /// Focuses the next text field in the list of text fields. /// [index] is the index of the current text field. void _focusNextTextfield(int index) { @@ -474,47 +519,6 @@ class _RoundViewState extends State { return resultText; } - List _getRotatedPlayers() { - final winnerIndex = _getPreviousRoundWinnerIndex(); - return [ - widget.gameSession.players[winnerIndex], - ...widget.gameSession.players.sublist(winnerIndex + 1), - ...widget.gameSession.players.sublist(0, winnerIndex) - ]; - } - - List _getOriginalIndices() { - final winnerIndex = _getPreviousRoundWinnerIndex(); - return [ - winnerIndex, - ...List.generate(widget.gameSession.players.length - winnerIndex - 1, - (i) => winnerIndex + i + 1), - ...List.generate(winnerIndex, (i) => i) - ]; - } - - int _getPreviousRoundWinnerIndex() { - if (widget.roundNumber == 1) { - return 0; // In der ersten Runde einfach den ersten Spieler nehmen - } - - final previousRound = widget.gameSession.roundList[widget.roundNumber - 2]; - final scores = previousRound.scores; - - // Finde den niedrigsten Score (Gewinner) - int minScore = scores[0]; - int winnerIndex = 0; - - for (int i = 1; i < scores.length; i++) { - if (scores[i] < minScore) { - minScore = scores[i]; - winnerIndex = i; - } - } - - return winnerIndex; - } - @override void dispose() { for (final controller in _scoreControllerList) { From b225b28e32bb815f4c47babe4184a6d38bb214cc Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 12:35:13 +0200 Subject: [PATCH 106/108] Updated version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ba369cf..5a10eca 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.6+505 +version: 0.4.7+505 environment: sdk: ^3.5.4 From 4478c00b9d770fb5810d4de97665f56fd8d1e29f Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 12:42:30 +0200 Subject: [PATCH 107/108] Corrected kamikaze index --- lib/presentation/views/round_view.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/presentation/views/round_view.dart b/lib/presentation/views/round_view.dart index 113791c..7c62120 100644 --- a/lib/presentation/views/round_view.dart +++ b/lib/presentation/views/round_view.dart @@ -248,7 +248,7 @@ class _RoundViewState extends State { (_kamikazePlayerIndex == originalIndex) ? null - : index; + : originalIndex; }); }, child: Container( diff --git a/pubspec.yaml b/pubspec.yaml index 5a10eca..6773a53 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: cabo_counter description: "Mobile app for the card game Cabo" publish_to: 'none' -version: 0.4.7+505 +version: 0.4.7+506 environment: sdk: ^3.5.4 From 287d57d973d04825412549525961a0a693f3a5a6 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Mon, 14 Jul 2025 14:19:25 +0200 Subject: [PATCH 108/108] Update Version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23ec7d3..66eab9f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CABO Counter -![Version](https://img.shields.io/badge/Version-0.4.4-orange) +![Version](https://img.shields.io/badge/Version-0.4.7-orange) ![Flutter](https://img.shields.io/badge/Flutter-3.32.1-blue?logo=flutter) ![Dart](https://img.shields.io/badge/Dart-3.8.1-blue?logo=dart) ![iOS](https://img.shields.io/badge/iOS-18.5-white?logo=apple)