kasy-cli 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/bin/kasy.js +122 -7
  2. package/lib/commands/add.js +2 -2
  3. package/lib/commands/codemagic.js +11 -4
  4. package/lib/commands/deploy.js +3 -3
  5. package/lib/commands/favicon.js +115 -0
  6. package/lib/commands/icon.js +143 -0
  7. package/lib/commands/ios.js +20 -5
  8. package/lib/commands/new.js +8 -20
  9. package/lib/commands/remove.js +1 -1
  10. package/lib/commands/reset.js +287 -0
  11. package/lib/commands/run.js +24 -17
  12. package/lib/commands/splash.js +3 -4
  13. package/lib/commands/update.js +1 -1
  14. package/lib/scaffold/backends/api/patch/README.md +1 -1
  15. package/lib/scaffold/backends/api/patch/android/app/src/main/AndroidManifest.xml +1 -1
  16. package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +53 -0
  17. package/lib/scaffold/backends/api/pubspec.yaml.tpl +11 -1
  18. package/lib/scaffold/backends/firebase/tokens.js +2 -2
  19. package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -2
  20. package/lib/scaffold/backends/supabase/migrations/20240101000011_dedupe_device_tokens.sql +34 -0
  21. package/lib/scaffold/backends/supabase/patch/README.md +1 -1
  22. package/lib/scaffold/backends/supabase/patch/android/app/src/main/AndroidManifest.xml +1 -1
  23. package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +43 -0
  24. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +11 -1
  25. package/lib/utils/apple-release.js +85 -16
  26. package/lib/utils/checks.js +4 -105
  27. package/lib/utils/flutter-run.js +173 -0
  28. package/lib/utils/i18n.js +335 -0
  29. package/lib/utils/mobile-identity.js +35 -0
  30. package/lib/utils/ui.js +114 -0
  31. package/package.json +1 -2
  32. package/templates/firebase/README.en.md +1 -1
  33. package/templates/firebase/README.es.md +1 -1
  34. package/templates/firebase/README.md +1 -1
  35. package/templates/firebase/android/app/build.gradle.kts +10 -1
  36. package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
  37. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
  38. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +160 -11
  39. package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
  40. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
  41. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
  42. package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
  43. package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
  44. package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
  45. package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
  46. package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +46 -0
  47. package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  48. package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  49. package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  50. package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  51. package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  52. package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
  53. package/templates/firebase/assets/images/favicon.png +0 -0
  54. package/templates/firebase/assets/images/icon.png +0 -0
  55. package/templates/firebase/firestore.indexes.json +10 -0
  56. package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
  57. package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
  58. package/templates/firebase/functions/src/index.ts +1 -0
  59. package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
  60. package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
  61. package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
  62. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  63. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  64. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  65. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  66. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  67. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  68. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  69. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  70. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  71. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  72. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
  73. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
  74. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
  75. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
  76. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  77. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  78. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
  79. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
  80. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  81. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  82. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  83. package/templates/firebase/ios/Runner/Info.plist +2 -2
  84. package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
  85. package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
  86. package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
  87. package/templates/firebase/lib/components/kasy_button.dart +8 -0
  88. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
  89. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +67 -19
  90. package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
  91. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
  92. package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
  93. package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
  94. package/templates/firebase/lib/features/home/home_page.dart +0 -6
  95. package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
  96. package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
  97. package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
  98. package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
  99. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
  100. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
  101. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
  102. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
  103. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
  104. package/templates/firebase/lib/i18n/en.i18n.json +4 -1
  105. package/templates/firebase/lib/i18n/es.i18n.json +4 -1
  106. package/templates/firebase/lib/i18n/pt.i18n.json +4 -1
  107. package/templates/firebase/pubspec.yaml +6 -1
  108. package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
  109. package/templates/firebase/web/favicon.png +0 -0
  110. package/templates/firebase/web/icons/Icon-192.png +0 -0
  111. package/templates/firebase/web/icons/Icon-512.png +0 -0
  112. package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
  113. package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
  114. package/templates/firebase/web/index.html +3 -0
  115. package/templates/firebase/web/manifest.json +3 -3
  116. package/templates/firebase/assets/images/app_icon.png +0 -0
  117. package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
  118. package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
  119. package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
  120. package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
@@ -443,6 +443,7 @@
443
443
  "group_yesterday": "Ayer",
444
444
  "group_older": "Más antiguas",
445
445
  "empty_cta": "Activar notificaciones",
446
+ "empty_cta_open_settings": "Abrir ajustes",
446
447
  "delete_all": "Eliminar todo",
