kasy-cli 1.13.0 → 1.15.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 (157) hide show
  1. package/bin/kasy.js +140 -12
  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 +28 -7
  8. package/lib/commands/new.js +8 -20
  9. package/lib/commands/remove.js +1 -1
  10. package/lib/commands/reset.js +385 -0
  11. package/lib/commands/run.js +24 -17
  12. package/lib/commands/splash.js +14 -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 +115 -16
  26. package/lib/utils/checks.js +45 -107
  27. package/lib/utils/debug.js +75 -0
  28. package/lib/utils/flutter-run.js +173 -0
  29. package/lib/utils/friendly-error.js +91 -0
  30. package/lib/utils/i18n/messages-en.js +970 -0
  31. package/lib/utils/i18n/messages-es.js +968 -0
  32. package/lib/utils/i18n/messages-pt.js +968 -0
  33. package/lib/utils/i18n.js +21 -2483
  34. package/lib/utils/mobile-identity.js +35 -0
  35. package/lib/utils/png-padding.js +120 -0
  36. package/lib/utils/ui.js +114 -0
  37. package/package.json +8 -4
  38. package/templates/firebase/README.en.md +1 -1
  39. package/templates/firebase/README.es.md +1 -1
  40. package/templates/firebase/README.md +1 -1
  41. package/templates/firebase/android/app/build.gradle.kts +10 -1
  42. package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
  43. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
  44. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +161 -11
  45. package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
  46. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
  47. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
  48. package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
  49. package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
  50. package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
  51. package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
  52. package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png +0 -0
  53. package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png +0 -0
  54. package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
  55. package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png +0 -0
  56. package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png +0 -0
  57. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
  58. package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
  59. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
  60. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
  61. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
  62. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
  63. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png +0 -0
  64. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png +0 -0
  65. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
  66. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png +0 -0
  67. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png +0 -0
  68. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
  69. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png +0 -0
  70. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png +0 -0
  71. package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
  72. package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +53 -0
  73. package/templates/firebase/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
  74. package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  75. package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  76. package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
  80. package/templates/firebase/assets/images/favicon.png +0 -0
  81. package/templates/firebase/assets/images/icon.png +0 -0
  82. package/templates/firebase/assets/images/icon_android.png +0 -0
  83. package/templates/firebase/assets/images/icon_foreground_empty.png +0 -0
  84. package/templates/firebase/assets/images/splash_logo_dark_android12.png +0 -0
  85. package/templates/firebase/assets/images/splash_logo_light_android12.png +0 -0
  86. package/templates/firebase/firestore.indexes.json +10 -0
  87. package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
  88. package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
  89. package/templates/firebase/functions/src/index.ts +1 -0
  90. package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
  91. package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
  92. package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
  93. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  94. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  95. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  96. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  98. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  99. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  100. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  101. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  102. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  103. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
  104. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
  105. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
  106. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
  107. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  108. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  109. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
  110. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
  111. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  112. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  113. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  114. package/templates/firebase/ios/Runner/Info.plist +2 -2
  115. package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
  116. package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
  117. package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
  118. package/templates/firebase/lib/components/components.dart +1 -0
  119. package/templates/firebase/lib/components/kasy_avatar.dart +88 -57
  120. package/templates/firebase/lib/components/kasy_avatar_presets.dart +116 -74
  121. package/templates/firebase/lib/components/kasy_button.dart +8 -0
  122. package/templates/firebase/lib/components/kasy_tabs.dart +431 -0
  123. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
  124. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +73 -19
  125. package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
  126. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
  127. package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
  128. package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
  129. package/templates/firebase/lib/features/home/home_components_page.dart +1 -1
  130. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +316 -93
  131. package/templates/firebase/lib/features/home/home_page.dart +0 -6
  132. package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
  133. package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
  134. package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
  135. package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
  136. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
  137. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
  138. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
  139. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
  140. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
  141. package/templates/firebase/lib/i18n/en.i18n.json +4 -1
  142. package/templates/firebase/lib/i18n/es.i18n.json +4 -1
  143. package/templates/firebase/lib/i18n/pt.i18n.json +4 -1
  144. package/templates/firebase/pubspec.yaml +10 -3
  145. package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
  146. package/templates/firebase/web/favicon.png +0 -0
  147. package/templates/firebase/web/icons/Icon-192.png +0 -0
  148. package/templates/firebase/web/icons/Icon-512.png +0 -0
  149. package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
  150. package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
  151. package/templates/firebase/web/index.html +9 -0
  152. package/templates/firebase/web/manifest.json +3 -3
  153. package/templates/firebase/assets/images/app_icon.png +0 -0
  154. package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
  155. package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
  156. package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
  157. package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
@@ -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
- }