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 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/core/constants.dart b/lib/core/constants.dart new file mode 100644 index 0000000..5b6e90e --- /dev/null +++ b/lib/core/constants.dart @@ -0,0 +1,22 @@ +import 'package:rate_my_app/rate_my_app.dart'; + +class Constants { + static const String appDevPhase = 'Beta'; + + static const String INSTAGRAM_LINK = 'https://instagram.felixkirchner.de'; + static const String GITHUB_LINK = 'https://github.felixkirchner.de'; + static const String GITHUB_ISSUES_LINK = + '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'; + + static RateMyApp rateMyApp = RateMyApp( + appStoreIdentifier: '6747105718', + minDays: 15, + remindDays: 45, + minLaunches: 15, + remindLaunches: 40); +} 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/l10n/app_de.arb b/lib/l10n/arb/app_de.arb similarity index 90% rename from lib/l10n/app_de.arb rename to lib/l10n/arb/app_de.arb index 0a76696..d89399b 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -30,6 +30,16 @@ } } }, + "pre_rating_title": "Gefällt dir die App?", + "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", "game_title": "Titel des Spiels", @@ -103,6 +113,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 88% rename from lib/l10n/app_en.arb rename to lib/l10n/arb/app_en.arb index edca935..e01e242 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -30,6 +30,16 @@ } } }, + "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", + "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": "Contact via E-Mail", + "email_subject": "Feedback: Cabo Counter App", + "email_body": "I have the following feedback...", + "overview": "Overview", "new_game": "New Game", "game_title": "Game Title", @@ -103,6 +113,7 @@ "create_issue": "Create Issue", "wiki": "Wiki", "app_version": "App Version", + "privacy_policy": "Privacy Policy", "loading": "Loading...", "build": "Build No.", 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 91% rename from lib/l10n/app_localizations.dart rename to lib/l10n/generated/app_localizations.dart index 9f85aab..af778ae 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, @@ -218,6 +218,60 @@ 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 @pre_rating_title. + /// + /// In de, this message translates to: + /// **'Gefällt dir die App?'** + String get pre_rating_title; + + /// No description provided for @pre_rating_message. + /// + /// In de, this message translates to: + /// **'Feedback hilft mir, die App zu verbessern. Vielen Dank!'** + String get pre_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 @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: @@ -566,6 +620,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 89% rename from lib/l10n/app_localizations_de.dart rename to lib/l10n/generated/app_localizations_de.dart index 23b41ac..afe558f 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -71,6 +71,35 @@ 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 pre_rating_title => 'Gefällt dir die App?'; + + @override + String get pre_rating_message => + 'Feedback hilft mir, die App zu verbessern. Vielen Dank!'; + + @override + String get yes => 'Ja'; + + @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'; @@ -256,6 +285,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 88% rename from lib/l10n/app_localizations_en.dart rename to lib/l10n/generated/app_localizations_en.dart index eea3896..a986058 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -71,6 +71,35 @@ class AppLocalizationsEn extends AppLocalizations { return 'Are you sure you want to delete the game \"$gameTitle\"? This action cannot be undone.'; } + @override + String get pre_rating_title => 'Do you like the app?'; + + @override + String get pre_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 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 via 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'; @@ -253,6 +282,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.'; diff --git a/lib/main.dart b/lib/main.dart index b826072..9279426 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,9 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +import 'package:cabo_counter/core/custom_theme.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'; 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/information_view.dart b/lib/presentation/views/about_view.dart similarity index 74% rename from lib/presentation/views/information_view.dart rename to lib/presentation/views/about_view.dart index 1d0918b..128f91f 100644 --- a/lib/presentation/views/information_view.dart +++ b/lib/presentation/views/about_view.dart @@ -1,11 +1,13 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +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) { @@ -28,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'), @@ -54,15 +60,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/lib/presentation/views/active_game_view.dart b/lib/presentation/views/active_game_view.dart index ddc0299..9ecae1b 100644 --- a/lib/presentation/views/active_game_view.dart +++ b/lib/presentation/views/active_game_view.dart @@ -1,11 +1,11 @@ +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/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'; 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'; @@ -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(() { diff --git a/lib/presentation/views/create_game_view.dart b/lib/presentation/views/create_game_view.dart index 2c04e74..0d23494 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/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'; -import 'package:cabo_counter/utility/custom_theme.dart'; import 'package:flutter/cupertino.dart'; enum CreateStatus { 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..1f8b5d0 100644 --- a/lib/presentation/views/main_menu_view.dart +++ b/lib/presentation/views/main_menu_view.dart @@ -1,13 +1,19 @@ +import 'package:cabo_counter/core/constants.dart'; +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/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'; 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'; +import 'package:url_launcher/url_launcher.dart'; + +enum PreRatingDialogDecision { yes, no, cancel } + +enum BadRatingDialogDecision { email, cancel } class MainMenuView extends StatefulWidget { const MainMenuView({super.key}); @@ -29,6 +35,17 @@ class _MainMenuViewState extends State { }); }); gameManager.addListener(_updateView); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await Constants.rateMyApp.init(); + + if (Constants.rateMyApp.shouldOpenDialog && + Constants.appDevPhase != 'Beta') { + await Future.delayed(const Duration(milliseconds: 600)); + if (!mounted) return; + _handleFeedbackDialog(context); + } + }); } void _updateView() { @@ -57,14 +74,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( @@ -128,7 +143,7 @@ class _MainMenuViewState extends State { final String gameTitle = gameManager .gameList[index].gameTitle; return await _showDeleteGamePopup( - gameTitle); + context, gameTitle); }, onDismissed: (direction) { gameManager @@ -204,40 +219,144 @@ class _MainMenuViewState extends State { return AppLocalizations.of(context).unlimited; } + /// 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', + ); + + 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 PreRatingDialogDecision.yes: + if (context.mounted) Constants.rateMyApp.showStarRateDialog(context); + break; + case PreRatingDialogDecision.no: + if (context.mounted) { + badRatingDecision = await _showBadRatingDialog(context); + } + if (badRatingDecision == BadRatingDialogDecision.email) { + if (context.mounted) { + launchUrl(emailUri); + } + } + break; + case PreRatingDialogDecision.cancel: + break; + } + } + /// 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( + Future _showDeleteGamePopup( + BuildContext context, String gameTitle) async { + return await showCupertinoDialog( context: context, - builder: (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( - onPressed: () { - Navigator.pop(context, false); - }, - child: Text(AppLocalizations.of(context).cancel), + title: Text( + AppLocalizations.of(context).delete_game_title, ), - CupertinoDialogAction( - onPressed: () { - Navigator.pop(context, true); - }, - child: Text( - AppLocalizations.of(context).delete, - style: const TextStyle( - fontWeight: FontWeight.bold, color: Colors.red), + content: Text(AppLocalizations.of(context) + .delete_game_message(gameTitle)), + actions: [ + CupertinoDialogAction( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text(AppLocalizations.of(context).cancel), ), - ), - ], - ); + CupertinoDialogAction( + isDestructiveAction: true, + isDefaultAction: true, + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text( + AppLocalizations.of(context).delete, + ), + ) + ]); }, ) ?? false; - return shouldDelete; + } + + /// 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 { + 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(PreRatingDialogDecision.yes), + isDefaultAction: true, + child: Text(AppLocalizations.of(context).yes), + ), + CupertinoDialogAction( + onPressed: () => + Navigator.of(context).pop(PreRatingDialogDecision.no), + child: Text(AppLocalizations.of(context).no), + ), + CupertinoDialogAction( + onPressed: () => Navigator.of(context).pop(), + isDestructiveAction: true, + child: Text(AppLocalizations.of(context).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( + 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(BadRatingDialogDecision.email), + child: Text(AppLocalizations.of(context).contact_email), + ), + CupertinoDialogAction( + isDestructiveAction: true, + onPressed: () => Navigator.of(context).pop(), + child: Text(AppLocalizations.of(context).cancel)) + ], + )) ?? + BadRatingDialogDecision.cancel; } @override diff --git a/lib/presentation/views/mode_selection_view.dart b/lib/presentation/views/mode_selection_view.dart index 93cdc7a..a7d3ce7 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/l10n/app_localizations.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.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..1e02cc3 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/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'; 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..9bed7fc 100644 --- a/lib/presentation/views/settings_view.dart +++ b/lib/presentation/views/settings_view.dart @@ -1,10 +1,11 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; +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'; -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'; @@ -53,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, @@ -70,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, @@ -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..0c98cc7 100644 --- a/lib/presentation/views/tab_view.dart +++ b/lib/presentation/views/tab_view.dart @@ -1,7 +1,7 @@ -import 'package:cabo_counter/l10n/app_localizations.dart'; -import 'package:cabo_counter/presentation/views/information_view.dart'; +import 'package:cabo_counter/core/custom_theme.dart'; +import 'package:cabo_counter/l10n/generated/app_localizations.dart'; +import 'package:cabo_counter/presentation/views/about_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 { @@ -39,7 +39,7 @@ class _TabViewState extends State { if (index == 0) { return const MainMenuView(); } else { - return const InformationView(); + return const AboutView(); } }); }, diff --git a/lib/presentation/widgets/custom_form_row.dart b/lib/presentation/widgets/custom_form_row.dart index 7a266ae..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/presentation/widgets/stepper.dart'; -import 'package:cabo_counter/utility/custom_theme.dart'; +import 'package:cabo_counter/core/custom_theme.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 87% rename from lib/presentation/widgets/stepper.dart rename to lib/presentation/widgets/custom_stepper.dart index 8ca2635..a05a4cb 100644 --- a/lib/presentation/widgets/stepper.dart +++ b/lib/presentation/widgets/custom_stepper.dart @@ -1,13 +1,13 @@ -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 { +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 diff --git a/lib/services/version_service.dart b/lib/services/version_service.dart index c23d187..6511c69 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/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/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'; -} diff --git a/pubspec.yaml b/pubspec.yaml index 5edeba0..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+382 +version: 0.4.0+467 environment: sdk: ^3.5.4 @@ -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: