import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../estado/estado_entitlement.dart'; import '../l10n/gen/app_localizations.dart'; import '../tema/pluriwave_tokens.dart'; import 'pluri_glass_surface.dart'; import 'pluri_layout.dart'; /// Reusable paywall sheet (Design "File Changes" — `hoja_premium.dart`), /// opened from every gated entry point plus the Settings premium row /// (freemium-gating spec "Purchase Entry Points At Every Gate Plus /// Settings"). Mirrors `FormularioEmisoraPersonalizada`'s bottom-sheet /// shape (`ajustes_emisoras_personalizadas.dart`). Future mostrarHojaPremium(BuildContext context) { return showModalBottomSheet( context: context, isScrollControlled: true, useSafeArea: true, backgroundColor: Colors.transparent, builder: (_) => const HojaPremium(), ); } class HojaPremium extends StatelessWidget { const HojaPremium({super.key}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final entitlement = context.watch(); final bottom = MediaQuery.of(context).viewInsets.bottom; return Padding( padding: EdgeInsets.fromLTRB( PluriLayout.horizontal, PluriLayout.horizontal, PluriLayout.horizontal, PluriLayout.horizontal + bottom, ), child: PluriGlassSurface( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ Icon( Icons.workspace_premium_rounded, color: PluriWaveTokens.brand, ), const SizedBox(width: 10), Expanded( child: Text( l10n.funcionPremium, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w900, ), ), ), ], ), const SizedBox(height: 20), // FIX 3 (code review): user-facing feedback for a failed // purchase/restore, or a restore that found nothing — before // this, `resultadoUsuario` had ZERO UI, so the spinner just // stopped with no feedback at all. Never the raw // `EventoCompra.mensaje` developer string — always the mapped, // generic localized message. if (entitlement.resultadoUsuario != null) Padding( key: const ValueKey('hoja-premium-resultado'), padding: const EdgeInsets.only(bottom: 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( entitlement.resultadoUsuario == ResultadoEntitlementUsuario.error ? Icons.error_outline_rounded : Icons.info_outline_rounded, size: 18, color: entitlement.resultadoUsuario == ResultadoEntitlementUsuario.error ? Theme.of(context).colorScheme.error : Theme.of(context).textTheme.bodyMedium?.color, ), const SizedBox(width: 8), Expanded( child: Text( entitlement.resultadoUsuario == ResultadoEntitlementUsuario.error ? l10n.compraError : l10n.restauracionSinCompras, style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( key: const ValueKey('hoja-premium-resultado-descartar'), icon: const Icon(Icons.close_rounded, size: 18), onPressed: () => entitlement.consumirResultadoUsuario(), padding: EdgeInsets.zero, constraints: const BoxConstraints(), visualDensity: VisualDensity.compact, ), ], ), ), if (entitlement.esPremium) Padding( key: const ValueKey('hoja-premium-activo'), padding: const EdgeInsets.only(bottom: 12), child: Text( l10n.premiumActivo, style: Theme.of(context).textTheme.bodyMedium, ), ) else FilledButton.icon( key: const ValueKey('hoja-premium-comprar'), onPressed: entitlement.compraEnCurso ? null : () => entitlement.comprar(), icon: entitlement.compraEnCurso ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.lock_open_rounded), label: Text(l10n.desbloquearPremium), ), const SizedBox(height: 10), OutlinedButton( key: const ValueKey('hoja-premium-restaurar'), onPressed: entitlement.compraEnCurso ? null : () => entitlement.restaurar(), child: Text(l10n.restaurarCompras), ), ], ), ), ); } }