kasy-cli 1.12.1 → 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 (249) hide show
  1. package/bin/kasy.js +143 -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 +219 -0
  13. package/lib/commands/update.js +1 -1
  14. package/lib/scaffold/CHANGELOG.json +9 -0
  15. package/lib/scaffold/backends/api/patch/README.md +1 -1
  16. package/lib/scaffold/backends/api/patch/android/app/src/main/AndroidManifest.xml +1 -1
  17. package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +53 -0
  18. package/lib/scaffold/backends/api/patch/lib/main.dart +29 -10
  19. package/lib/scaffold/backends/api/pubspec.yaml.tpl +11 -1
  20. package/lib/scaffold/backends/firebase/tokens.js +2 -2
  21. package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -2
  22. package/lib/scaffold/backends/supabase/migrations/20240101000011_dedupe_device_tokens.sql +34 -0
  23. package/lib/scaffold/backends/supabase/patch/README.md +1 -1
  24. package/lib/scaffold/backends/supabase/patch/android/app/src/main/AndroidManifest.xml +1 -1
  25. package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +43 -0
  26. package/lib/scaffold/backends/supabase/patch/lib/main.dart +29 -10
  27. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +11 -1
  28. package/lib/scaffold/features/README.md +15 -139
  29. package/lib/scaffold/shared/generator-utils.js +16 -15
  30. package/lib/utils/apple-release.js +85 -16
  31. package/lib/utils/checks.js +4 -105
  32. package/lib/utils/flutter-run.js +173 -0
  33. package/lib/utils/i18n.js +413 -0
  34. package/lib/utils/mobile-identity.js +35 -0
  35. package/lib/utils/ui.js +114 -0
  36. package/package.json +2 -3
  37. package/templates/firebase/README.en.md +1 -1
  38. package/templates/firebase/README.es.md +1 -1
  39. package/templates/firebase/README.md +1 -1
  40. package/templates/firebase/android/app/build.gradle.kts +10 -1
  41. package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
  42. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
  43. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +160 -11
  44. package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
  45. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
  46. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
  47. package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
  48. package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
  49. package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
  50. package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
  51. package/templates/firebase/android/app/src/main/res/drawable-hdpi/splash.png +0 -0
  52. package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
  53. package/templates/firebase/android/app/src/main/res/drawable-mdpi/splash.png +0 -0
  54. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  55. package/templates/firebase/android/app/src/main/res/drawable-night/launch_background.xml +9 -0
  56. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
  57. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/splash.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-mdpi/splash.png +0 -0
  60. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  61. package/templates/firebase/android/app/src/main/res/drawable-night-v21/launch_background.xml +9 -0
  62. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
  63. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/splash.png +0 -0
  64. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
  65. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/splash.png +0 -0
  66. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
  67. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/splash.png +0 -0
  68. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
  69. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/splash.png +0 -0
  70. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
  71. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/splash.png +0 -0
  72. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
  73. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/splash.png +0 -0
  74. package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
  75. package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +46 -0
  76. package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  80. package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  81. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +2 -1
  82. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -0
  83. package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
  84. package/templates/firebase/assets/images/favicon.png +0 -0
  85. package/templates/firebase/assets/images/icon.png +0 -0
  86. package/templates/firebase/assets/images/splash_logo_dark.png +0 -0
  87. package/templates/firebase/assets/images/splash_logo_light.png +0 -0
  88. package/templates/firebase/firestore.indexes.json +10 -0
  89. package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
  90. package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
  91. package/templates/firebase/functions/src/index.ts +1 -0
  92. package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
  93. package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
  94. package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
  95. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  96. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  98. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  99. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  100. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  101. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  102. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  103. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  104. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  105. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
  106. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
  107. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
  108. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
  109. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  110. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  111. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
  112. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
  113. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  114. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  115. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  116. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +9 -8
  117. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  118. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +33 -0
  119. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  120. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  121. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  122. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png +0 -0
  123. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png +0 -0
  124. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png +0 -0
  125. package/templates/firebase/ios/Runner/Base.lproj/LaunchScreen.storyboard +1 -1
  126. package/templates/firebase/ios/Runner/Info.plist +2 -2
  127. package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
  128. package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
  129. package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
  130. package/templates/firebase/lib/components/kasy_button.dart +8 -0
  131. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
  132. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +67 -19
  133. package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
  134. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
  135. package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
  136. package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
  137. package/templates/firebase/lib/core/theme/providers/theme_provider.dart +48 -24
  138. package/templates/firebase/lib/features/home/home_page.dart +0 -6
  139. package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
  140. package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
  141. package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
  142. package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
  143. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
  144. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
  145. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
  146. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +4 -0
  147. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +1 -0
  148. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +13 -0
  149. package/templates/firebase/lib/features/settings/settings_page.dart +158 -18
  150. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
  151. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
  152. package/templates/firebase/lib/i18n/en.i18n.json +10 -3
  153. package/templates/firebase/lib/i18n/es.i18n.json +10 -3
  154. package/templates/firebase/lib/i18n/pt.i18n.json +10 -3
  155. package/templates/firebase/lib/main.dart +29 -10
  156. package/templates/firebase/pubspec.yaml +10 -6
  157. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +1 -1
  158. package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
  159. package/templates/firebase/web/favicon.png +0 -0
  160. package/templates/firebase/web/icons/Icon-192.png +0 -0
  161. package/templates/firebase/web/icons/Icon-512.png +0 -0
  162. package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
  163. package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
  164. package/templates/firebase/web/index.html +50 -39
  165. package/templates/firebase/web/manifest.json +3 -3
  166. package/templates/firebase/web/splash/img/dark-1x.png +0 -0
  167. package/templates/firebase/web/splash/img/dark-2x.png +0 -0
  168. package/templates/firebase/web/splash/img/dark-3x.png +0 -0
  169. package/templates/firebase/web/splash/img/dark-4x.png +0 -0
  170. package/templates/firebase/web/splash/img/light-1x.png +0 -0
  171. package/templates/firebase/web/splash/img/light-2x.png +0 -0
  172. package/templates/firebase/web/splash/img/light-3x.png +0 -0
  173. package/templates/firebase/web/splash/img/light-4x.png +0 -0
  174. package/lib/scaffold/features/analytics/lib/core/data/api/analytics_api.dart +0 -124
  175. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.es.md +0 -35
  176. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.md +0 -35
  177. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.pt.md +0 -35
  178. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.es.md +0 -12
  179. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.md +0 -12
  180. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.pt.md +0 -12
  181. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.es.md +0 -17
  182. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  183. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.pt.md +0 -17
  184. package/lib/scaffold/features/ci/.github/dependabot.yml +0 -16
  185. package/lib/scaffold/features/ci/.github/workflows/app.yml +0 -20
  186. package/lib/scaffold/features/ci/.gitlab/templates/deploy.yaml +0 -14
  187. package/lib/scaffold/features/ci/.gitlab/templates/dropbox.yaml +0 -19
  188. package/lib/scaffold/features/ci/.gitlab/templates/flutter.yaml +0 -163
  189. package/lib/scaffold/features/ci/.gitlab/templates/mailgun.yaml +0 -28
  190. package/lib/scaffold/features/ci/.gitlab-ci.yml +0 -37
  191. package/lib/scaffold/features/ci/codemagic.yaml +0 -157
  192. package/lib/scaffold/features/facebook/lib/core/data/api/tracking_api.dart +0 -111
  193. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_request_entity.dart +0 -27
  194. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart +0 -27
  195. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_request_api.dart +0 -50
  196. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_vote_api.dart +0 -79
  197. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feature_requests.dart +0 -48
  198. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feedback_state.dart +0 -42
  199. package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart +0 -147
  200. package/lib/scaffold/features/feedback/lib/features/feedbacks/repositories/feature_request_repository.dart +0 -95
  201. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
  202. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/feedback_page.dart +0 -175
  203. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -76
  204. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/feature_card.dart +0 -279
  205. package/lib/scaffold/features/ios-release/.kasy/apple.env.example +0 -8
  206. package/lib/scaffold/features/ios-release/.kasy/codemagic.env.example +0 -7
  207. package/lib/scaffold/features/ios-release/docs/codemagic-release.en.md +0 -50
  208. package/lib/scaffold/features/ios-release/docs/codemagic-release.es.md +0 -50
  209. package/lib/scaffold/features/ios-release/docs/codemagic-release.pt.md +0 -50
  210. package/lib/scaffold/features/ios-release/docs/ios-release.en.md +0 -41
  211. package/lib/scaffold/features/ios-release/docs/ios-release.es.md +0 -41
  212. package/lib/scaffold/features/ios-release/docs/ios-release.pt.md +0 -41
  213. package/lib/scaffold/features/ios-release/scripts/bump-ios-version.js +0 -38
  214. package/lib/scaffold/features/ios-release/scripts/release-ios.sh +0 -137
  215. package/lib/scaffold/features/llm_chat/lib/features/llm_chat/llm_chat_page.dart +0 -301
  216. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/providers/reminder_notifier.dart +0 -81
  217. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/repositories/reminder_preferences.dart +0 -76
  218. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/ui/reminder_page.dart +0 -282
  219. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/entities/user_info_entity.dart +0 -24
  220. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/user_infos_api.dart +0 -71
  221. package/lib/scaffold/features/onboarding/lib/features/onboarding/models/user_info.dart +0 -92
  222. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_model.dart +0 -15
  223. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_provider.dart +0 -78
  224. package/lib/scaffold/features/onboarding/lib/features/onboarding/repositories/user_infos_repository.dart +0 -29
  225. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/animations/page_transitions.dart +0 -30
  226. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -66
  227. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_features.dart +0 -72
  228. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_loader.dart +0 -92
  229. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
  230. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_questions.dart +0 -89
  231. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/onboarding_page.dart +0 -94
  232. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_background.dart +0 -80
  233. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_feature.dart +0 -139
  234. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +0 -110
  235. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_progress.dart +0 -84
  236. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -173
  237. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_reassurance.dart +0 -45
  238. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +0 -77
  239. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +0 -392
  240. package/lib/scaffold/features/revenuecat/lib/core/data/api/tracking_api.dart +0 -116
  241. package/lib/scaffold/features/revenuecat/lib/core/data/models/subscription.dart +0 -322
  242. package/lib/scaffold/features/revenuecat/lib/core/home_widgets/home_widget_background_task.dart +0 -41
  243. package/lib/scaffold/features/revenuecat/lib/core/states/user_state_notifier.dart +0 -305
  244. package/templates/firebase/assets/images/app_icon.png +0 -0
  245. package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
  246. package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
  247. package/templates/firebase/assets/images/splashscreen.png +0 -0
  248. package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
  249. package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