447
448
  "delete_all_confirm_title": "¿Eliminar todas las notificaciones?",
448
449
  "delete_all_confirm_message": "Esto eliminará todas las notificaciones de tu cuenta. Esta acción no se puede deshacer.",
@@ -527,8 +528,10 @@
527
528
  "greeting_evening": "Buenas noches",
528
529
  "title_with_name": "¡Hola, $name!",
529
530
  "title_default": "¡Hola!",
531
+ "title_logged_out": "Te esperamos de vuelta",
530
532
  "plan_free": "Plan gratuito",
531
- "plan_pro": "PRO"
533
+ "plan_pro": "PRO",
534
+ "quote": "Siempre parece imposible hasta que se hace."
532
535
  }
533
536
  }
534
537
 
@@ -443,6 +443,7 @@
443
443
  "group_yesterday": "Ontem",
444
444
  "group_older": "Mais antigas",
445
445
  "empty_cta": "Ativar notificações",
446
+ "empty_cta_open_settings": "Abrir configurações",
446
447
  "delete_all": "Excluir tudo",
447
448
  "delete_all_confirm_title": "Excluir todas as notificações?",
448
449
  "delete_all_confirm_message": "Isso vai remover todas as notificações da sua conta. Essa ação não pode ser desfeita.",
@@ -527,8 +528,10 @@
527
528
  "greeting_evening": "Boa noite",
528
529
  "title_with_name": "Olá, $name!",
529
530
  "title_default": "Olá!",
531
+ "title_logged_out": "Aguardamos seu retorno",
530
532
  "plan_free": "Plano grátis",
531
- "plan_pro": "PRO"
533
+ "plan_pro": "PRO",
534
+ "quote": "Sempre parece impossível, até que seja feito."
532
535
  }
533
536
  }
534
537
 
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16
16
  # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17
17
  # In Windows, build-name is used as the major, minor, and patch parts
18
18
  # of the product and file versions while build-number is used as the build suffix.
19
- version: 1.0.0+23
19
+ version: 1.0.0+31
20
20
 
21
21
  environment:
22
22
  sdk: ^3.11.0
@@ -130,6 +130,11 @@ flutter_launcher_icons:
130
130
  android: ic_launcher
131
131
  ios: true
132
132
  remove_alpha_ios: true
133
+ web:
134
+ generate: true
135
+ image_path: assets/images/favicon.png
136
+ background_color: "#01171f"
137
+ theme_color: "#01171f"
133
138
  flutter_native_splash:
134
139
  color: "#FFFFFF"
135
140
  color_dark: "#000000"
@@ -15,6 +15,15 @@ class FakeDeviceApi implements DeviceApi {
15
15
  await Future.delayed(const Duration(milliseconds: 100));
16
16
  }
17
17
 
18
+ @override
19
+ Future<void> touch(String userId, String installationId) async {}
20
+
21
+ @override
22
+ Future<void> cleanupStaleDevices(
23
+ String userId,
24
+ String currentInstallationId,
25
+ ) async {}
26
+
18
27
  @override
