kasy-cli 1.32.0 → 1.35.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 (169) hide show
  1. package/README.md +1 -1
  2. package/bin/kasy.js +66 -2
  3. package/docs/cli-reference.md +7 -7
  4. package/lib/commands/apple-web.js +222 -0
  5. package/lib/commands/configure.js +3 -91
  6. package/lib/commands/doctor.js +20 -0
  7. package/lib/commands/facebook.js +189 -0
  8. package/lib/commands/new.js +61 -11
  9. package/lib/commands/release-version.js +234 -0
  10. package/lib/commands/update.js +27 -0
  11. package/lib/scaffold/CHANGELOG.json +27 -0
  12. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +24 -0
  13. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api_interface.dart +15 -0
  14. package/lib/scaffold/backends/api/patch/lib/features/authentication/repositories/authentication_repository.dart +27 -0
  15. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  16. package/lib/scaffold/backends/firebase/setup-from-scratch.js +199 -21
  17. package/lib/scaffold/backends/patch-base-hashes.json +66 -0
  18. package/lib/scaffold/backends/supabase/deploy.js +92 -0
  19. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +2 -0
  20. package/lib/scaffold/backends/supabase/migrations/20240101000007_welcome_notification.sql +36 -23
  21. package/lib/scaffold/backends/supabase/migrations/20240101000014_subscription_trial_end.sql +6 -0
  22. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +92 -3
  23. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/repositories/authentication_repository.dart +36 -0
  24. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  25. package/lib/scaffold/generate.js +53 -4
  26. package/lib/scaffold/shared/generator-utils.js +18 -6
  27. package/lib/utils/apple-web.js +147 -0
  28. package/lib/utils/facebook.js +162 -0
  29. package/lib/utils/i18n/messages-en.js +85 -0
  30. package/lib/utils/i18n/messages-es.js +85 -0
  31. package/lib/utils/i18n/messages-pt.js +85 -0
  32. package/package.json +5 -2
  33. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +73 -0
  34. package/templates/firebase/AGENTS.md +170 -0
  35. package/templates/firebase/CLAUDE.md +16 -0
  36. package/templates/firebase/DESIGN_SYSTEM.md +269 -0
  37. package/templates/firebase/docs/auth-setup.en.md +4 -2
  38. package/templates/firebase/docs/auth-setup.es.md +4 -2
  39. package/templates/firebase/docs/auth-setup.pt.md +4 -2
  40. package/templates/firebase/firebase.json +56 -1
  41. package/templates/firebase/functions/src/authentication/triggers.ts +10 -6
  42. package/templates/firebase/functions/src/core/data/entities/subscription_entity.ts +5 -0
  43. package/templates/firebase/functions/src/core/data/entities/user_entity.ts +9 -1
  44. package/templates/firebase/functions/src/core/data/repositories/user_repository.ts +27 -0
  45. package/templates/firebase/functions/src/subscriptions/models/subscriptions.ts +3 -0
  46. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +4 -0
  47. package/templates/firebase/lib/components/components.dart +1 -0
  48. package/templates/firebase/lib/components/kasy_alert.dart +0 -1
  49. package/templates/firebase/lib/components/kasy_app_bar.dart +35 -17
  50. package/templates/firebase/lib/components/kasy_bottom_sheet.dart +0 -1
  51. package/templates/firebase/lib/components/kasy_date_picker.dart +0 -5
  52. package/templates/firebase/lib/components/kasy_dialog.dart +0 -1
  53. package/templates/firebase/lib/components/kasy_otp_verification_bottom_sheet.dart +1 -1
  54. package/templates/firebase/lib/components/kasy_screen.dart +114 -0
  55. package/templates/firebase/lib/components/kasy_sidebar.dart +189 -120
  56. package/templates/firebase/lib/components/kasy_text_area.dart +0 -1
  57. package/templates/firebase/lib/components/kasy_text_field.dart +0 -1
  58. package/templates/firebase/lib/components/kasy_text_field_otp.dart +0 -1
  59. package/templates/firebase/lib/components/kasy_toast.dart +108 -73
  60. package/templates/firebase/lib/core/app_update/app_update_repository.dart +54 -0
  61. package/templates/firebase/lib/core/app_update/app_update_status.dart +14 -0
  62. package/templates/firebase/lib/core/app_update/update_available_sheet.dart +70 -0
  63. package/templates/firebase/lib/core/bottom_menu/active_tab_notifier.dart +40 -3
  64. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +82 -34
  65. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +6 -6
  66. package/templates/firebase/lib/core/bottom_menu/web_url.dart +20 -0
  67. package/templates/firebase/lib/core/chrome/chrome_visibility.dart +22 -0
  68. package/templates/firebase/lib/core/config/features.dart +5 -0
  69. package/templates/firebase/lib/core/data/api/remote_config_api.dart +38 -1
  70. package/templates/firebase/lib/core/guards/guard.dart +16 -2
  71. package/templates/firebase/lib/core/icons/kasy_icons.dart +3 -0
  72. package/templates/firebase/lib/core/rating/widgets/rate_banner.dart +2 -5
  73. package/templates/firebase/lib/core/rating/widgets/review_popup.dart +48 -124
  74. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +14 -0
  75. package/templates/firebase/lib/core/states/components/maybe_show_update_available.dart +32 -0
  76. package/templates/firebase/lib/core/states/logout_action.dart +5 -1
  77. package/templates/firebase/lib/core/states/user_state_notifier.dart +85 -14
  78. package/templates/firebase/lib/core/theme/responsive_text_theme.dart +69 -0
  79. package/templates/firebase/lib/core/theme/texts.dart +90 -57
  80. package/templates/firebase/lib/core/theme/type_scale.dart +77 -0
  81. package/templates/firebase/lib/core/theme/web_background_sync_web.dart +20 -6
  82. package/templates/firebase/lib/core/utils/image_bytes_loader.dart +12 -0
  83. package/templates/firebase/lib/core/utils/image_bytes_loader_io.dart +28 -0
  84. package/templates/firebase/lib/core/utils/image_bytes_loader_web.dart +56 -0
  85. package/templates/firebase/lib/core/web_screen_width.dart +15 -0
  86. package/templates/firebase/lib/core/web_screen_width_io.dart +3 -0
  87. package/templates/firebase/lib/core/web_screen_width_web.dart +10 -0
  88. package/templates/firebase/lib/core/web_viewport_scale.dart +61 -25
  89. package/templates/firebase/lib/core/widgets/focus_visibility.dart +89 -0
  90. package/templates/firebase/lib/core/widgets/update_bottom_sheet.dart +29 -126
  91. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +11 -8
  92. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_composer.dart +21 -0
  93. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart +1 -2
  94. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +2 -3
  95. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +1 -2
  96. package/templates/firebase/lib/features/authentication/api/auth_web_support.dart +12 -0
  97. package/templates/firebase/lib/features/authentication/api/auth_web_support_web.dart +25 -0
  98. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +266 -0
  99. package/templates/firebase/lib/features/authentication/api/authentication_api_interface.dart +22 -0
  100. package/templates/firebase/lib/features/authentication/navigation/post_login_navigation.dart +32 -0
  101. package/templates/firebase/lib/features/authentication/providers/phone_auth_notifier.dart +7 -7
  102. package/templates/firebase/lib/features/authentication/providers/signin_state_provider.dart +34 -10
  103. package/templates/firebase/lib/features/authentication/providers/signup_state_provider.dart +2 -2
  104. package/templates/firebase/lib/features/authentication/repositories/authentication_repository.dart +37 -0
  105. package/templates/firebase/lib/features/authentication/repositories/exceptions/authentication_exceptions.dart +8 -0
  106. package/templates/firebase/lib/features/authentication/ui/components/otp_verification.dart +2 -2
  107. package/templates/firebase/lib/features/authentication/ui/components/phone_input.dart +2 -2
  108. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +80 -15
  109. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +20 -14
  110. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +2 -2
  111. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +2 -2
  112. package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -1
  113. package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +1 -2
  114. package/templates/firebase/lib/features/home/design_system_page.dart +134 -67
  115. package/templates/firebase/lib/features/home/home_components_page.dart +8 -1
  116. package/templates/firebase/lib/features/home/home_components_preview_page.dart +1 -3
  117. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +186 -56
  118. package/templates/firebase/lib/features/home/home_page.dart +4 -0
  119. package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +169 -208
  120. package/templates/firebase/lib/features/notifications/providers/notifications_provider.dart +10 -0
  121. package/templates/firebase/lib/features/notifications/ui/components/notification_settings_sheet.dart +2 -2
  122. package/templates/firebase/lib/features/notifications/ui/components/notification_tile.dart +21 -8
  123. package/templates/firebase/lib/features/notifications/ui/components/push_notification_switcher.dart +2 -2
  124. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +3 -4
  125. package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +84 -128
  126. package/templates/firebase/lib/features/onboarding/providers/onboarding_model.dart +11 -0
  127. package/templates/firebase/lib/features/onboarding/providers/onboarding_provider.dart +30 -3
  128. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +13 -4
  129. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +3 -4
  130. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +2 -4
  131. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +2 -1
  132. package/templates/firebase/lib/features/settings/settings_page.dart +152 -11
  133. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +58 -21
  134. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +12 -12
  135. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +1 -4
  136. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +2 -2
  137. package/templates/firebase/lib/features/settings/ui/components/create_password_sheet.dart +141 -0
  138. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +0 -1
  139. package/templates/firebase/lib/features/settings/ui/widgets/admin_card.dart +42 -38
  140. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +17 -2
  141. package/templates/firebase/lib/features/subscriptions/api/entities/subscription_entity.dart +3 -0
  142. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +12 -11
  143. package/templates/firebase/lib/features/subscriptions/repositories/subscription_repository.dart +25 -2
  144. package/templates/firebase/lib/features/subscriptions/ui/component/active_premium_content.dart +319 -143
  145. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_minimal.dart +1 -5
  146. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_row.dart +1 -5
  147. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_with_switch.dart +2 -5
  148. package/templates/firebase/lib/features/subscriptions/ui/component/premium_content.dart +1 -4
  149. package/templates/firebase/lib/features/subscriptions/ui/widgets/feature_line.dart +1 -2
  150. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +2 -7
  151. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_feature.dart +2 -4
  152. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +1 -4
  153. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +0 -3
  154. package/templates/firebase/lib/i18n/en.i18n.json +54 -7
  155. package/templates/firebase/lib/i18n/es.i18n.json +54 -7
  156. package/templates/firebase/lib/i18n/pt.i18n.json +54 -7
  157. package/templates/firebase/lib/main.dart +11 -2
  158. package/templates/firebase/lib/router.dart +94 -13
  159. package/templates/firebase/pubspec.yaml +1 -1
  160. package/templates/firebase/test/core/data/api/fake_remote_config_api.dart +14 -0
  161. package/templates/firebase/test/core/states/user_state_notifier_test.dart +47 -3
  162. package/templates/firebase/test/core/web_viewport_scale_test.dart +68 -0
  163. package/templates/firebase/test/features/authentication/data/api/auth_api_fake.dart +15 -0
  164. package/templates/firebase/tool/design_check.dart +152 -0
  165. package/templates/firebase/web/index.html +162 -14
  166. package/templates/firebase/assets/images/review.png +0 -0
  167. package/templates/firebase/assets/images/update.png +0 -0
  168. package/templates/firebase/lib/core/guards/user_info_guard.dart +0 -61
  169. package/templates/firebase/lib/features/notifications/ui/components/notifications_header.dart +0 -32