@@ -1,41 +0,0 @@
1
- # Publish to iOS (App Store)
2
-
3
- ## Prerequisites
4
-
5
- - Paid [Apple Developer](https://developer.apple.com) account
6
- - App created in [App Store Connect](https://appstoreconnect.apple.com) with the same **Bundle ID** as your project
7
- - Mac with Xcode and Flutter installed
8
-
9
- ## One-time setup
10
-
11
- ```bash
12
- kasy ios configure
13
- ```
14
-
15
- Opens Apple links, collects the API key (`.p8`), and writes `.kasy/apple.env` (gitignored).
16
-
17
- ## Ship a new build
18
-
19
- ```bash
20
- kasy ios release
21
- ```
22
-
23
- Bumps the build number in `pubspec.yaml`, builds the IPA, and uploads to App Store Connect (no Transporter).
24
-
25
- Shortcut: `make release-ios`
26
-
27
- ### Options
28
-
29
- - `kasy ios build` — IPA only, no upload
30
- - `kasy ios release --no-bump` — skip build number bump
31
- - `kasy ios release --version-name 1.0.1` — change marketing version
32
-
33
- ## No Mac?
34
-
35
- Use cloud builds: [codemagic-release.md](./codemagic-release.md) and `kasy codemagic configure` / `kasy codemagic release`.
36
-
37
- ## Check environment
38
-
39
- ```bash
40
- kasy doctor
41
- ```
@@ -1,41 +0,0 @@
1
- # Publicar en iOS (App Store)
2
-
3
- ## Requisitos
4
-
5
- - Cuenta de pago [Apple Developer](https://developer.apple.com)
6
- - App creada en [App Store Connect](https://appstoreconnect.apple.com) con el mismo **Bundle ID** del proyecto
7
- - Mac con Xcode y Flutter instalados
8
-
9
- ## Configuración única
10
-
11
- ```bash
12
- kasy ios configure
13
- ```
14
-
15
- Abre los enlaces de Apple, pide la clave API (`.p8`) y guarda `.kasy/apple.env` (no se sube a Git).
16
-
17
- ## Enviar nueva versión
18
-
19
- ```bash
20
- kasy ios release
21
- ```
22
-
23
- Incrementa el build en `pubspec.yaml`, genera el IPA y lo sube a App Store Connect (sin Transporter).
24
-
25
- Atajo: `make release-ios`
26
-
27
- ### Opciones
28
-
29
- - `kasy ios build` — solo IPA, sin subir
30
- - `kasy ios release --no-bump` — no incrementar build
31
- - `kasy ios release --version-name 1.0.1` — cambiar versión visible
32
-
33
- ## ¿Sin Mac?
34
-
35
- Build en la nube: [codemagic-release.md](./codemagic-release.md) y `kasy codemagic configure` / `kasy codemagic release`.
36
-
37
- ## Verificar entorno
38
-
39
- ```bash
40
- kasy doctor
41
- ```
@@ -1,41 +0,0 @@
1
- # Publicar no iOS (App Store)
2
-
3
- ## Pré-requisitos
4
-
5
- - Conta [Apple Developer](https://developer.apple.com) (paga)
6
- - App criado no [App Store Connect](https://appstoreconnect.apple.com) com o mesmo **Bundle ID** do projeto
7
- - Mac com Xcode e Flutter instalados
8
-
9
- ## Configuração única
10
-
11
- ```bash
12
- kasy ios configure
13
- ```
14
-
15
- O comando abre os links da Apple, pede a chave API (`.p8`) e grava `.kasy/apple.env` (não vai para o Git).
16
-
17
- ## Enviar nova versão
18
-
19
- ```bash
20
- kasy ios release
21
- ```
22
-
23
- Isso incrementa o build no `pubspec.yaml`, gera o IPA e envia para a App Store Connect (sem Transporter).
24
-
25
- Atalho: `make release-ios`
26
-
27
- ### Opções
28
-
29
- - `kasy ios build` — só gera o IPA, sem enviar
30
- - `kasy ios release --no-bump` — não incrementa o build
31
- - `kasy ios release --version-name 1.0.1` — altera a versão visível
32
-
33
- ## Sem Mac?
34
-
35
- Use build na nuvem: [codemagic-release.md](./codemagic-release.md) e `kasy codemagic configure` / `kasy codemagic release`.
36
-
37
- ## Verificar ambiente
38
-
39
- ```bash
40
- kasy doctor
41
- ```
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const fs = require('node:fs');
5
- const path = require('node:path');
6
-
7
- const root = path.resolve(__dirname, '..');
8
- const pubspecPath = path.join(root, 'pubspec.yaml');
9
-
10
- function parseArgs() {
11
- const args = process.argv.slice(2);
12
- let versionName = null;
13
- for (let i = 0; i < args.length; i += 1) {
14
- if (args[i] === '--version-name' && args[i + 1]) {
15
- versionName = args[i + 1];
16
- i += 1;
17
- }
18
- }
19
- return { versionName };
20
- }
21
-
22
- function bumpPubspecVersion(content, versionNameOverride) {
23
- const match = content.match(/^version:\s*(\d+\.\d+\.\d+)\+(\d+)\s*$/m);
24
- if (!match) {
25
- throw new Error('Invalid version line in pubspec.yaml (expected format: 1.0.0+1)');
26
- }
27
- const name = versionNameOverride || match[1];
28
- const build = parseInt(match[2], 10) + 1;
29
- const next = `version: ${name}+${build}`;
30
- const updated = content.replace(/^version:\s*.*/m, next);
31
- return { updated, label: `${name}+${build}` };
32
- }
33
-
34
- const { versionName } = parseArgs();
35
- const content = fs.readFileSync(pubspecPath, 'utf8');
36
- const { updated, label } = bumpPubspecVersion(content, versionName);
37
- fs.writeFileSync(pubspecPath, updated);
38
- console.log(label);
@@ -1,137 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Build and upload iOS IPA to App Store Connect.
3
- # Prefer: kasy ios release (configure first with kasy ios configure)
4
-
5
- set -euo pipefail
6
-
7
- ROOT="$(cd "$(dirname "$0")/.." && pwd)"
8
- cd "$ROOT"
9
-
10
- APPLE_ENV="${ROOT}/.kasy/apple.env"
11
- UPLOAD=1
12
- BUMP=1
13
- VERSION_NAME=""
14
-
15
- while [[ $# -gt 0 ]]; do
16
- case "$1" in
17
- --no-upload) UPLOAD=0; shift ;;
18
- --no-bump) BUMP=0; shift ;;
19
- --version-name)
20
- VERSION_NAME="${2:-}"
21
- shift 2
22
- ;;
23
- *) echo "Unknown option: $1" >&2; exit 1 ;;
24
- esac
25
- done
26
-
27
- if [[ ! -f pubspec.yaml ]]; then
28
- echo "pubspec.yaml not found. Run from the Flutter project root." >&2
29
- exit 1
30
- fi
31
-
32
- check_google_ios_url_scheme() {
33
- local google_plist="${ROOT}/ios/Runner/GoogleService-Info.plist"
34
- local info_plist="${ROOT}/ios/Runner/Info.plist"
35
-
36
- [[ -f "$google_plist" ]] || return 0
37
-
38
- if [[ ! -f "$info_plist" ]]; then
39
- echo "ios/Runner/Info.plist not found." >&2
40
- exit 1
41
- fi
42
-
43
- local reversed_client_id
44
- reversed_client_id="$(/usr/libexec/PlistBuddy -c "Print :REVERSED_CLIENT_ID" "$google_plist" 2>/dev/null || true)"
45
- if [[ -z "$reversed_client_id" ]]; then
46
- echo "REVERSED_CLIENT_ID not found in ios/Runner/GoogleService-Info.plist." >&2
47
- echo "Run flutterfire configure before building iOS." >&2
48
- exit 1
49
- fi
50
-
51
- if ! /usr/libexec/PlistBuddy -c "Print :CFBundleURLTypes" "$info_plist" 2>/dev/null | grep -Fq "$reversed_client_id"; then
52
- echo "Google Sign-In iOS URL scheme mismatch." >&2
53
- echo "Expected CFBundleURLSchemes to include: $reversed_client_id" >&2
54
- echo "Run: kasy ios clean, then rebuild. If it still fails, re-run flutterfire configure/project setup." >&2
55
- exit 1
56
- fi
57
- }
58
-
59
- check_google_ios_url_scheme
60
-
61
- if [[ "$BUMP" -eq 1 ]]; then
62
- BUMP_SCRIPT="${ROOT}/scripts/bump-ios-version.js"
63
- if [[ -f "$BUMP_SCRIPT" ]]; then
64
- if [[ -n "$VERSION_NAME" ]]; then
65
- node "$BUMP_SCRIPT" --version-name "$VERSION_NAME"
66
- else
67
- node "$BUMP_SCRIPT"
68
- fi
69
- else
70
- echo "bump-ios-version.js not found" >&2
71
- exit 1
72
- fi
73
- fi
74
-
75
- load_dart_defines() {
76
- DEFINES_ARGS=()
77
- if [[ -f .dart_defines ]]; then
78
- while IFS= read -r line || [[ -n "$line" ]]; do
79
- [[ -z "$line" || "$line" =~ ^# ]] && continue
80
- DEFINES_ARGS+=("$line")
81
- done < .dart_defines
82
- fi
83
- # Projects generated by Kasy may inline DEFINES in the Makefile instead of .dart_defines
84
- if [[ ${#DEFINES_ARGS[@]} -eq 0 && -f Makefile ]]; then
85
- local block
86
- block="$(awk '/^DEFINES :=/{flag=1;next} flag && /^[^[:space:]#]/{exit} flag{print}' Makefile)"
87
- while IFS= read -r line || [[ -n "$block" ]]; do
88
- line="${line%%\\}" # trim trailing backslash from Makefile continuation
89
- line="$(echo "$line" | xargs)"
90
- [[ -z "$line" || "$line" =~ ^# ]] && continue
91
- DEFINES_ARGS+=("$line")
92
- done <<< "$block"
93
- fi
94
- }
95
-
96
- load_dart_defines
97
-
98
- echo "Building IPA..."
99
- if [[ ${#DEFINES_ARGS[@]} -gt 0 ]]; then
100
- flutter build ipa --release "${DEFINES_ARGS[@]}"
101
- else
102
- flutter build ipa --release
103
- fi
104
-
105
- if [[ "$UPLOAD" -eq 0 ]]; then
106
- echo "IPA built at build/ios/ipa/"
107
- exit 0
108
- fi
109
-
110
- if [[ ! -f "$APPLE_ENV" ]]; then
111
- echo "Missing $APPLE_ENV — run: kasy ios configure" >&2
112
- exit 1
113
- fi
114
-
115
- # shellcheck disable=SC1090
116
- source "$APPLE_ENV"
117
-
118
- if [[ -z "${APP_STORE_API_KEY:-}" || -z "${APP_STORE_ISSUER_ID:-}" ]]; then
119
- echo "APP_STORE_API_KEY and APP_STORE_ISSUER_ID required in .kasy/apple.env" >&2
120
- exit 1
121
- fi
122
-
123
- shopt -s nullglob
124
- IPA=(build/ios/ipa/*.ipa)
125
- shopt -u nullglob
126
-
127
- if [[ ${#IPA[@]} -eq 0 || ! -f "${IPA[0]}" ]]; then
128
- echo "IPA not found in build/ios/ipa/" >&2
129
- exit 1
130
- fi
131
-
132
- echo "Uploading to App Store Connect..."
133
- xcrun altool --upload-app --type ios -f "${IPA[0]}" \
134
- --apiKey "$APP_STORE_API_KEY" \
135
- --apiIssuer "$APP_STORE_ISSUER_ID"
136
-
137
- echo "Upload complete."
@@ -1,301 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:kasy_kit/components/components.dart';
4
- import 'package:kasy_kit/core/theme/theme.dart';
5
- import 'package:kasy_kit/core/widgets/kasy_app_bar.dart';
6
- import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
7
- import 'package:kasy_kit/features/llm_chat/providers/llm_chat_notifier.dart';
8
- import 'package:kasy_kit/i18n/translations.g.dart';
9
-
10
- class LlmChatPage extends ConsumerStatefulWidget {
11
- const LlmChatPage({super.key});
12
-
13
- @override
14
- ConsumerState<LlmChatPage> createState() => _LlmChatPageState();
15
- }
16
-
17
- class _LlmChatPageState extends ConsumerState<LlmChatPage> {
18
- final TextEditingController _controller = TextEditingController();
19
- final ScrollController _scrollController = ScrollController();
20
-
21
- @override
22
- void initState() {
23
- super.initState();
24
- // Scroll to bottom once the history finishes loading.
25
- WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom());
26
- }
27
-
28
- void _sendMessage() {
29
- final prompt = _controller.text.trim();
30
- if (prompt.isEmpty) return;
31
- _controller.clear();
32
- ref.read(llmChatNotifierProvider.notifier).sendMessage(prompt);
33
- }
34
-
35
- void _scrollToBottom() {
36
- WidgetsBinding.instance.addPostFrameCallback((_) {
37
- if (!_scrollController.hasClients) return;
38
- _scrollController.animateTo(
39
- _scrollController.position.maxScrollExtent,
40
- duration: const Duration(milliseconds: 250),
41
- curve: Curves.easeOut,
42
- );
43
- });
44
- }
45
-
46
- @override
47
- void dispose() {
48
- _controller.dispose();
49
- _scrollController.dispose();
50
- super.dispose();
51
- }
52
-
53
- @override
54
- Widget build(BuildContext context) {
55
- // Scroll to bottom whenever the message list changes.
56
- ref.listen(llmChatNotifierProvider, (_, _) => _scrollToBottom());
57
-
58
- final chatAsync = ref.watch(llmChatNotifierProvider);
59
-
60
- return Scaffold(
61
- backgroundColor: context.colors.background,
62
- body: Column(
63
- crossAxisAlignment: CrossAxisAlignment.stretch,
64
- children: [
65
- KasyAppBar(
66
- title: t.llm_chat.title,
67
- ),
68
- Expanded(
69
- child: chatAsync.when(
70
- loading: () => const Center(child: CircularProgressIndicator()),
71
- error: (_, _) =>
72
- Center(child: Text(t.llm_chat.error_network)),
73
- data: (chatState) => _buildBody(context, chatState),
74
- ),
75
- ),
76
- ],
77
- ),
78
- );
79
- }
80
-
81
- Widget _buildBody(BuildContext context, LlmChatState chatState) {
82
- final messages = chatState.messages;
83
- final isReplying = chatState.isReplying;
84
-
85
- // Show the typing indicator only while waiting for the first SSE chunk.
86
- // Once streaming starts the partial assistant bubble is already in [messages].
87
- final showTypingIndicator = isReplying && !chatState.streamingStarted;
88
-
89
- return Column(
90
- children: [
91
- Expanded(
92
- child: messages.isEmpty && !isReplying
93
- ? Center(
94
- child: Text(
95
- t.llm_chat.empty_state,
96
- style: context.textTheme.bodyLarge?.copyWith(
97
- color: context.colors.muted,
98
- ),
99
- ),
100
- )
101
- : ScrollConfiguration(
102
- behavior: const KasyKitScrollBehavior(),
103
- child: ListView.builder(
104
- controller: _scrollController,
105
- padding: const EdgeInsets.fromLTRB(
106
- KasySpacing.pageHorizontalGutter,
107
- KasySpacing.md,
108
- KasySpacing.pageHorizontalGutter,
109
- KasySpacing.md,
110
- ),
111
- // +1 for the typing indicator shown before the first SSE chunk
112
- itemCount: messages.length + (showTypingIndicator ? 1 : 0),
113
- itemBuilder: (context, index) {
114
- if (showTypingIndicator && index == messages.length) {
115
- return const Align(
116
- alignment: Alignment.centerLeft,
117
- child: _TypingIndicator(),
118
- );
119
- }
120
- final message = messages[index];
121
- return _ChatBubble(
122
- message: message,
123
- isUser: message.role == 'user',
124
- );
125
- },
126
- ),
127
- ),
128
- ),
129
- SafeArea(
130
- top: false,
131
- child: Padding(
132
- padding: const EdgeInsets.fromLTRB(
133
- KasySpacing.pageHorizontalGutter,
134
- KasySpacing.sm,
135
- KasySpacing.pageHorizontalGutter,
136
- KasySpacing.md,
137
- ),
138
- child: Row(
139
- crossAxisAlignment: CrossAxisAlignment.end,
140
- children: [
141
- Expanded(
142
- child: KasyTextField(
143
- controller: _controller,
144
- maxLines: 4,
145
- textInputAction: TextInputAction.send,
146
- onSubmitted: (_) => _sendMessage(),
147
- hint: t.llm_chat.hint,
148
- ),
149
- ),
150
- const SizedBox(width: KasySpacing.smd),
151
- SizedBox(
152
- height: 48,
153
- width: 48,
154
- child: ElevatedButton(
155
- style: ElevatedButton.styleFrom(
156
- padding: EdgeInsets.zero,
157
- shape: RoundedRectangleBorder(
158
- borderRadius: KasyRadius.smBorderRadius,
159
- ),
160
- ),
161
- onPressed: isReplying ? null : _sendMessage,
162
- child: isReplying
163
- ? SizedBox(
164
- width: 18,
165
- height: 18,
166
- child: CircularProgressIndicator(
167
- strokeWidth: 2,
168
- color: context.colors.muted,
169
- ),
170
- )
171
- : const Icon(KasyIcons.send, size: 20),
172
- ),
173
- ),
174
- ],
175
- ),
176
- ),
177
- ),
178
- ],
179
- );
180
- }
181
- }
182
-
183
- class _ChatBubble extends StatelessWidget {
184
- const _ChatBubble({required this.message, required this.isUser});
185
-
186
- final ChatMessage message;
187
- final bool isUser;
188
-
189
- @override
190
- Widget build(BuildContext context) {
191
- return Align(
192
- alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
193
- child: Container(
194
- margin: EdgeInsets.only(
195
- bottom: KasySpacing.sm,
196
- left: isUser ? 48 : 0,
197
- right: isUser ? 0 : 48,
198
- ),
199
- padding: const EdgeInsets.symmetric(
200
- horizontal: KasySpacing.smd,
201
- vertical: KasySpacing.sm,
202
- ),
203
- decoration: BoxDecoration(
204
- color: isUser
205
- ? context.colors.primary
206
- : context.colors.onBackground.withValues(alpha: 0.06),
207
- borderRadius: BorderRadius.only(
208
- topLeft: const Radius.circular(16),
209
- topRight: const Radius.circular(16),
210
- bottomLeft:
211
- isUser ? const Radius.circular(16) : const Radius.circular(4),
212
- bottomRight:
213
- isUser ? const Radius.circular(4) : const Radius.circular(16),
214
- ),
215
- ),
216
- child: Text(
217
- message.content,
218
- style: context.textTheme.bodyMedium?.copyWith(
219
- color:
220
- isUser ? context.colors.onPrimary : context.colors.onBackground,
221
- ),
222
- ),
223
- ),
224
- );
225
- }
226
- }
227
-
228
- /// Animated three-dot indicator shown while the assistant is generating a reply.
229
- class _TypingIndicator extends StatefulWidget {
230
- const _TypingIndicator();
231
-
232
- @override
233
- State<_TypingIndicator> createState() => _TypingIndicatorState();
234
- }
235
-
236
- class _TypingIndicatorState extends State<_TypingIndicator>
237
- with SingleTickerProviderStateMixin {
238
- late final AnimationController _controller;
239
-
240
- @override
241
- void initState() {
242
- super.initState();
243
- _controller = AnimationController(
244
- vsync: this,
245
- duration: const Duration(milliseconds: 900),
246
- )..repeat();
247
- }
248
-
249
- @override
250
- void dispose() {
251
- _controller.dispose();
252
- super.dispose();
253
- }
254
-
255
- @override
256
- Widget build(BuildContext context) {
257
- return Container(
258
- margin: const EdgeInsets.only(bottom: KasySpacing.sm),
259
- padding: const EdgeInsets.symmetric(
260
- horizontal: KasySpacing.smd,
261
- vertical: KasySpacing.sm,
262
- ),
263
- decoration: BoxDecoration(
264
- color: context.colors.onBackground.withValues(alpha: 0.06),
265
- borderRadius: const BorderRadius.only(
266
- topLeft: Radius.circular(16),
267
- topRight: Radius.circular(16),
268
- bottomLeft: Radius.circular(4),
269
- bottomRight: Radius.circular(16),
270
- ),
271
- ),
272
- child: Row(
273
- mainAxisSize: MainAxisSize.min,
274
- spacing: 4,
275
- children: List.generate(3, (i) {
276
- return AnimatedBuilder(
277
- animation: _controller,
278
- builder: (context, _) {
279
- // Each dot bounces at a slightly different phase.
280
- final double phase =
281
- (_controller.value - i * 0.2).clamp(0.0, 1.0);
282
- final double scale = 1.0 +
283
- 0.4 * (phase < 0.5 ? phase * 2 : (1.0 - phase) * 2);
284
- return Transform.scale(
285
- scale: scale,
286
- child: Container(
287
- width: 6,
288
- height: 6,
289
- decoration: BoxDecoration(
290
- shape: BoxShape.circle,
291
- color: context.colors.muted,
292
- ),
293
- ),
294
- );
295
- },
296
- );
297
- }),
298
- ),
299
- );
300
- }
301
- }
@@ -1,81 +0,0 @@
1
- import 'package:kasy_kit/core/states/translations.dart';
2
- import 'package:kasy_kit/features/local_reminder/repositories/reminder_preferences.dart';
3
- import 'package:kasy_kit/features/notifications/api/local_notifier.dart';
4
- import 'package:riverpod_annotation/riverpod_annotation.dart';
5
-
6
- part 'reminder_notifier.g.dart';
7
-
8
- const _kReminderId = 42;
9
-
10
- @Riverpod(keepAlive: true)
11
- class ReminderNotifier extends _$ReminderNotifier {
12
- @override
13
- Future<ReminderState> build() {
14
- return ReminderPreferences.load();
15
- }
16
-
17
- Future<void> setEnabled(bool value) async {
18
- final current = state.requireValue;
19
- await _applyAndSave(current.copyWith(enabled: value));
20
- }
21
-
22
- Future<void> setType(ReminderType type) async {
23
- final current = state.requireValue;
24
- await _applyAndSave(current.copyWith(type: type));
25
- }
26
-
27
- Future<void> setTime(int hour, int minute) async {
28
- final current = state.requireValue;
29
- await _applyAndSave(current.copyWith(hour: hour, minute: minute));
30
- }
31
-
32
- Future<void> setDayOfWeek(int day) async {
33
- final current = state.requireValue;
34
- await _applyAndSave(current.copyWith(dayOfWeek: day));
35
- }
36
-
37
- Future<void> setDate(DateTime date) async {
38
- final current = state.requireValue;
39
- await _applyAndSave(current.copyWith(date: date));
40
- }
41
-
42
- Future<void> _applyAndSave(ReminderState next) async {
43
- state = AsyncData(next);
44
- final notifier = ref.read(localNotifierProvider);
45
- await notifier.cancel(_kReminderId);
46
-
47
- if (next.enabled) {
48
- final tr = ref.read(translationsProvider).dailyReminder;
49
- switch (next.type) {
50
- case ReminderType.daily:
51
- await notifier.scheduleDailyAt(
52
- notificationId: _kReminderId,
53
- title: tr.title,
54
- body: tr.body,
55
- hour: next.hour,
56
- minute: next.minute,
57
- );
58
- case ReminderType.weekly:
59
- await notifier.scheduleWeekly(
60
- notificationId: _kReminderId,
61
- title: tr.title,
62
- body: tr.body,
63
- dayOfWeekIndex: next.dayOfWeek,
64
- hour: next.hour,
65
- minute: next.minute,
66
- );
67
- case ReminderType.specificDate:
68
- if (next.date != null) {
69
- await notifier.scheduleAt(
70
- notificationId: _kReminderId,
71
- title: tr.title,
72
- body: tr.body,
73
- date: next.date!,
74
- );
75
- }
76
- }
77
- }
78
-
79
- await ReminderPreferences.save(next);
80
- }
81
- }