Update codebase

This commit is contained in:
Hamza-Ayed
2026-08-09 16:56:13 +03:00
parent 95e2e4f35d
commit b64debaa88
1058 changed files with 164327 additions and 113928 deletions
@@ -28,7 +28,32 @@ class OrderData {
final double? driverEarningsExtra;
final String? driverEarningsCurrency;
/// نوع الطلب: 'exclusive' خلال السبق الحصري، 'public' بعده، و null في
/// وضع البثّ الحر (عندها لا تُعرض شارة ويعمل المؤقّت بمدته العادية).
final String? offerType;
/// ثواني السبق الحصري كما حددها الخادم (DISPATCH_HEAD_START_SECONDS).
final int offerExpiresIn;
/// رحلة محجوزة مسبقاً — موعد مضبوط لا طلب فوري.
final bool isScheduled;
/// موعد الانطلاق كما حجزه الراكب. فارغ في الرحلات العادية.
final String scheduledAt;
/// طلب وصل عبر رسالة نصية — الراكب بلا إنترنت.
///
/// ليست تفصيلاً تجميلياً: هذا الراكب لن يصله شات ولا إشعار ولن يرى
/// السائق على الخريطة. الاتصال الهاتفي هو القناة الوحيدة، والسائق
/// الذي لا يعرف ذلك سينتظر رداً لن يأتي ثم يُلغي.
final bool isSmsRide;
OrderData({
this.offerType,
this.offerExpiresIn = 0,
this.isScheduled = false,
this.scheduledAt = '',
this.isSmsRide = false,
required this.customerName,
required this.customerToken,
required this.tripDistanceKm,
@@ -135,9 +160,95 @@ 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,
// ‏الفهرسان 39/40 — يضيفهما add_ride.php للرحلات المحجوزة.
isScheduled: list.length > 39 &&
(list[39].toString() == '1' || list[39].toString() == 'true'),
scheduledAt: list.length > 40 ? list[40].toString() : '',
// ‏الفهرس 41 — طلب بالرسائل النصية.
isSmsRide: list.length > 41 &&
(list[41].toString() == '1' || list[41].toString() == 'true'),
);
}
/// بناء من حمولة السوكِت (خريطة). أسماء الحقول تطابق 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,
isScheduled: str('is_scheduled') == '1' || str('is_scheduled') == 'true',
scheduledAt: str('scheduled_at'),
isSmsRide: str('is_sms') == '1' || str('is_sms') == 'true',
);
}
/// الموحِّد: 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':