import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/api/api_client_provider.dart'; import '../../../core/auth/auth_notifier.dart'; import '../../../core/theme/halo_tokens.dart'; import '../../../core/theme/widgets/widgets.dart'; import '../widgets/verif_choice_sheet.dart'; class DisplayNameScreen extends ConsumerStatefulWidget { const DisplayNameScreen({super.key}); @override ConsumerState createState() => _DisplayNameScreenState(); } class _DisplayNameScreenState extends ConsumerState { final _controller = TextEditingController(); ProviderSubscription>? _authSub; String? _errorMessage; bool _routedAfterLogin = false; @override void initState() { super.initState(); // Listener registered once in initState (see feedback_riverpod_listen_in_build). // We need to react to auth state changes once the anonymous login resolves // to drive the post-name onboarding fork. _authSub = ref.listenManual>(authProvider, (prev, next) { if (!mounted) return; if (next is AsyncError) { setState(() => _errorMessage = next.error.toString()); return; } final data = next.valueOrNull; if (data is AuthAnonymousData && !_routedAfterLogin) { _routedAfterLogin = true; _proceedAfterLogin(); } }); } @override void dispose() { _authSub?.close(); _controller.dispose(); super.dispose(); } void _submit() { final name = _controller.text.trim(); if (name.isEmpty) return; setState(() => _errorMessage = null); ref.read(authProvider.notifier).loginAnonymous(name); } /// After an anonymous login succeeds, decide where to send the user. /// /// 1. Read `/api/client/onboarding-state`. If `has_consulted_before`, the /// user is a returning customer — skip the onboarding sequence and /// jump straight to the duration picker (Stage 3 owns that route). /// 2. Otherwise show the Verif Choice Sheet and route based on the picked /// branch. Future _proceedAfterLogin() async { bool hasConsultedBefore = false; try { final response = await ref.read(apiClientProvider).get('/api/client/onboarding-state'); final data = response['data'] as Map?; hasConsultedBefore = (data?['has_consulted_before'] as bool?) ?? false; } catch (_) { // Treat as first-time on failure — safer to over-collect onboarding // info than to silently strand a returning user. } if (!mounted) return; if (hasConsultedBefore) { // TODO(stage3): Stage 3 will own /payment/duration-pick — for now // route there as a placeholder so returning users can continue. context.go('/payment/duration-pick'); return; } final choice = await VerifChoiceSheet.show(context); if (!mounted || choice == null) { // User dismissed the sheet — let them tap Lanjut again to retry. _routedAfterLogin = false; return; } if (!mounted) return; await routeForVerifChoice(context, ref, choice); } @override Widget build(BuildContext context) { final authState = ref.watch(authProvider); final isLoading = authState is AsyncLoading; return Scaffold( appBar: AppBar(title: const Text('Siapa namamu?')), body: SafeArea( child: Padding( padding: const EdgeInsets.all(HaloSpacing.s24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Pilih nama yang ingin kamu gunakan. Nama ini akan terlihat oleh bestie kamu.', style: TextStyle( fontFamily: HaloTokens.fontBody, fontSize: 15, height: 22 / 15, color: HaloTokens.inkSoft, ), ), const SizedBox(height: HaloSpacing.s24), TextField( controller: _controller, decoration: const InputDecoration( labelText: 'Nama panggilan', ), textInputAction: TextInputAction.done, onSubmitted: (_) => _submit(), ), if (_errorMessage != null) ...[ const SizedBox(height: HaloSpacing.s12), Text( _errorMessage!, textAlign: TextAlign.center, style: const TextStyle( fontFamily: HaloTokens.fontBody, color: HaloTokens.danger, fontSize: 13, ), ), ], const SizedBox(height: HaloSpacing.s24), HaloButton( label: isLoading ? 'memproses...' : 'lanjut', fullWidth: true, onPressed: isLoading ? null : _submit, ), ], ), ), ), ); } }