import 'dart:async'; /// تحديث حالة من أي مصدر (WebSocket أو FCM). class TripUpdate { final String tripId; final String status; final String? driverId; final Map extra; const TripUpdate({ required this.tripId, required this.status, this.driverId, this.extra = const {}, }); } /// موقع سائق من WebSocket. class DriverLocationUpdate { final String tripId; final double lat; final double lng; final double heading; final double speed; const DriverLocationUpdate({ required this.tripId, required this.lat, required this.lng, this.heading = 0, this.speed = 0, }); } /// ترتيب الحالات — يُستخدم للتصفية الصامتة (docs/23 §15). int statusOrder(String s) { switch (s) { case 'searching': return 0; case 'assigned': return 1; case 'driver_arriving': return 2; case 'driver_arrived': return 3; case 'in_progress': return 4; case 'completed': return 5; case 'paid': return 6; default: return -1; } } /// محول أحداث موحّد — يستقبل من WebSocket وFCM ويُخرج أولاً-فوزان. /// /// التصفية الصامتة: أي حدث بحالة ≤ الحالة المعروضة يُهمل. /// لا حاجة لمعرّفات رسالة — ترتيب الحالات يكفي (docs/23 §15). class TripEventMerger { final _updateController = StreamController.broadcast(); final _locationController = StreamController.broadcast(); /// الحالة المعروضة حالياً لكل رحلة — للتصفية الصامتة. final Map _currentOrder = {}; Stream get updateStream => _updateController.stream; Stream get locationStream => _locationController.stream; /// استقبال تحديث حالة من أي مصدر. void addUpdate(TripUpdate update) { final newOrder = statusOrder(update.status); final currentOrder = _currentOrder[update.tripId] ?? -1; // التصفية الصامتة: تجاهل الأحداث القديمة أو المتطابقة. if (newOrder >= 0 && newOrder <= currentOrder) return; // تحديث الحالة المعروضة. if (newOrder >= 0) { _currentOrder[update.tripId] = newOrder; } _updateController.add(update); } /// استقبال موقع سائق (لا يحتاج تصفية — كل ping مفيد). void addLocation(DriverLocationUpdate location) { _locationController.add(location); } void dispose() { _updateController.close(); _locationController.close(); } }