Update: 2026-08-07 01:53:09

This commit is contained in:
Hamza-Ayed
2026-08-07 01:53:09 +03:00
parent 5b864e30df
commit 9830e5f649
7 changed files with 289 additions and 26 deletions
@@ -28,7 +28,16 @@ class OrderData {
final double? driverEarningsExtra;
final String? driverEarningsCurrency;
/// نوع الطلب: 'exclusive' خلال السبق الحصري، 'public' بعده، و null في
/// وضع البثّ الحر (عندها لا تُعرض شارة ويعمل المؤقّت بمدته العادية).
final String? offerType;
/// ثواني السبق الحصري كما حددها الخادم (DISPATCH_HEAD_START_SECONDS).
final int offerExpiresIn;
OrderData({
this.offerType,
this.offerExpiresIn = 0,
required this.customerName,
required this.customerToken,
required this.tripDistanceKm,
@@ -135,9 +144,83 @@ class OrderData {
list.length > 36 && list[36].toString().isNotEmpty
? list[36].toString()
: null,
// ‏مسار FCM لا يحمل هذين اليوم — يبقيان محايدَين فلا تظهر شارة.
offerType: list.length > 37 && list[37].toString().isNotEmpty
? list[37].toString()
: null,
offerExpiresIn:
list.length > 38 ? (int.tryParse(list[38].toString()) ?? 0) : 0,
);
}
/// بناء من حمولة السوكِت (خريطة). أسماء الحقول تطابق buildMarketPayload()
/// في backend/ride/rides/add_ride.php — أي تغيير هناك يجب أن ينعكس هنا.
///
/// ملاحظة: حمولة السوق لا تحمل اسم الراكب ولا توكنه عمداً (تصل سائقين لن
/// يقودوا الرحلة)، فتبقى القيم الافتراضية حتى يُقبل الطلب.
factory OrderData.fromSocketMap(Map<dynamic, dynamic> map) {
String str(String key, [String fallback = '']) =>
map[key]?.toString().isNotEmpty == true ? map[key].toString() : fallback;
final distanceToPassengerMeters =
double.tryParse(str('distanceToPassenger', '0')) ?? 0.0;
return OrderData(
customerName: str('passengerName', 'Unknown Customer'),
customerToken: str('passengerToken', 'Unknown token'),
tripDistanceKm: double.tryParse(str('distance', '0')) ?? 0.0,
price: str('price', '0').split('.')[0],
startLocationAddress: str('startName', 'Unknown Address'),
endLocationAddress: str('endName', 'Unknown Address'),
distanceToPassengerKm: distanceToPassengerMeters / 1000.0,
tripDurationMinutes: _secondsToRoundedUpMinutes(str('duration', '0')),
durationToPassengerMinutes:
_secondsToRoundedUpMinutes(str('durationToPassenger', '0')),
rideType: _getRideType(str('carType', 'Unknown')),
orderId: str('id', 'N/A'),
passengerId: str('passengerId', 'N/A'),
passengerRate: str('passengerRate', 'N/A'),
rawStartCoordinates: str('start_location').isNotEmpty
? str('start_location')
: (str('start_lat').isNotEmpty
? '${str('start_lat')},${str('start_lng')}'
: null),
rawEndCoordinates: str('end_location').isNotEmpty
? str('end_location')
: (str('end_lat').isNotEmpty
? '${str('end_lat')},${str('end_lng')}'
: null),
driverEarningsExtra: double.tryParse(str('driver_earnings_extra')),
driverEarningsCurrency: str('driver_earnings_currency').isNotEmpty
? str('driver_earnings_currency')
: null,
offerType: str('offer_type').isNotEmpty ? str('offer_type') : null,
offerExpiresIn: int.tryParse(str('offer_expires_in', '0')) ?? 0,
);
}
/// الموحِّد: FCM يصل قائمةً مفهرسة، والسوكِت يصل خريطة JSON. النافذة
/// الواحدة تخدم المسارين، فالتمييز يتم هنا لا في كل مستدعٍ.
/// يرجع null على أي شكل غير معروف بدل أن يرمي — النافذة تبقى صامتة
/// بدل أن تنهار على حمولة غريبة.
static OrderData? fromAny(dynamic event) {
if (event is List) return OrderData.fromList(event);
if (event is Map) {
// ‏خريطتان مختلفتان تصلان هنا: حمولة السوكِت (أسماء الخادم: id،
// ‏carType، startName) وخريطة toMap() الداخلية (orderId، rideType).
// ‏نميّز بالمفتاح لا بالنوع، وإلا قرأنا إحداهما بقواعد الأخرى فخرجت
// ‏كل الحقول فارغة بصمت.
if (event.containsKey('orderId') || event.containsKey('rideType')) {
return OrderData.fromMap(Map<String, dynamic>.from(event));
}
return OrderData.fromSocketMap(event);
}
return null;
}
static String _getRideType(String type) {
switch (type) {
case 'Comfort':