From 68dbed1e560123882c05c4d725c6d4b7c555830c Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Wed, 9 Jul 2025 21:04:47 +0200 Subject: [PATCH 01/39] 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 02/39] 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 03/39] 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 04/39] 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 05/39] 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 06/39] 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 07/39] 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 08/39] 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 09/39] 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 10/39] 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 11/39] 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 12/39] 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 13/39] 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 14/39] 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 15/39] 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 16/39] 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 17/39] 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 18/39] 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 19/39] 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 20/39] 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 21/39] 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 22/39] 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 23/39] 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 24/39] 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 25/39] 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 26/39] 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 27/39] 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 28/39] 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 29/39] 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 30/39] 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 31/39] 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 32/39] 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 33/39] 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 34/39] 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 35/39] 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 36/39] 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 37/39] 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 38/39] 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 39/39] 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, + ), + ) ]); }, ) ??