import "dart:async"; import "package:bus_running_record/pages/auth/page.dart"; import "package:bus_running_record/provider/supabase_state.dart"; import "package:go_router/go_router.dart"; import "package:provider/provider.dart"; import "package:shadcn_flutter/shadcn_flutter.dart"; class VerifyEmailPage extends StatefulWidget { static const String routePath = "/verify-email"; static final GoRoute route = GoRoute( path: routePath, builder: (context, state) { final email = state.uri.queryParameters["email"] ?? ""; return VerifyEmailPage(email: email); }, ); const VerifyEmailPage({required this.email, super.key}); final String email; @override State createState() => _VerifyEmailPageState(); } class _VerifyEmailPageState extends State { static const int _tokenLength = 6; String _token = ""; bool _isSubmitting = false; String? _error; Future _verifyToken(String token) async { if (widget.email.isEmpty) { setState(() => _error = "Missing email address for verification."); return; } if (token.length != _tokenLength) { setState(() => _error = "Enter the $_tokenLength-digit token from your email."); return; } setState(() { _isSubmitting = true; _error = null; }); try { await context.read().verifySignUpOtp( email: widget.email, token: token, ); if (mounted) context.go("/"); } catch (error, stackTrace) { debugPrint( "[VerifyEmailPage] verifyToken failed (${error.runtimeType}): $error", ); debugPrintStack(stackTrace: stackTrace); if (mounted) { setState(() { _error = error.toString(); }); } } finally { if (mounted) { setState(() => _isSubmitting = false); } } } @override Widget build(BuildContext context) { final topPadding = MediaQuery.of(context).padding.top; return Scaffold( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 460), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Gap(topPadding + 16), Text("You're almost there!").x2Large.semiBold, const Gap(8), Text( "We sent a $_tokenLength-digit token to your email.\n(${widget.email})", ).xSmall.muted.textCenter, const Gap(14), InputOTP( onChanged: (value) { setState(() { _token = value.otpToString(); _error = null; }); }, onSubmitted: (value) { FocusScope.of(context).unfocus(); unawaited(_verifyToken(value.otpToString())); }, children: [ InputOTPChild.character(allowDigit: true), InputOTPChild.character(allowDigit: true), InputOTPChild.character(allowDigit: true), InputOTPChild.character(allowDigit: true), InputOTPChild.character(allowDigit: true), InputOTPChild.character(allowDigit: true), ], ), const Gap(20), Button.primary( enabled: !_isSubmitting && _token.length == _tokenLength, onPressed: () => unawaited(_verifyToken(_token)), alignment: Alignment.center, child: Text( _isSubmitting ? "Verifying..." : "Verify token", ), ), const Gap(8), Button.text( onPressed: _isSubmitting ? null : () => context.go(LoginPage.routePath), child: const Text("Back to sign in"), ), if (_error != null) ...[ const Gap(8), Text( _error!, style: TextStyle( color: Theme.of(context).colorScheme.destructive, ), ).xSmall.textCenter, ], ], ), ), ), ), ); } }