kasy-cli 1.34.0 → 1.36.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 (141) hide show
  1. package/README.md +1 -1
  2. package/bin/kasy.js +24 -2
  3. package/docs/cli-reference.md +7 -7
  4. package/lib/commands/new.js +11 -9
  5. package/lib/commands/release-version.js +234 -0
  6. package/lib/commands/update.js +27 -0
  7. package/lib/scaffold/CHANGELOG.json +18 -0
  8. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +24 -0
  9. package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api_interface.dart +15 -0
  10. package/lib/scaffold/backends/api/patch/lib/features/authentication/repositories/authentication_repository.dart +27 -0
  11. package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  12. package/lib/scaffold/backends/firebase/setup-from-scratch.js +35 -21
  13. package/lib/scaffold/backends/patch-base-hashes.json +66 -0
  14. package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +2 -0
  15. package/lib/scaffold/backends/supabase/migrations/20240101000007_welcome_notification.sql +36 -23
  16. package/lib/scaffold/backends/supabase/migrations/20240101000014_subscription_trial_end.sql +6 -0
  17. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +82 -3
  18. package/lib/scaffold/backends/supabase/patch/lib/features/authentication/repositories/authentication_repository.dart +36 -0
  19. package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/entities/subscription_entity.dart +1 -0
  20. package/lib/scaffold/generate.js +53 -4
  21. package/lib/utils/i18n/messages-en.js +23 -0
  22. package/lib/utils/i18n/messages-es.js +23 -0
  23. package/lib/utils/i18n/messages-pt.js +23 -0
  24. package/package.json +5 -2
  25. package/templates/firebase/.firebase/hosting.YnVpbGQvd2Vi.cache +73 -0
  26. package/templates/firebase/AGENTS.md +83 -0
  27. package/templates/firebase/DESIGN_SYSTEM.md +37 -2
  28. package/templates/firebase/docs/auth-setup.en.md +2 -0
  29. package/templates/firebase/docs/auth-setup.es.md +2 -0
  30. package/templates/firebase/docs/auth-setup.pt.md +2 -0
  31. package/templates/firebase/firebase.json +56 -1
  32. package/templates/firebase/functions/src/authentication/triggers.ts +10 -6
  33. package/templates/firebase/functions/src/core/data/entities/subscription_entity.ts +5 -0
  34. package/templates/firebase/functions/src/core/data/entities/user_entity.ts +9 -1
  35. package/templates/firebase/functions/src/core/data/repositories/user_repository.ts +27 -0
  36. package/templates/firebase/functions/src/subscriptions/models/subscriptions.ts +3 -0
  37. package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +4 -0
  38. package/templates/firebase/lib/components/kasy_alert.dart +0 -1
  39. package/templates/firebase/lib/components/kasy_app_bar.dart +31 -16
  40. package/templates/firebase/lib/components/kasy_bottom_sheet.dart +0 -1
  41. package/templates/firebase/lib/components/kasy_date_picker.dart +0 -5
  42. package/templates/firebase/lib/components/kasy_dialog.dart +0 -1
  43. package/templates/firebase/lib/components/kasy_otp_verification_bottom_sheet.dart +1 -1
  44. package/templates/firebase/lib/components/kasy_sidebar.dart +215 -178
  45. package/templates/firebase/lib/components/kasy_text_area.dart +0 -1
  46. package/templates/firebase/lib/components/kasy_text_field.dart +0 -1
  47. package/templates/firebase/lib/components/kasy_text_field_otp.dart +0 -1
  48. package/templates/firebase/lib/components/kasy_toast.dart +107 -41
  49. package/templates/firebase/lib/core/app_update/app_update_repository.dart +54 -0
  50. package/templates/firebase/lib/core/app_update/app_update_status.dart +14 -0
  51. package/templates/firebase/lib/core/app_update/update_available_sheet.dart +70 -0
  52. package/templates/firebase/lib/core/bottom_menu/active_tab_notifier.dart +40 -3
  53. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +82 -34
  54. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +6 -6
  55. package/templates/firebase/lib/core/bottom_menu/web_url.dart +20 -0
  56. package/templates/firebase/lib/core/data/api/remote_config_api.dart +38 -1
  57. package/templates/firebase/lib/core/guards/guard.dart +16 -2
  58. package/templates/firebase/lib/core/icons/kasy_icons.dart +3 -0
  59. package/templates/firebase/lib/core/rating/widgets/rate_banner.dart +2 -5
  60. package/templates/firebase/lib/core/rating/widgets/review_popup.dart +5 -3
  61. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +14 -0
  62. package/templates/firebase/lib/core/states/components/maybe_show_update_available.dart +32 -0
  63. package/templates/firebase/lib/core/states/logout_action.dart +5 -1
  64. package/templates/firebase/lib/core/states/user_state_notifier.dart +85 -14
  65. package/templates/firebase/lib/core/theme/responsive_text_theme.dart +69 -0
  66. package/templates/firebase/lib/core/theme/texts.dart +90 -57
  67. package/templates/firebase/lib/core/theme/type_scale.dart +77 -0
  68. package/templates/firebase/lib/core/theme/web_background_sync_web.dart +20 -6
  69. package/templates/firebase/lib/core/utils/image_bytes_loader.dart +12 -0
  70. package/templates/firebase/lib/core/utils/image_bytes_loader_io.dart +28 -0
  71. package/templates/firebase/lib/core/utils/image_bytes_loader_web.dart +56 -0
  72. package/templates/firebase/lib/core/web_screen_width.dart +15 -0
  73. package/templates/firebase/lib/core/web_screen_width_io.dart +3 -0
  74. package/templates/firebase/lib/core/web_screen_width_web.dart +10 -0
  75. package/templates/firebase/lib/core/web_viewport_scale.dart +61 -25
  76. package/templates/firebase/lib/core/widgets/focus_visibility.dart +89 -0
  77. package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +1 -2
  78. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_chat_conversation_view.dart +1 -2
  79. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +2 -3
  80. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +1 -2
  81. package/templates/firebase/lib/features/authentication/api/auth_web_support.dart +12 -0
  82. package/templates/firebase/lib/features/authentication/api/auth_web_support_web.dart +25 -0
  83. package/templates/firebase/lib/features/authentication/api/authentication_api.dart +205 -0
  84. package/templates/firebase/lib/features/authentication/api/authentication_api_interface.dart +22 -0
  85. package/templates/firebase/lib/features/authentication/navigation/post_login_navigation.dart +32 -0
  86. package/templates/firebase/lib/features/authentication/providers/phone_auth_notifier.dart +7 -7
  87. package/templates/firebase/lib/features/authentication/providers/signin_state_provider.dart +34 -10
  88. package/templates/firebase/lib/features/authentication/providers/signup_state_provider.dart +2 -2
  89. package/templates/firebase/lib/features/authentication/repositories/authentication_repository.dart +37 -0
  90. package/templates/firebase/lib/features/authentication/repositories/exceptions/authentication_exceptions.dart +8 -0
  91. package/templates/firebase/lib/features/authentication/ui/components/otp_verification.dart +2 -2
  92. package/templates/firebase/lib/features/authentication/ui/components/phone_input.dart +2 -2
  93. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +59 -0
  94. package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +2 -2
  95. package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +2 -2
  96. package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -1
  97. package/templates/firebase/lib/features/home/design_system_page.dart +134 -67
  98. package/templates/firebase/lib/features/home/home_components_page.dart +4 -3
  99. package/templates/firebase/lib/features/home/home_components_preview_page.dart +1 -3
  100. package/templates/firebase/lib/features/home/home_components_preview_registry.dart +226 -105
  101. package/templates/firebase/lib/features/home/home_page.dart +4 -0
  102. package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +8 -3
  103. package/templates/firebase/lib/features/notifications/providers/notifications_provider.dart +10 -0
  104. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +3 -1
  105. package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +8 -3
  106. package/templates/firebase/lib/features/onboarding/providers/onboarding_model.dart +11 -0
  107. package/templates/firebase/lib/features/onboarding/providers/onboarding_provider.dart +30 -3
  108. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +13 -4
  109. package/templates/firebase/lib/features/settings/settings_page.dart +152 -11
  110. package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +43 -15
  111. package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +12 -12
  112. package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +1 -4
  113. package/templates/firebase/lib/features/settings/ui/components/create_password_sheet.dart +141 -0
  114. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +0 -1
  115. package/templates/firebase/lib/features/settings/ui/widgets/admin_card.dart +42 -38
  116. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +17 -2
  117. package/templates/firebase/lib/features/subscriptions/api/entities/subscription_entity.dart +3 -0
  118. package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +12 -11
  119. package/templates/firebase/lib/features/subscriptions/repositories/subscription_repository.dart +25 -2
  120. package/templates/firebase/lib/features/subscriptions/ui/component/active_premium_content.dart +319 -143
  121. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_minimal.dart +1 -5
  122. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_row.dart +1 -5
  123. package/templates/firebase/lib/features/subscriptions/ui/component/paywall_with_switch.dart +2 -5
  124. package/templates/firebase/lib/features/subscriptions/ui/component/premium_content.dart +1 -4
  125. package/templates/firebase/lib/features/subscriptions/ui/widgets/feature_line.dart +1 -2
  126. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +2 -7
  127. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_feature.dart +2 -4
  128. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +1 -4
  129. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +0 -3
  130. package/templates/firebase/lib/i18n/en.i18n.json +49 -3
  131. package/templates/firebase/lib/i18n/es.i18n.json +49 -3
  132. package/templates/firebase/lib/i18n/pt.i18n.json +49 -3
  133. package/templates/firebase/lib/main.dart +11 -2
  134. package/templates/firebase/lib/router.dart +92 -13
  135. package/templates/firebase/pubspec.yaml +1 -1
  136. package/templates/firebase/test/core/data/api/fake_remote_config_api.dart +14 -0
  137. package/templates/firebase/test/core/states/user_state_notifier_test.dart +47 -3
  138. package/templates/firebase/test/core/web_viewport_scale_test.dart +68 -0
  139. package/templates/firebase/test/features/authentication/data/api/auth_api_fake.dart +15 -0
  140. package/templates/firebase/web/index.html +162 -14
  141. package/templates/firebase/lib/core/guards/user_info_guard.dart +0 -61