@@ -1,5 +1,4 @@
1
1
  import 'package:flutter/material.dart';
2
- import 'package:google_fonts/google_fonts.dart';
3
2
  import 'package:kasy_kit/core/theme/theme.dart';
4
3
 
5
4
  class PremiumFeature extends StatelessWidget {
@@ -25,10 +24,9 @@ class PremiumFeature extends StatelessWidget {
25
24
  Expanded(
26
25
  child: Text(
27
26
  text,
28
- style: GoogleFonts.albertSans(
29
- fontSize: 16,
30
- fontWeight: FontWeight.w500,
27
+ style: context.textTheme.bodyLarge?.copyWith(
31
28
  color: context.colors.onPrimary,
29
+ fontWeight: FontWeight.w500,
32
30
  ),
33
31
  ),
34
32
  ),
@@ -242,9 +242,7 @@ class _SelectableColState extends State<SelectableCol>
242
242
  flex: 0,
243
243
  child: Text(
244
244
  widget.label.toUpperCase(),
245
- style: context.textTheme.titleLarge?.copyWith(
246
- fontSize: 21,
247
- fontWeight: FontWeight.w700,
245
+ style: context.textTheme.titleMedium?.copyWith(
248
246
  color: switch(widget.brightness) {
249
247
  Brightness.light => context.colors.onBackground,
250
248
  _ => context.colors.background,
@@ -272,7 +270,6 @@ class _SelectableColState extends State<SelectableCol>
272
270
  Text(
273
271
  widget.sublabel,
274
272
  style: context.textTheme.bodyLarge?.copyWith(
275
- fontWeight: FontWeight.w400,
276
273
  color: switch(widget.brightness) {
277
274
  Brightness.light => context.colors.onBackground.withValues(alpha: .7),
278
275
  _ => context.colors.background,
@@ -243,8 +243,6 @@ class _SelectableRowState extends State<SelectableRow>
243
243
  TextSpan(
244
244
  text: widget.label.toUpperCase(),
245
245
  style: context.textTheme.titleMedium?.copyWith(
246
- fontSize: 18,
247
- fontWeight: FontWeight.w700,
248
246
  color: switch (widget
249
247
  .brightness) {
250
248
  Brightness.dark =>
@@ -290,7 +288,6 @@ class _SelectableRowState extends State<SelectableRow>
290
288
  Text(
291
289
  widget.sublabel,
292
290
  style: context.textTheme.bodySmall?.copyWith(
293
- fontWeight: FontWeight.w400,
294
291
  color: switch (widget.brightness) {
295
292
  Brightness.dark =>
296
293
  context.colors.onBackground
@@ -1,4 +1,12 @@
1
1
  {
2
+ "common": {
3
+ "close": "Close",
4
+ "copied": "Copied",
5
+ "saved": "Saved",
6
+ "error": "Error",
7
+ "unavailable": "Unavailable",
8
+ "native_only_title": "Native app only"
9
+ },
2
10
  "admin_console": {
3
11
  "tabs": {
4
12
  "overview": "Overview",
@@ -143,7 +151,9 @@
143
151
  "error_text": "Wrong email, password or this email is not registered",
144
152
  "email_invalid": "Email not valid",
145
153
  "password_required": "You must provide a password",
146
- "password_too_short": "Your password must be at least 5 characters long"
154
+ "password_too_short": "Your password must be at least 5 characters long",
155
+ "social_error": "Couldn't sign in with $provider",
156
+ "email_already_registered": "This email already has an account with a different sign-in method. Use the method you signed up with."
147
157
  },
148
158
  "signup": {
149
159
  "title": "Signup now",
@@ -237,7 +247,17 @@
237
247
  "managed_elsewhere_title": "Subscription on another platform",
238
248
  "managed_elsewhere_description": "This subscription was made on another platform and can't be managed or cancelled here. Sign in to your account on the platform where you purchased it.",
239
249
  "restore_button": "Restore purchases",
240
- "cancel_button": "Close"
250
+ "cancel_button": "Close",
251
+ "billing_title": "Billing",
252
+ "plan_label": "Account plan",
253
+ "plan_fallback": "Premium",
254
+ "manage_subscription": "Manage subscription",
255
+ "restore_purchases": "Restore purchases",
256
+ "renews_on": "Renews on $date",
257
+ "expires_on": "Expires on $date",
258
+ "trial_label": "Free trial",
259
+ "trial_until": "Free trial until $date",
260
+ "charges_from": "$price starting $date"
241
261
  },
242
262
  "paywallWithSwitch": {
243
263
  "withTrial": {
@@ -386,7 +406,15 @@
386
406
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
387
407
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit."
388
408
  ],
389
- "continue_button": "Continue"
409
+ "continue_button": "Got it"
410
+ },
411
+ "update_available": {
412
+ "title": "Update available",
413
+ "description": "A newer version of the app is available. Update to get the latest improvements and fixes.",
414
+ "forced_title": "Update required",
415
+ "forced_description": "This version is no longer supported. Please update to keep using the app.",
416
+ "update_button": "Update now",
417
+ "later_button": "Not now"
390
418
  },
391
419
  "request_notification_permission": {
392
420
  "title": "Enable notifications?",
@@ -402,8 +430,8 @@
402
430
  "cancel_button": "Cancel"
403
431
  },
404
432
  "review_popup": {
405
- "title": "Your opinion matters!",
406
- "description": "Help us grow by sharing your experience with others. It only takes a few seconds and makes a huge difference to us 🙏🏻",
433
+ "title": "Your feedback matters",
434
+ "description": "If the app has been helpful, a quick review goes a long way. Got ideas to improve it? We'd love to hear them.",
407
435
  "cancel_button": "Suggest improvements",
408
436
  "rate_button": "Write a review"
409
437
  },
@@ -485,6 +513,7 @@
485
513
  "biometric_not_enabled_message": "App Lock wasn't enabled.",
486
514
  "feedback": "Send feedback",
487
515
  "premium": "Premium",
516
+ "billing": "Billing",
488
517
  "privacy": "Privacy policy",
489
518
  "support": "Support",
490
519
  "disconnect": "Yes, sign out",
@@ -497,6 +526,20 @@
497
526
  "register": "Register",
498
527
  "name_label": "Name",
499
528
  "email_label": "Email",
529
+ "connected_with_label": "Connected with",
530
+ "provider_email": "Email & password",
531
+ "provider_phone": "Phone",
532
+ "create_password_title": "Create password",
533
+ "create_password_subtitle": "Set a password so you can also sign in with email and password, on top of social login.",
534
+ "create_password_field": "New password",
535
+ "create_password_confirm_label": "Confirm password",
536
+ "create_password_success": "Password created",
537
+ "create_password_error": "Couldn't create the password. Please try again.",
538
+ "create_password_too_short": "Password must be at least 6 characters",
539
+ "create_password_mismatch": "Passwords don't match",
540
+ "link_social": "Link $provider",
541
+ "link_social_success": "$provider linked",
542
+ "link_social_error": "Couldn't link the account. Please try again.",
500
543
  "edit_name_title": "Edit name",
501
544
  "edit_name_hint": "Your name",
502
545
  "edit_name_save": "Save",
@@ -517,7 +560,8 @@
517
560
  "error": "Something went wrong. Please try again."
518
561
  },
519
562
  "admin": {
520
- "update_bottom_sheet": "Update bottom sheet",
563
+ "update_bottom_sheet": "Preview what's new",
564
+ "preview_update_available": "Preview update available",
521
565
  "paywalls": "Paywalls",
522
566
  "test_onboarding": "Test onboarding",
523
567
  "copy_user_id": "Copy user id",
@@ -526,12 +570,13 @@
526
570
  "fcm_token_copied": "FCM Token copied to clipboard",
527
571
  "fcm_token_unavailable": "Token unavailable (notifications disabled?)",
528
572
  "ask_notification": "Ask for notification",
573
+ "native_only": "Available only in the native app (iOS / Android)",
529
574
  "ask_review": "Ask for review",
530
575
  "home_widgets_panel": "Home Widgets panel",
531
576
  "home_widgets_title": "Home Widgets Panel",
532
577
  "inspector_fab_title": "Widget inspector",
533
578
  "inspector_fab_subtitle_prefix": "Global shortcut:",
534
- "update_mywidget_title": "📱 Update MyWidget Widget",
579
+ "update_mywidget_title": "Update MyWidget Widget",
535
580
  "update_mywidget_desc": "Call manual update for MyWidget widget",
536
581
  "paywalls_title": "Paywalls Admin Panel",
537
582
  "send_push_title": "Send notification",
@@ -617,6 +662,8 @@
617
662
  "title_verify": "Verify code",
618
663
  "verification_code": "Verification Code",
619
664
  "code_sent": "We have sent a verification code to $phone",
665
+ "signin_success_title": "All set",
666
+ "signin_success_text": "You're now signed in with your phone number",
620
667
  "verify_code": "Verify Code",
621
668
  "resend_code": "Resend Code",
622
669
  "enter_all_digits": "Please enter all 6 digits"
@@ -1,4 +1,12 @@
1
1
  {
2
+ "common": {
3
+ "close": "Cerrar",
4
+ "copied": "Copiado",
5
+ "saved": "Guardado",
6
+ "error": "Error",
7
+ "unavailable": "No disponible",
8
+ "native_only_title": "Solo en la app nativa"
9
+ },
2
10
  "admin_console": {
3
11
  "tabs": {
4
12
  "overview": "Resumen",
@@ -143,7 +151,9 @@
143
151
  "error_text": "Correo, contraseña incorrectos o este correo no está registrado",
144
152
  "email_invalid": "Correo electrónico inválido",
145
153
  "password_required": "Debes ingresar una contraseña",
146
- "password_too_short": "Tu contraseña debe tener al menos 5 caracteres"
154
+ "password_too_short": "Tu contraseña debe tener al menos 5 caracteres",
155
+ "social_error": "No se pudo iniciar sesión con $provider",
156
+ "email_already_registered": "Este correo ya tiene una cuenta con otro método de inicio de sesión. Usa el método con el que te registraste."
147
157
  },
148
158
  "signup": {
149
159
  "title": "Regístrate ahora",
@@ -237,7 +247,17 @@
237
247
  "managed_elsewhere_title": "Suscripción en otra plataforma",
238
248
  "managed_elsewhere_description": "Esta suscripción se realizó en otra plataforma y no se puede gestionar ni cancelar aquí. Inicia sesión en tu cuenta en la plataforma donde la compraste.",
239
249
  "restore_button": "Restaurar compras",
240
- "cancel_button": "Cerrar"
250
+ "cancel_button": "Cerrar",
251
+ "billing_title": "Facturación",
252
+ "plan_label": "Plan de cuenta",
253
+ "plan_fallback": "Premium",
254
+ "manage_subscription": "Administrar suscripción",
255
+ "restore_purchases": "Restaurar compras",
256
+ "renews_on": "Renueva el $date",
257
+ "expires_on": "Expira el $date",
258
+ "trial_label": "Evaluación gratuita",
259
+ "trial_until": "Evaluación gratuita hasta el $date",
260
+ "charges_from": "$price a partir del $date"
241
261
  },
242
262
  "paywallWithSwitch": {
243
263
  "withTrial": {
@@ -386,7 +406,15 @@
386
406
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
387
407
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit."
388
408
  ],
389
- "continue_button": "Continuar"
409
+ "continue_button": "Entendido"
410
+ },
411
+ "update_available": {
412
+ "title": "Actualización disponible",
413
+ "description": "Hay una versión más nueva de la app. Actualiza para tener las últimas mejoras y correcciones.",
414
+ "forced_title": "Actualización requerida",
415
+ "forced_description": "Esta versión ya no es compatible. Actualiza para seguir usando la app.",
416
+ "update_button": "Actualizar ahora",
417
+ "later_button": "Ahora no"
390
418
  },
391
419
  "request_notification_permission": {
392
420
  "title": "¿Activar notificaciones?",
@@ -402,8 +430,8 @@
402
430
  "cancel_button": "Cancelar"
403
431
  },
404
432
  "review_popup": {
405
- "title": "¡Tu opinión importa!",
406
- "description": "Ayúdanos a crecer compartiendo tu experiencia. Solo toma unos segundos y hace una gran diferencia para nosotros 🙏🏻",
433
+ "title": "Tu opinión importa",
434
+ "description": "Si la app te ha sido útil, una reseña rápida marca la diferencia. ¿Tienes ideas para mejorarla? Nos encantaría escucharlas.",
407
435
  "cancel_button": "Sugerir mejoras",
408
436
  "rate_button": "Escribir una reseña"
409
437
  },
@@ -485,6 +513,7 @@
485
513
  "biometric_not_enabled_message": "El bloqueo de la app no se activó.",
486
514
  "feedback": "Enviar comentarios",
487
515
  "premium": "Premium",
516
+ "billing": "Facturación",
488
517
  "privacy": "Política de privacidad",
489
518
  "support": "Soporte",
490
519
  "disconnect": "Sí, salir",
@@ -497,6 +526,20 @@
497
526
  "register": "Registrarse",
498
527
  "name_label": "Nombre",
499
528
  "email_label": "Correo electrónico",
529
+ "connected_with_label": "Conectado con",
530
+ "provider_email": "Correo y contraseña",
531
+ "provider_phone": "Teléfono",
532
+ "create_password_title": "Crear contraseña",
533
+ "create_password_subtitle": "Define una contraseña para también iniciar sesión con correo y contraseña, además del inicio de sesión social.",
534
+ "create_password_field": "Nueva contraseña",
535
+ "create_password_confirm_label": "Confirmar contraseña",
536
+ "create_password_success": "Contraseña creada",
537
+ "create_password_error": "No se pudo crear la contraseña. Inténtalo de nuevo.",
538
+ "create_password_too_short": "La contraseña debe tener al menos 6 caracteres",
539
+ "create_password_mismatch": "Las contraseñas no coinciden",
540
+ "link_social": "Vincular $provider",
541
+ "link_social_success": "$provider vinculado",
542
+ "link_social_error": "No se pudo vincular la cuenta. Inténtalo de nuevo.",
500
543
  "edit_name_title": "Editar nombre",
501
544
  "edit_name_hint": "Tu nombre",
502
545
  "edit_name_save": "Guardar",
@@ -517,7 +560,8 @@
517
560
  "error": "Algo salió mal. Por favor, inténtalo de nuevo."
518
561
  },
519
562
  "admin": {
520
- "update_bottom_sheet": "Actualizar bottom sheet",
563
+ "update_bottom_sheet": "Previsualizar novedades",
564
+ "preview_update_available": "Previsualizar actualización disponible",
521
565
  "paywalls": "Paywalls",
522
566
  "test_onboarding": "Probar onboarding",
523
567
  "copy_user_id": "Copiar ID de usuario",
@@ -526,12 +570,13 @@
526
570
  "fcm_token_copied": "FCM Token copiado al portapapeles",
527
571
  "fcm_token_unavailable": "Token no disponible (¿notificaciones desactivadas?)",
528
572
  "ask_notification": "Pedir permiso de notificación",
573
+ "native_only": "Disponible solo en la app nativa (iOS / Android)",
529
574
  "ask_review": "Pedir evaluación",
530
575
  "home_widgets_panel": "Panel de Home Widgets",
531
576
  "home_widgets_title": "Panel de Home Widgets",
532
577
  "inspector_fab_title": "Inspector de widgets",
533
578
  "inspector_fab_subtitle_prefix": "Atajo global:",
534
- "update_mywidget_title": "📱 Actualizar Widget MyWidget",
579
+ "update_mywidget_title": "Actualizar Widget MyWidget",
535
580
  "update_mywidget_desc": "Llamar a la actualización manual para el widget MyWidget",
536
581
  "paywalls_title": "Panel de Admin de Paywalls",
537
582
  "send_push_title": "Enviar notificación",
@@ -617,6 +662,8 @@
617
662
  "title_verify": "Verificar Código",
618
663
  "verification_code": "Código de Verificación",
619
664
  "code_sent": "Hemos enviado un código de verificación a $phone",
665
+ "signin_success_title": "Listo",
666
+ "signin_success_text": "Has iniciado sesión con tu número de teléfono",
620
667
  "verify_code": "Verificar Código",
621
668
  "resend_code": "Reenviar Código",
622
669
  "enter_all_digits": "Por favor, ingresa los 6 dígitos"
@@ -1,4 +1,12 @@
1
1
  {
2
+ "common": {
3
+ "close": "Fechar",
4
+ "copied": "Copiado",
5
+ "saved": "Salvo",
6
+ "error": "Erro",
7
+ "unavailable": "Indisponível",
8
+ "native_only_title": "Apenas no app nativo"
9
+ },
2
10
  "admin_console": {
3
11
  "tabs": {
4
12
  "overview": "Visão geral",
@@ -143,7 +151,9 @@
143
151
  "error_text": "E-mail, senha incorretos ou este e-mail não está cadastrado",
144
152
  "email_invalid": "E-mail inválido",
145
153
  "password_required": "Informe uma senha",
146
- "password_too_short": "Sua senha deve ter pelo menos 5 caracteres"
154
+ "password_too_short": "Sua senha deve ter pelo menos 5 caracteres",
155
+ "social_error": "Não foi possível entrar com $provider",
156
+ "email_already_registered": "Esse e-mail já tem conta com outro método de login. Entre com o método que você usou antes."
147
157
  },
148
158
  "signup": {
149
159
  "title": "Cadastre-se agora",
@@ -237,7 +247,17 @@
237
247
  "managed_elsewhere_title": "Assinatura em outra plataforma",
238
248
  "managed_elsewhere_description": "Esta assinatura foi feita em outra plataforma e não pode ser gerenciada ou cancelada por aqui. Acesse a conta na plataforma onde você fez a compra.",
239
249
  "restore_button": "Restaurar compras",
240
- "cancel_button": "Fechar"
250
+ "cancel_button": "Fechar",
251
+ "billing_title": "Cobrança",
252
+ "plan_label": "Plano da conta",
253
+ "plan_fallback": "Premium",
254
+ "manage_subscription": "Gerenciar assinatura",
255
+ "restore_purchases": "Restaurar compras",
256
+ "renews_on": "Renova em $date",
257
+ "expires_on": "Expira em $date",
258
+ "trial_label": "Avaliação gratuita",
259
+ "trial_until": "Avaliação gratuita até $date",
260
+ "charges_from": "$price a partir de $date"
241
261
  },
242
262
  "paywallWithSwitch": {
243
263
  "withTrial": {
@@ -386,7 +406,15 @@
386
406
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
387
407
  "- Lorem ipsum dolor sit amet, consectetur adipiscing elit."
388
408
  ],
389
- "continue_button": "Continuar"
409
+ "continue_button": "Entendi"
410
+ },
411
+ "update_available": {
412
+ "title": "Atualização disponível",
413
+ "description": "Há uma versão mais nova do app. Atualize para ter as últimas melhorias e correções.",
414
+ "forced_title": "Atualização necessária",
415
+ "forced_description": "Esta versão não é mais suportada. Atualize para continuar usando o app.",
416
+ "update_button": "Atualizar agora",
417
+ "later_button": "Agora não"
390
418
  },
391
419
  "request_notification_permission": {
392
420
  "title": "Ativar notificações?",
@@ -402,8 +430,8 @@
402
430
  "cancel_button": "Cancelar"
403
431
  },
404
432
  "review_popup": {
405
- "title": "Sua opinião importa!",
406
- "description": "Ajude-nos a crescer compartilhando sua experiência. Leva apenas alguns segundos e faz uma enorme diferença para nós 🙏🏻",
433
+ "title": "Sua opinião importa",
434
+ "description": "Se o app tem te ajudado, uma avaliação rápida faz toda a diferença. Prefere sugerir melhorias? A gente quer ouvir.",
407
435
  "cancel_button": "Sugerir melhorias",
408
436
  "rate_button": "Escrever uma avaliação"
409
437
  },
@@ -485,6 +513,7 @@
485
513
  "biometric_not_enabled_message": "Bloqueio do app não foi ativado.",
486
514
  "feedback": "Enviar feedback",
487
515
  "premium": "Premium",
516
+ "billing": "Cobrança",
488
517
  "privacy": "Política de privacidade",
489
518
  "support": "Suporte",
490
519
  "disconnect": "Sim, sair",
@@ -497,6 +526,20 @@
497
526
  "register": "Cadastrar",
498
527
  "name_label": "Nome",
499
528
  "email_label": "Email",
529
+ "connected_with_label": "Conectado com",
530
+ "provider_email": "E-mail e senha",
531
+ "provider_phone": "Telefone",
532
+ "create_password_title": "Criar senha",
533
+ "create_password_subtitle": "Defina uma senha para também entrar com e-mail e senha, além do login social.",
534
+ "create_password_field": "Nova senha",
535
+ "create_password_confirm_label": "Confirmar senha",
536
+ "create_password_success": "Senha criada",
537
+ "create_password_error": "Não foi possível criar a senha. Tente novamente.",
538
+ "create_password_too_short": "A senha deve ter pelo menos 6 caracteres",
539
+ "create_password_mismatch": "As senhas não coincidem",
540
+ "link_social": "Vincular $provider",
541
+ "link_social_success": "$provider vinculado",
542
+ "link_social_error": "Não foi possível vincular. Tente novamente.",
500
543
  "edit_name_title": "Editar nome",
501
544
  "edit_name_hint": "Seu nome",
502
545
  "edit_name_save": "Salvar",
@@ -517,7 +560,8 @@
517
560
  "error": "Algo deu errado. Por favor, tente novamente."
518
561
  },
519
562
  "admin": {
520
- "update_bottom_sheet": "Atualizar bottom sheet",
563
+ "update_bottom_sheet": "Pré-visualizar novidades",
564
+ "preview_update_available": "Pré-visualizar atualização disponível",
521
565
  "paywalls": "Paywalls",
522
566
  "test_onboarding": "Testar onboarding",
523
567
  "copy_user_id": "Copiar ID do usuário",
@@ -526,12 +570,13 @@
526
570
  "fcm_token_copied": "FCM Token copiado para a área de transferência",
527
571
  "fcm_token_unavailable": "Token não disponível (notificações desativadas?)",
528
572
  "ask_notification": "Pedir permissão de notificação",
573
+ "native_only": "Disponível apenas no app nativo (iOS / Android)",
529
574
  "ask_review": "Pedir avaliação",
530
575
  "home_widgets_panel": "Painel de Home Widgets",
531
576
  "home_widgets_title": "Painel de Home Widgets",
532
577
  "inspector_fab_title": "Inspector de widgets",
533
578
  "inspector_fab_subtitle_prefix": "Atalho global:",
534
- "update_mywidget_title": "📱 Atualizar Widget MyWidget",
579
+ "update_mywidget_title": "Atualizar Widget MyWidget",
535
580
  "update_mywidget_desc": "Chamar atualização manual para o widget MyWidget",
536
581
  "paywalls_title": "Painel Admin de Paywalls",
537
582
  "send_push_title": "Enviar notificação",
@@ -617,6 +662,8 @@
617
662
  "title_verify": "Verificar Código",
618
663
  "verification_code": "Código de Verificação",
619
664
  "code_sent": "Enviamos um código de verificação para $phone",
665
+ "signin_success_title": "Tudo certo",
666
+ "signin_success_text": "Você entrou com seu número de telefone",
620
667
  "verify_code": "Verificar Código",
621
668
  "resend_code": "Reenviar Código",
622
669
  "enter_all_digits": "Por favor, digite todos os 6 números"
@@ -18,9 +18,11 @@ import 'package:kasy_kit/core/initializer/onstart_widget.dart';
18
18
  import 'package:kasy_kit/core/keyboard_fix/keyboard_flicker_fix.dart';
19
19
  import 'package:kasy_kit/core/shared_preferences/shared_preferences.dart';
20
20
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
21
+ import 'package:kasy_kit/core/theme/responsive_text_theme.dart';
21
22
  import 'package:kasy_kit/core/theme/theme.dart';
22
23
  import 'package:kasy_kit/core/web_device_preview/web_device_preview.dart';
23
24
  import 'package:kasy_kit/core/web_viewport_scale.dart';
25
+ import 'package:kasy_kit/core/widgets/focus_visibility.dart';
24
26
  import 'package:kasy_kit/environments.dart';
25
27
  import 'package:kasy_kit/features/authentication/api/authentication_api.dart';
26
28
  import 'package:kasy_kit/features/notifications/api/local_notifier.dart';
@@ -189,8 +191,15 @@ class _MyAppState extends ConsumerState<MyApp> {
189
191
  analyticsApiProvider,
190
192
  facebookEventApiProvider,
191
193
  ],
192
- onReady: WebViewportScale.wrap(
193
- DevicePreview.appBuilder(context, child),
194
+ onReady: FocusVisibility(
195
+ child: WebViewportScale.wrap(
196
+ DevicePreview.appBuilder(
197
+ context,
198
+ ResponsiveTextTheme(
199
+ child: child ?? const SizedBox.shrink(),
200
+ ),
201
+ ),
202
+ ),
194
203
  ),
195
204
  onError: (_, error) => InitializationErrorPage(error: error),
196
205
  onLoading: Scaffold(
@@ -3,12 +3,15 @@ import 'package:flutter/material.dart';
3
3
  import 'package:flutter_riverpod/flutter_riverpod.dart';
4
4
  import 'package:go_router/go_router.dart';
5
5
  import 'package:kasy_kit/core/bottom_menu/bottom_menu.dart';
6
+ import 'package:kasy_kit/core/chrome/chrome_visibility.dart';
6
7
  import 'package:kasy_kit/core/config/features.dart';
7
8
  import 'package:kasy_kit/core/data/api/analytics_api.dart';
8
- import 'package:kasy_kit/core/guards/user_info_guard.dart';
9
+ import 'package:kasy_kit/core/data/models/user.dart';
9
10
  import 'package:kasy_kit/core/navigation/kasy_navigation_config.dart';
10
11
  import 'package:kasy_kit/core/navigation/kasy_page_transition.dart';
11
12
  import 'package:kasy_kit/core/security/biometric_guard.dart';
13
+ import 'package:kasy_kit/core/shared_preferences/shared_preferences.dart';
14
+ import 'package:kasy_kit/core/states/user_state_notifier.dart';
12
15
  import 'package:kasy_kit/core/widgets/page_not_found.dart';
13
16
  import 'package:kasy_kit/features/authentication/ui/phone_auth_page.dart';
14
17
  import 'package:kasy_kit/features/authentication/ui/recover_password_page.dart';
@@ -30,7 +33,17 @@ import 'package:kasy_kit/features/subscriptions/ui/component/premium_page_factor
30
33
  import 'package:kasy_kit/features/subscriptions/ui/premium_page.dart';
31
34
  import 'package:logger/logger.dart';
32
35
 
33
- final goRouterProvider = Provider<GoRouter>((ref) => generateRouter());
36
+ final goRouterProvider = Provider<GoRouter>((ref) {
37
+ // Re-run the auth redirect whenever the user state changes (sign-in, logout,
38
+ // onboarding completion, guest continue). This is what makes navigation
39
+ // reactive and reliable — the redirect replaces the old "navigate from inside
40
+ // a widget's build()" pattern that could leave a black screen frozen until
41
+ // the user tapped the screen.
42
+ final refresh = ValueNotifier<int>(0);
43
+ ref.listen(userStateNotifierProvider, (_, _) => refresh.value++);
44
+ ref.onDispose(refresh.dispose);
45
+ return generateRouter(ref: ref, refreshListenable: refresh);
46
+ });
34
47
 
35
48
  extension GoRouterRiverpod on Ref {
36
49
  GoRouter get goRouter => read(goRouterProvider);
@@ -38,14 +51,86 @@ extension GoRouterRiverpod on Ref {
38
51
 
39
52
  final navigatorKey = GlobalKey<NavigatorState>();
40
53
 
54
+ /// Auth/onboarding routes the redirect must never bounce an unauthenticated
55
+ /// user away from (they're the destinations).
56
+ const Set<String> _authRoutes = {
57
+ '/signin',
58
+ '/signup',
59
+ '/signinWithPhone',
60
+ '/recover_password',
61
+ };
62
+
63
+ /// Single source of truth for "where should this user be allowed to go".
64
+ ///
65
+ /// Replaces the old widget-tree guards. Driven reactively by GoRouter via the
66
+ /// [goRouterProvider] refresh listenable, so a logout (or sign-in, or onboarding
67
+ /// completion) navigates immediately without depending on a post-frame callback.
68
+ String? _authRedirect(Ref ref, GoRouterState state) {
69
+ final user = ref.read(userStateNotifierProvider).user;
70
+ // Session still resolving on boot: the Initializer shows a spinner. Don't
71
+ // redirect yet — we re-run once the state resolves (refresh listenable).
72
+ if (user.isLoading) return null;
73
+
74
+ final String loc = state.matchedLocation;
75
+ final bool onAuthRoute = _authRoutes.contains(loc);
76
+ final bool onboardingDone = ref
77
+ .read(sharedPreferencesProvider)
78
+ .getOnboardingCompleted();
79
+ final bool hasIdentity = user.idOrNull != null;
80
+
81
+ // WEB: there's no onboarding screen. Require a signed-in account OR an
82
+ // explicit guest choice ("continue without account" sets isOnboarded).
83
+ if (kIsWeb && withWeb) {
84
+ final bool ready = hasIdentity || user.isOnboarded || onboardingDone;
85
+ if (ready) {
86
+ if (onAuthRoute && user is AuthenticatedUserData) return '/';
87
+ return null;
88
+ }
89
+ return onAuthRoute ? null : '/signin';
90
+ }
91
+
92
+ // NATIVE. Let the onboarding flow own its own screens and its exit: it runs a
93
+ // nested navigator (questions → permissions → loader → paywall) and navigates
94
+ // to '/' itself when done. Bouncing it off '/onboarding' here would tear that
95
+ // nested flow down mid-way.
96
+ if (loc == '/onboarding') return null;
97
+
98
+ // Authenticated users skip onboarding: signing in is itself a commitment to
99
+ // the app, and they may be returning users who onboarded on another device.
100
+ final bool onboardedEffective =
101
+ !withOnboarding ||
102
+ user.isOnboarded ||
103
+ onboardingDone ||
104
+ user is AuthenticatedUserData;
105
+
106
+ // 1) One-time onboarding gate. Auth routes (sign-in/sign-up/recover) stay
107
+ // reachable even before onboarding is done — that's the "I already have an
108
+ // account" path out of the first onboarding screen. Only non-auth routes
109
+ // are gated back to onboarding.
110
+ if (!onboardedEffective) return onAuthRoute ? null : '/onboarding';
111
+
112
+ // 2) Onboarded: using the app needs a backend identity. Anonymous accounts
113
+ // are created lazily (onboarding end / "continue as guest"), so after a
114
+ // logout there's no identity → send to sign-in, never back to onboarding.
115
+ if (hasIdentity) {
116
+ if (onAuthRoute && user is AuthenticatedUserData) return '/';
117
+ return null;
118
+ }
119
+ return onAuthRoute ? null : '/signin';
120
+ }
121
+
41
122
  GoRouter generateRouter({
123
+ required Ref ref,
42
124
  String? initialLocation,
43
125
  List<GoRoute>? additionalRoutes,
44
126
  List<NavigatorObserver>? observers,
127
+ Listenable? refreshListenable,
45
128
  }) {
46
129
  return GoRouter(
47
- initialLocation: '/',
130
+ initialLocation: initialLocation ?? '/',
48
131
  navigatorKey: navigatorKey,
132
+ refreshListenable: refreshListenable,
133
+ redirect: (context, state) => _authRedirect(ref, state),
49
134
  // Catches unknown routes (e.g. an Android warm-start from the home widget
50
135
  // landed on a stale URI) and silently sends the user home instead of
51
136
  // surfacing a dead-end "404" page. We log the offending URI so a real
@@ -62,6 +147,7 @@ GoRouter generateRouter({
62
147
  },
63
148
  observers: [
64
149
  AnalyticsObserver(analyticsApi: MixpanelAnalyticsApi.instance()),
150
+ KasyChromeVisibilityObserver(),
65
151
 
66
152
  ...?observers,
67
153
  ],
@@ -71,10 +157,7 @@ GoRouter generateRouter({
71
157
  path: '/',
72
158
  pageBuilder: (context, state) => kasyTransitionPage(
73
159
  key: state.pageKey,
74
- child: const UserInfosGuard(
75
- fallbackRoute: '/onboarding',
76
- child: BiometricGuard(child: BottomMenu()),
77
- ),
160
+ child: const BiometricGuard(child: BottomMenu()),
78
161
  ),
79
162
  ),
80
163
  // Home showcase detail screens. These are TOP-LEVEL routes (siblings of
@@ -190,9 +273,8 @@ GoRouter generateRouter({
190
273
  path: '/notifications',
191
274
  pageBuilder: (context, state) => kasyTransitionPage(
192
275
  key: state.pageKey,
193
- child: const UserInfosGuard(
194
- fallbackRoute: '/onboarding',
195
- child: BiometricGuard(child: BottomMenu(initialRoute: 'notifications')),
276
+ child: const BiometricGuard(
277
+ child: BottomMenu(initialRoute: 'notifications'),
196
278
  ),
197
279
  ),
198
280
  ),
@@ -201,9 +283,8 @@ GoRouter generateRouter({
201
283
  path: '/settings',
202
284
  pageBuilder: (context, state) => kasyTransitionPage(
203
285
  key: state.pageKey,
204
- child: const UserInfosGuard(
205
- fallbackRoute: '/onboarding',
206
- child: BiometricGuard(child: BottomMenu(initialRoute: 'settings')),
286
+ child: const BiometricGuard(
287
+ child: BottomMenu(initialRoute: 'settings'),
207
288
  ),
208
289
  ),
209
290
  ),