import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import 'package:sxchnge_analytics/sxchnge_analytics.dart'; import 'package:sxchnge_kyc/sxchnge_kyc.dart'; import 'copy.dart'; import 'form.dart'; import 'limits.dart'; import 'status.dart'; import '../generated/i18n/app_localizations.dart'; class IdentityPage extends StatefulWidget { const IdentityPage({super.key}); @override State createState() => IdentityPageState(); } class IdentityPageState extends State { late final _limits = LimitsProvider(context.read()); late final _identity = IdentityProvider( context.read(), context.read(), JourneyRecorder(context.read()), onStatusChanged: _limits.refresh, ); final _contentKey = GlobalKey(); final _feedbackKey = GlobalKey(); final _focus = FocusNode(skipTraversal: true); String? _lastView; void _transition() { final p = _identity; if (p.busy) return; final view = '${p.editing}:${p.step}:${p.record?.status}:${p.error}'; if (_lastView == null) { _lastView = view; return; } if (view == _lastView) return; _lastView = view; WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; final target = p.error != null ? _feedbackKey.currentContext : _contentKey.currentContext; if (target != null) { Scrollable.ensureVisible( target, alignment: 0.05, duration: const Duration(milliseconds: 180), ); } if (p.error == null) _focus.requestFocus(); }); } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _identity.addListener(_transition); _identity.refresh(); } }); } Future allowExit() async { if (_identity.busy) return false; if (!_identity.hasDraft) return true; final l = AppLocalizations.of(context)!; return await showDialog( context: context, builder: (context) => AlertDialog( title: Text(l.kycDiscardTitle), content: Text(l.kycDiscardBody), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text(l.kycStay), ), FilledButton( onPressed: () => Navigator.pop(context, true), child: Text(l.kycDiscard), ), ], ), ) ?? false; } Future _leave({bool account = false}) async { if (!await allowExit() || !mounted) return; _identity.exit(); if (account) { if (context.canPop()) { context.pop(); } else { context.go('/limits'); } } } @override void dispose() { _identity.removeListener(_transition); _identity.dispose(); _focus.dispose(); _limits.dispose(); super.dispose(); } @override Widget build(BuildContext context) => ChangeNotifierProvider.value( value: _limits, child: ListenableBuilder( listenable: _identity, builder: (context, _) { final p = _identity; final l = AppLocalizations.of(context)!; final t = Theme.of(context); return PopScope( canPop: !p.hasDraft && !p.busy, onPopInvokedWithResult: (didPop, _) { if (!didPop) _leave(account: true); }, child: Scaffold( body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 720), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 36), Semantics( header: true, child: Text( l.kycTitle, style: t.textTheme.displaySmall, ), ), const SizedBox(height: 12), Text(l.kycIntro, style: t.textTheme.bodyLarge), const SizedBox(height: 24), if (p.busy) ...[ const LinearProgressIndicator(), const SizedBox(height: 12), Semantics( liveRegion: true, child: Text( p.record == null ? l.kycLoading : l.kycWorking, ), ), const SizedBox(height: 16), ], if (p.error != null) ...[ Semantics( key: _feedbackKey, liveRegion: true, child: Text( errorText(l, p.error!), key: const ValueKey('kyc-error'), style: TextStyle(color: t.colorScheme.error), ), ), const SizedBox(height: 16), ], if (p.stale && p.record != null) Text( l.kycStatusStale, style: TextStyle(color: t.colorScheme.error), ), Focus( includeSemantics: false, key: _contentKey, focusNode: _focus, child: p.editing ? IdentityForm(provider: p) : p.record != null ? StatusPanel(provider: p) : const SizedBox.shrink(), ), if (!p.editing || p.stale) TextButton( onPressed: p.busy ? null : p.refresh, child: Text(l.kycRefresh), ), if (!p.editing) ...[ const SizedBox(height: 16), const LimitsPanel(), ], const SizedBox(height: 16), if (p.editing) TextButton( onPressed: p.busy ? null : _leave, child: Text(l.kycSkip), ), TextButton( onPressed: p.busy ? null : () => _leave(account: true), child: Text(l.wLimits), ), ], ), ), ), ), ), ), ); }, ), ); }