@@ -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?",
@@ -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",
@@ -518,6 +561,7 @@
518
561
  },
519
562
  "admin": {
520
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",
@@ -618,6 +662,8 @@
618
662
  "title_verify": "Verify code",
619
663
  "verification_code": "Verification Code",
620
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",
621
667
  "verify_code": "Verify Code",
622
668
  "resend_code": "Resend Code",
623
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?",
@@ -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",
@@ -518,6 +561,7 @@
518
561
  },
519
562
  "admin": {
520
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",
@@ -618,6 +662,8 @@
618
662
  "title_verify": "Verificar Código",
619
663
  "verification_code": "Código de Verificación",
620
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",
621
667
  "verify_code": "Verificar Código",
622
668
  "resend_code": "Reenviar Código",
623
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?",
@@ -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",
@@ -518,6 +561,7 @@
518
561
  },
519
562
  "admin": {
520
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",
@@ -618,6 +662,8 @@
618
662
  "title_verify": "Verificar Código",
619
663
  "verification_code": "Código de Verificação",
620
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",
621
667
  "verify_code": "Verificar Código",
622
668
  "resend_code": "Reenviar Código",
623
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(
@@ -6,10 +6,12 @@ import 'package:kasy_kit/core/bottom_menu/bottom_menu.dart';
6
6
  import 'package:kasy_kit/core/chrome/chrome_visibility.dart';
7
7
  import 'package:kasy_kit/core/config/features.dart';
8
8
  import 'package:kasy_kit/core/data/api/analytics_api.dart';
9
- import 'package:kasy_kit/core/guards/user_info_guard.dart';
9
+ import 'package:kasy_kit/core/data/models/user.dart';
10
10
  import 'package:kasy_kit/core/navigation/kasy_navigation_config.dart';
11
11
  import 'package:kasy_kit/core/navigation/kasy_page_transition.dart';
12
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';
13
15
  import 'package:kasy_kit/core/widgets/page_not_found.dart';
14
16
  import 'package:kasy_kit/features/authentication/ui/phone_auth_page.dart';
15
17
  import 'package:kasy_kit/features/authentication/ui/recover_password_page.dart';
@@ -31,7 +33,17 @@ import 'package:kasy_kit/features/subscriptions/ui/component/premium_page_factor
31
33
  import 'package:kasy_kit/features/subscriptions/ui/premium_page.dart';
32
34
  import 'package:logger/logger.dart';
33
35
 
34
- 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
+ });
35
47
 
