import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/notifications/notif_permission.dart'; import '../../../core/theme/halo_tokens.dart'; import '../../../core/theme/widgets/widgets.dart'; /// Phase 4 Stage 4 — post-payment notif gate. /// /// If the OS notif permission is already granted on entry, the screen /// auto-advances to the searching shell. Otherwise it offers two CTAs: /// "izinkan notifikasi" (request) and "nanti aja" (skip). Either resolution /// path advances to the searching shell — Stage 5 will refine that target if /// the post-payment pairing entry needs to differ. // TODO(stage5): if Stage 5 introduces a dedicated post-payment pairing entry, // retarget the advance from `/chat/searching` to whichever route owns the // blast trigger after a paid session. class NotifGateScreen extends ConsumerStatefulWidget { const NotifGateScreen({super.key}); @override ConsumerState createState() => _NotifGateScreenState(); } class _NotifGateScreenState extends ConsumerState { static const String _advanceRoute = '/chat/searching'; bool _resolving = false; bool _autoAdvanced = false; void _advance() { if (!mounted) return; context.go(_advanceRoute); } Future _onAllow() async { if (_resolving) return; setState(() => _resolving = true); await ref.read(notifPermissionStatusProvider.notifier).request(); if (!mounted) return; _advance(); } void _onLater() => _advance(); @override Widget build(BuildContext context) { final statusAsync = ref.watch(notifPermissionStatusProvider); // Already-granted users skip the screen entirely. Schedule the redirect // post-frame so we don't `context.go` mid-build. if (!_autoAdvanced && statusAsync.valueOrNull == NotifPermStatus.granted) { _autoAdvanced = true; WidgetsBinding.instance.addPostFrameCallback((_) => _advance()); } return PopScope( // The notif gate sits between payment-confirmed and the searching // shell. There is nothing meaningful to pop back to (the waiting // screen is terminal once paid), so we swallow the back gesture. canPop: false, child: Scaffold( backgroundColor: HaloTokens.bg, body: SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB( HaloSpacing.s24, HaloSpacing.s32, HaloSpacing.s24, HaloSpacing.s24, ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Spacer(), Center( child: Container( width: 96, height: 96, decoration: const BoxDecoration( color: HaloTokens.brandSofter, shape: BoxShape.circle, ), child: const Icon( Icons.notifications_active_outlined, color: HaloTokens.brandDark, size: 44, ), ), ), const SizedBox(height: HaloSpacing.s24), const Text( 'biar nggak ketinggalan', textAlign: TextAlign.center, style: TextStyle( fontFamily: HaloTokens.fontDisplay, fontSize: 26, height: 30 / 26, fontWeight: FontWeight.w700, color: HaloTokens.ink, ), ), const SizedBox(height: HaloSpacing.s12), const Text( 'kami pingin kabarin kamu pas bestie udah siap dengerin. izinin notifikasi biar nggak ada chat yang kelewat.', textAlign: TextAlign.center, style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 14, height: 20 / 14, color: HaloTokens.inkSoft, ), ), const Spacer(), HaloButton( label: 'izinkan notifikasi', fullWidth: true, onPressed: _resolving ? null : _onAllow, ), const SizedBox(height: HaloSpacing.s12), HaloButton( label: 'nanti aja', variant: HaloButtonVariant.ghost, fullWidth: true, onPressed: _resolving ? null : _onLater, ), ], ), ), ), ), ); } }