flagged bugs fix

This commit is contained in:
Suriya
2026-07-23 11:11:25 +05:30
parent 842c2307d0
commit 6c7d656de5
11 changed files with 305 additions and 94 deletions

View File

@@ -34,32 +34,12 @@ class _LoginScreenState extends State<LoginScreen> {
await state.initiateLogin(_phone.text);
if (!mounted) return;
state.isLoginFlow = true;
// Bypass OTP for Easter Egg
if (_phone.text.replaceAll(' ', '') == '9876543210') {
setState(() => _loading = false);
Navigator.pushNamed(context, Routes.enterPin);
return;
}
await state.sendOtp(
_phone.text,
onSuccess: () {
if (!mounted) return;
setState(() => _loading = false);
Navigator.pushNamed(context, Routes.otp);
},
onError: (err) {
if (!mounted) return;
state.isLoginFlow = false;
setState(() => _loading = false);
SnackbarUtils.showError(context, err);
},
);
// Backend already gates login behind the PIN check, so no separate
// OTP possession-of-phone step is needed for returning users.
setState(() => _loading = false);
Navigator.pushNamed(context, Routes.enterPin);
} catch (e) {
if (!mounted) return;
state.isLoginFlow = false;
setState(() => _loading = false);
SnackbarUtils.showError(context, e.toString());
}

View File

@@ -1,8 +1,11 @@
import 'dart:async';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'package:doormile/main.dart';
import 'package:doormile/shared/state/app_state.dart';
@@ -18,7 +21,7 @@ class OtpScreen extends StatefulWidget {
State<OtpScreen> createState() => _OtpScreenState();
}
class _OtpScreenState extends State<OtpScreen> {
class _OtpScreenState extends State<OtpScreen> with CodeAutoFill {
final List<TextEditingController> _controllers =
List.generate(6, (_) => TextEditingController());
final List<FocusNode> _nodes = List.generate(6, (_) => FocusNode());
@@ -30,9 +33,40 @@ class _OtpScreenState extends State<OtpScreen> {
void initState() {
super.initState();
_startTimer();
listenForCode();
_prefillDebugBypass();
WidgetsBinding.instance.addPostFrameCallback((_) => _nodes[0].requestFocus());
}
/// Debug-only tester shortcut, see [AppState.verifyOtp]. Compiled out of
/// release builds via kDebugMode.
Future<void> _prefillDebugBypass() async {
if (!kDebugMode) return;
final prefs = await SharedPreferences.getInstance();
final provider = prefs.getInt('smsDefaultProvider') ?? 0;
final passKey = prefs.getInt('smsPassKey');
if (provider != 1 || passKey == null) return;
if (!mounted || _controllers.any((c) => c.text.isNotEmpty)) return;
final passKeyStr = passKey.toString().padLeft(6, '0');
for (int i = 0; i < 6; i++) {
_controllers[i].text = passKeyStr[i];
}
setState(() {});
}
@override
void codeUpdated() {
if (!mounted) return;
final digits = (code ?? '').replaceAll(RegExp(r'[^0-9]'), '');
if (digits.length < 6) return;
for (int i = 0; i < 6; i++) {
_controllers[i].text = digits[i];
}
setState(() {});
_verify();
}
void _startTimer() {
_seconds = 28;
_timer?.cancel();
@@ -59,8 +93,6 @@ class _OtpScreenState extends State<OtpScreen> {
final state = context.read<AppState>();
if (state.isResettingPin) {
Navigator.pushReplacementNamed(context, Routes.resetPin);
} else if (state.isLoginFlow) {
Navigator.pushReplacementNamed(context, Routes.enterPin);
} else {
Navigator.pushReplacementNamed(context, Routes.setPin);
}
@@ -115,6 +147,7 @@ class _OtpScreenState extends State<OtpScreen> {
@override
void dispose() {
_timer?.cancel();
cancel();
for (final c in _controllers) {
c.dispose();
}
@@ -137,8 +170,10 @@ class _OtpScreenState extends State<OtpScreen> {
child: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: AppCard(
padding: const EdgeInsets.all(28),
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: AppCard(
padding: const EdgeInsets.all(28),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
@@ -147,10 +182,10 @@ class _OtpScreenState extends State<OtpScreen> {
height: 88,
decoration: const BoxDecoration(
color: AppColors.primaryFixed, shape: BoxShape.circle),
child: const Icon(Icons.mark_email_read_rounded, size: 40, color: AppColors.primary),
child: const Icon(Icons.sms_rounded, size: 40, color: AppColors.primary),
),
const SizedBox(height: 24),
Text('Verify your email',
Text('Verify your number',
textAlign: TextAlign.center, style: AppTheme.headlineLg),
const SizedBox(height: 8),
Text.rich(
@@ -159,7 +194,7 @@ class _OtpScreenState extends State<OtpScreen> {
children: [
const TextSpan(text: 'We sent a 6-digit code to '),
TextSpan(
text: context.read<AppState>().pendingEmail ?? 'your email',
text: '+91 $phone',
style: AppTheme.labelMd,
),
],
@@ -168,12 +203,19 @@ class _OtpScreenState extends State<OtpScreen> {
),
const SizedBox(height: 28),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(6, (i) => _OtpBox(
controller: _controllers[i],
node: _nodes[i],
onChanged: (v) => _onChanged(i, v),
)),
children: List.generate(
6,
(i) => Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: _OtpBox(
controller: _controllers[i],
node: _nodes[i],
onChanged: (v) => _onChanged(i, v),
),
),
),
),
),
const SizedBox(height: 28),
if (_seconds > 0)
@@ -198,6 +240,7 @@ class _OtpScreenState extends State<OtpScreen> {
),
),
),
),
),
);
}
@@ -213,7 +256,6 @@ class _OtpBox extends StatelessWidget {
Widget build(BuildContext context) {
final filled = controller.text.isNotEmpty;
return SizedBox(
width: 46,
height: 56,
child: TextField(
controller: controller,