36
48
  extension GoRouterRiverpod on Ref {
37
49
  GoRouter get goRouter => read(goRouterProvider);
@@ -39,14 +51,86 @@ extension GoRouterRiverpod on Ref {
39
51
 
40
52
  final navigatorKey = GlobalKey<NavigatorState>();
41
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
+
42
122
  GoRouter generateRouter({
123
+ required Ref ref,
43
124
  String? initialLocation,
44
125
  List<GoRoute>? additionalRoutes,
45
126
  List<NavigatorObserver>? observers,
127
+ Listenable? refreshListenable,
46
128
  }) {
47
129
  return GoRouter(
48
- initialLocation: '/',
130
+ initialLocation: initialLocation ?? '/',
49
131
  navigatorKey: navigatorKey,
132
+ refreshListenable: refreshListenable,
133
+ redirect: (context, state) => _authRedirect(ref, state),
50
134
  // Catches unknown routes (e.g. an Android warm-start from the home widget
51
135
  // landed on a stale URI) and silently sends the user home instead of
52
136
  // surfacing a dead-end "404" page. We log the offending URI so a real
@@ -73,10 +157,7 @@ GoRouter generateRouter({
73
157
  path: '/',
74
158
  pageBuilder: (context, state) => kasyTransitionPage(
75
159
  key: state.pageKey,
76
- child: const UserInfosGuard(
77
- fallbackRoute: '/onboarding',
78
- child: BiometricGuard(child: BottomMenu()),
79
- ),
160
+ child: const BiometricGuard(child: BottomMenu()),
80
161
  ),
81
162
  ),
82
163
  // Home showcase detail screens. These are TOP-LEVEL routes (siblings of
@@ -192,9 +273,8 @@ GoRouter generateRouter({
192
273
  path: '/notifications',
193
274
  pageBuilder: (context, state) => kasyTransitionPage(
194
275
  key: state.pageKey,
195
- child: const UserInfosGuard(
196
- fallbackRoute: '/onboarding',
197
- child: BiometricGuard(child: BottomMenu(initialRoute: 'notifications')),
276
+ child: const BiometricGuard(
277
+ child: BottomMenu(initialRoute: 'notifications'),
198
278
  ),
199
279
  ),
200
280
  ),
@@ -203,9 +283,8 @@ GoRouter generateRouter({
203
283
  path: '/settings',
204
284
  pageBuilder: (context, state) => kasyTransitionPage(
205
285
  key: state.pageKey,
206
- child: const UserInfosGuard(
207
- fallbackRoute: '/onboarding',
208
- child: BiometricGuard(child: BottomMenu(initialRoute: 'settings')),
286
+ child: const BiometricGuard(
287
+ child: BottomMenu(initialRoute: 'settings'),
209
288
  ),
210
289
  ),
211
290
  ),
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16
16
  # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17
17
  # In Windows, build-name is used as the major, minor, and patch parts
18
18
  # of the product and file versions while build-number is used as the build suffix.
19
- version: 1.0.0+39
19
+ version: 1.0.0+53
20
20
 
21
21
  environment:
22
22
  sdk: ^3.11.0
@@ -23,6 +23,20 @@ class FakeRemoteConfigApi implements RemoteConfigApi {
23
23
  value: 'Lorem ipsum dolor sit amet',
24
24
  ),
25
25
  );
26
+
27
+ @override
28
+ AppUpdateConfigs get appUpdate => AppUpdateConfigs(
29
+ latestVersion: FakeRemoteConfigData<String>(
30
+ key: 'app_latest_version',
31
+ exists: true,
32
+ value: '0.0.0',
33
+ ),
34
+ minVersion: FakeRemoteConfigData<String>(
35
+ key: 'app_min_version',
36
+ exists: true,
37
+ value: '0.0.0',
38
+ ),
39
+ );
26
40
  }
27
41
 
28
42
  /// This is a fake implementation of RemoteConfigData