19
28
  Future<DeviceEntity> get() {
20
29
  return Future.value(
Binary file
@@ -32,6 +32,8 @@
32
32
 
33
33
 
34
34
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
35
+
36
+
35
37
  <style id="splash-screen-style">
36
38
  html {
37
39
  height: 100%
@@ -111,6 +113,7 @@
111
113
  <img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
112
114
  </picture>
113
115
 
116
+
114
117
  <script src="flutter_bootstrap.js" async=""></script>
115
118
  <script src="./local_notifications.js"></script>
116
119
 
@@ -3,8 +3,8 @@
3
3
  "short_name": "kasy",
4
4
  "start_url": ".",
5
5
  "display": "standalone",
6
- "background_color": "#0175C2",
7
- "theme_color": "#0175C2",
6
+ "background_color": "#01171f",
7
+ "theme_color": "#01171f",
8
8
  "description": "A new Flutter project.",
9
9
  "orientation": "portrait-primary",
10
10
  "prefer_related_applications": false,
@@ -32,4 +32,4 @@
32
32
  "purpose": "maskable"
33
33
  }
34
34
  ]
35
- }
35
+ }
@@ -1,88 +0,0 @@
1
- import 'package:flutter/foundation.dart';
2
- import 'package:flutter/material.dart';
3
- import 'package:flutter_riverpod/flutter_riverpod.dart';
4
- import 'package:kasy_kit/components/kasy_button.dart';
5
- import 'package:kasy_kit/components/kasy_dialog.dart';
6
- import 'package:kasy_kit/core/data/models/user.dart';
7
- import 'package:kasy_kit/core/icons/kasy_icons.dart';
8
- import 'package:kasy_kit/core/security/biometric_preference_notifier.dart';
9
- import 'package:kasy_kit/core/security/biometric_service.dart';
10
- import 'package:kasy_kit/core/security/biometric_ui_bundle.dart';
11
- import 'package:kasy_kit/core/shared_preferences/shared_preferences.dart';
12
- import 'package:kasy_kit/core/states/components/maybeshow_component.dart';
13
- import 'package:kasy_kit/core/states/models/event_model.dart';
14
- import 'package:kasy_kit/core/states/user_state_notifier.dart';
15
-
16
- class MaybeAskBiometricSetup extends MaybeShowWithRef {
17
- @override
18
- Future<bool> handle(WidgetRef ref, AppEvent event) async {
19
- if (event is! OnAppStartEvent) return false;
20
- if (kIsWeb) return false;
21
-
22
- final prefs = ref.read(sharedPreferencesProvider);
23
- if (prefs.getBiometricPromptShown()) return false;
24
- if (prefs.getBiometricEnabled()) return false;
25
-
26
- final user = ref.read(userStateNotifierProvider).user;
27
- if (user is! AuthenticatedUserData) return false;
28
-
29
- final service = ref.read(biometricServiceProvider);
30
- if (!await service.isAvailable()) return false;
31
-
32
- await Future.delayed(const Duration(milliseconds: 800));
33
- if (!ref.context.mounted) return false;
34
-
35
- final platform = Theme.of(ref.context).platform;
36
- final slot = await ref.read(biometricCopySlotFamily(platform).future);
37
- if (!ref.context.mounted) return false;
38
- final bundle = BioBundle.fromSlot(ref.context, slot);
39
-
40
- // ignore: use_build_context_synchronously
41
- await _showPrompt(ref.context, ref, bundle);
42
- await prefs.setBiometricPromptShown(true);
43
- return true;
44
- }
45
-
46
- Future<void> _showPrompt(
47
- BuildContext context,
48
- WidgetRef ref,
49
- BioBundle bundle,
50
- ) async {
51
- await showKasyBlurDialog<void>(
52
- context: context,
53
- barrierDismissible: false,
54
- builder: (dialogContext) => KasyDialog(
55
- leadingIcon: KasyIcons.fingerprint,
56
- iconTone: KasyDialogIconTone.info,
57
- title: bundle.promptTitle,
58
- message: bundle.promptMessage,
59
- showCloseButton: false,
60
- actionsAxis: Axis.horizontal,
61
- actions: [
62
- KasyButton(
63
- label: bundle.notNowLabel,
64
- variant: KasyButtonVariant.outline,
65
- expand: true,
66
- onPressed: () => Navigator.of(dialogContext).pop(),
67
- ),
68
- KasyButton(
69
- label: bundle.enableLabel,
70
- expand: true,
71
- onPressed: () async {
72
- Navigator.of(dialogContext).pop();
73
- final service = ref.read(biometricServiceProvider);
74
- final authenticated = await service.authenticate(
75
- localizedReason: bundle.enableReason,
76
- );
77
- if (authenticated) {
78
- await ref
79
- .read(biometricPreferenceProvider.notifier)
80
- .setEnabled(true);
81
- }
82
- },
83
- ),
84
- ],
85
- ),
86
- );
87
- }
88
- }
@@ -1,144 +0,0 @@
1
- import 'package:flutter/foundation.dart';
2
- import 'package:flutter/material.dart';
3
- import 'package:flutter_riverpod/flutter_riverpod.dart';
4
- import 'package:kasy_kit/components/components.dart';
5
- import 'package:kasy_kit/core/icons/kasy_icons.dart';
6
- import 'package:kasy_kit/core/states/components/maybeshow_component.dart';
7
- import 'package:kasy_kit/core/states/models/event_model.dart';
8
- import 'package:kasy_kit/features/notifications/api/local_notifier.dart';
9
- import 'package:kasy_kit/features/notifications/repositories/notifications_repository.dart';
10
- import 'package:kasy_kit/i18n/translations.g.dart';
11
- import 'package:logger/logger.dart';
12
- import 'package:permission_handler/permission_handler.dart';
13
- import 'package:sentry_flutter/sentry_flutter.dart';
14
-
15
- /// This widget is used to show the notification permission dialog
16
- /// if user didn't grant permission, we will show the dialog again
17
- /// if user granted permission, we will not show the dialog again
18
- /// customize the trigger condition to your needs
19
- class MaybeShowNotificationPermission extends MaybeShowWithContext {
20
- @override
21
- Future<bool> handle(BuildContext context, AppEvent? event) async {
22
- if (event == null) return false;
23
- return switch (event) {
24
- OnAppStartEvent() => showNotificationPermissionSheetIfNeeded(context),
25
- _ => false,
26
- };
27
- }
28
- }
29
-
30
- Future<bool> showNotificationPermissionSheetIfNeeded(
31
- BuildContext context, {
32
- bool force = false,
33
- }) async {
34
- if (kIsWeb) return false;
35
- Logger().d("🔔 [MaybeShowNotificationPermission] checking permission status");
36
- final permissionStatus = await Permission.notification.status;
37
- if (permissionStatus.isGranted && !force) {
38
- debugPrint('🔔 [MaybeShowNotificationPermission] permission is granted ✅');
39
- return false;
40
- }
41
- if (context.mounted) {
42
- await showKasyBlurDialog<void>(
43
- context: context,
44
- builder: (dialogContext) => const _NotificationPermissionDialog(),
45
- );
46
- }
47
- return true;
48
- }
49
-
50
- class _NotificationPermissionDialog extends ConsumerWidget {
51
- const _NotificationPermissionDialog();
52
-
53
- @override
54
- Widget build(BuildContext context, WidgetRef ref) {
55
- final translations = Translations.of(
56
- context,
57
- ).request_notification_permission;
58
- final notificationSettings = ref.read(notificationsSettingsProvider);
59
- final notificationRepository = ref.read(notificationRepositoryProvider);
60
-
61
- return KasyDialog(
62
- leadingIcon: KasyIcons.notificationAdd,
63
- iconTone: KasyDialogIconTone.info,
64
- title: translations.title,
65
- message: translations.description,
66
- showCloseButton: false,
67
- actions: [
68
- KasyButton(
69
- label: translations.continue_button,
70
- expand: true,
71
- onPressed: () => _requestNotificationPermission(
72
- context,
73
- notificationSettings,
74
- notificationRepository,
75
- ),
76
- ),
77
- ],
78
- );
79
- }
80
-
81
- Future<void> _requestNotificationPermission(
82
- BuildContext context,
83
- NotificationSettings notificationSettings,
84
- NotificationsRepository notificationRepository,
85
- ) async {
86
- try {
87
- final permissionStatus = await Permission.notification.status;
88
-
89
- if (permissionStatus.isPermanentlyDenied) {
90
- if (!context.mounted) return;
91
- await _showPermissionDeniedDialog(context);
92
- if (!context.mounted) return;
93
- Navigator.of(context).pop();
94
- return;
95
- }
96
-
97
- if (permissionStatus.isDenied) {
98
- final granted = await notificationSettings.askPermission();
99
- if (!context.mounted) return;
100
- Navigator.of(context).pop();
101
- if (granted) {
102
- notificationRepository.init();
103
- } else {
104
- await _showPermissionDeniedDialog(context);
105
- }
106
- return;
107
- }
108
-
109
- final notificationsEnabled =
110
- await notificationSettings.areNotificationsEnabled();
111
- if (!context.mounted) return;
112
- if (notificationsEnabled) {
113
- await notificationRepository.init();
114
- if (!context.mounted) return;
115
- Navigator.of(context).pop();
116
- return;
117
- }
118
- await _showPermissionDeniedDialog(context);
119
- if (!context.mounted) return;
120
- Navigator.of(context).pop();
121
- } catch (e, stackTrace) {
122
- if (!context.mounted) return;
123
- debugPrint('🔔 [NotificationPermissionDialog] error: $e');
124
- Sentry.captureException(e, stackTrace: stackTrace);
125
- Navigator.of(context).pop();
126
- }
127
- }
128
-
129
- Future<void> _showPermissionDeniedDialog(BuildContext context) {
130
- final t = Translations.of(context).notification_permission_denied;
131
- return showKasyConfirmDialog(
132
- context,
133
- title: t.title,
134
- message: t.description,
135
- cancelLabel: t.cancel_button,
136
- confirmLabel: t.open_settings_button,
137
- onCancel: () => Navigator.of(context).pop(),
138
- onConfirm: () {
139
- Navigator.of(context).pop();
140
- openAppSettings();
141
- },
142
- );
143
- }
144
- }