Files
tripz-llc/apps/driver_new/lib/features/auth/cubit/login_state.dart
T
Hamza-AyedandClaude Opus 5 e22cc5c7e4 feat(apps): ثيم سيرو ومفتاح الدولة والعراق
## الثيم مقرَّب من سيرو (قرار المالك 2026-08-05)
- البذرة صارت كحليّ سيرو #1A2340 بدل الأزرق العام، والثانوي #8C9CF8
- fromSeed وحدها تنتج من بذرة داكنة أسطحاً باهتة، فثُبّت الأساسي والثانوي
  والأسطح صراحةً وتُرك الباقي للمشتقّ — شكل سيرو مع تباين M3
- الأدوار الدلالية بأرقام سيرو الميدانية: نجاح #22C55E · تحذير #F59E0B ·
  خطأ #EF4444، وبطاقة الليلي #2C2C2E

## مفتاح الدولة عاد
core/ui/phone_field.dart — النمط المجرَّب في سيرو، لكن بلا intl_phone_field:
تلك الحزمة تحمل 240 دولة ونحتاج أربعاً. CountryCode صريح ومقصور على أسواقنا.
- الاختيار من ورقة سفلية لا قائمة منسدلة: أهداف لمس أكبر وأربع دول لا مئتان
- الترتيب يبدأ بحزمة دولة المستأجر — الأردنيّ يرى الأردن أولاً بلا بحث
- الرقم يُرسل كاملاً بالمفتاح بلا + (962790000001) كما يقبله الخادم
- التحقّق صار بطول الدولة لا برقم ثابت: jo/sy تسع خانات، eg/iq عشر

## العراق
- CountryCode.iraq (+964) في قائمة الهاتف
- قنوات دفعه: زين كاش · كي كارد · نقد
⚠️ الباك إند لا يعرف حزمة `iq`: default-tariffs.ts فيه jo/sy/eg فقط، فمستأجر
عراقي يُزوَّد بلا أي تعرفة. موثَّق في الكود — وهو شغل خادم يسبق أي إطلاق عراقي.

flutter analyze نظيف في التطبيقين.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 01:08:18 +03:00

100 lines
3.1 KiB
Dart

import 'package:equatable/equatable.dart';
import '../../../core/api/api_failure.dart';
import '../../../core/payments/country_codes.dart';
import '../../../core/session/app_user.dart';
import 'phone_error.dart';
/// خطوات الدخول بالترتيب الميداني المثبت في سيرو (docs/39 §2): بوابتان
/// كاملتان **قبل** أي حقل إدخال، ثم الهاتف فالرمز.
enum LoginStep { agreement, permission, phone, otp, done }
enum LoginStatus { idle, submitting, failure, success }
class LoginState extends Equatable {
const LoginState({
this.step = LoginStep.agreement,
this.status = LoginStatus.idle,
this.phone = '',
this.country = CountryCode.jordan,
this.agreed = false,
this.failure,
this.phoneError,
this.resendIn = 0,
this.permissionPermanentlyDenied = false,
this.user,
});
final LoginStep step;
final LoginStatus status;
/// الرقم كما أدخله المستخدم — الخادم يطبّعه، فلا نطبّعه نحن (docs/38 §2).
final String phone;
/// الدولة المختارة — تحدّد المفتاح الدولي وطول التحقّق. تُبدأ من
/// `countryPack` المستأجر لا من لغة الجهاز.
final CountryCode country;
/// ما يُرسل للخادم: المفتاح + الرقم بلا `+` (`962790000001`).
String get e164 => '${country.dial}${phone.trim()}';
final bool agreed;
final ApiFailure? failure;
final PhoneError? phoneError;
/// ثوانٍ متبقّية قبل السماح بإعادة الإرسال. الخادم يحدّ بثلاثة طلبات كل
/// خمس دقائق، فالعدّاد ليس تجميلاً (docs/39 §2).
final int resendIn;
/// رُفض الإذن نهائياً — الحل الوحيد فتح إعدادات النظام.
final bool permissionPermanentlyDenied;
final AppUser? user;
bool get isSubmitting => status == LoginStatus.submitting;
bool get canResend => resendIn == 0 && !isSubmitting;
LoginState copyWith({
LoginStep? step,
LoginStatus? status,
String? phone,
CountryCode? country,
bool? agreed,
ApiFailure? failure,
PhoneError? phoneError,
int? resendIn,
bool? permissionPermanentlyDenied,
AppUser? user,
bool clearFailure = false,
bool clearPhoneError = false,
}) {
return LoginState(
step: step ?? this.step,
status: status ?? this.status,
phone: phone ?? this.phone,
country: country ?? this.country,
agreed: agreed ?? this.agreed,
failure: clearFailure ? null : (failure ?? this.failure),
phoneError: clearPhoneError ? null : (phoneError ?? this.phoneError),
resendIn: resendIn ?? this.resendIn,
permissionPermanentlyDenied:
permissionPermanentlyDenied ?? this.permissionPermanentlyDenied,
user: user ?? this.user,
);
}
@override
List<Object?> get props => [
step,
status,
phone,
country,
agreed,
failure,
phoneError,
resendIn,
permissionPermanentlyDenied,
user,
];
}