// food_overlay_bridge.dart — نافذة عرض التوصيل العائمة (أندرويد) ونتيجتها // // لماذا جسر مستقل عن كنترولر التوصيل: // العرض قد يصل والتطبيق في الخلفية وشاشة التوصيل مغلقة — بل وقد يصل من العزلة // الخلفية حيث لا وجود لأي كنترولر. فالاشتراك في نتيجة النافذة يجب أن يكون // عالمياً ويعيش طوال عمر التطبيق، ويردّ على الخادم بنفسه بلا انتظار شاشة. import 'dart:async'; import 'package:get/get.dart'; import 'package:trip_overlay_plugin/trip_overlay_plugin.dart'; import '../../print.dart'; import '../../views/food_delivery/food_task_details_page.dart'; import '../../views/widgets/error_snakbar.dart'; import 'food_delivery_models.dart'; import 'food_delivery_service.dart'; class FoodOverlayBridge { FoodOverlayBridge._(); static StreamSubscription? _acceptSub; static StreamSubscription? _rejectSub; /// تُنادى مرة واحدة عند إقلاع التطبيق static void attach() { if (!TripOverlayPlugin.isSupported) return; // أندرويد فقط if (_acceptSub != null) return; TripOverlayPlugin.initialize(); _acceptSub = TripOverlayPlugin.onFoodOrderAccepted.listen(_onAccepted); _rejectSub = TripOverlayPlugin.onFoodOrderRejected.listen(_onRejected); Log.print('🍔 [FoodOverlay] جاهز لاستقبال نتائج النافذة العائمة'); } static Future _onAccepted(String orderIdRaw) async { final orderId = int.tryParse(orderIdRaw) ?? 0; if (orderId == 0) return; Log.print('🍔 [FoodOverlay] قبول من النافذة — طلب #$orderId'); // القبول يتم هنا لا في الشاشة: النافذة تُغلق فوراً والخادم لا ينتظر // فتح واجهة. لو تأخّرنا حتى تُبنى الشاشة قد تنتهي مهلة العرض. final res = await FoodDeliveryService.respondToOffer(orderId, true); if (res.success) { mySnackbarSuccess('تم قبول طلب التوصيل'); Get.to(() => FoodTaskDetailsPage(orderId: orderId)); } else { mySnackbarWarning(res.message); } } static Future _onRejected(String orderIdRaw) async { final orderId = int.tryParse(orderIdRaw) ?? 0; if (orderId == 0) return; // الرفض الصريح يُبلَّغ الخادم ليعرض الطلب على سائق آخر فوراً بدل انتظار // انتهاء المهلة. انتهاء المهلة يصل بنفس الحدث، والخادم يتعامل مع الحالتين // بأمان (يرفض إن لم يعد العرض قائماً). Log.print('🍔 [FoodOverlay] رفض/انتهاء مهلة — طلب #$orderId'); await FoodDeliveryService.respondToOffer(orderId, false); } /// عرض النافذة لعرض توصيل وارد — تُنادى حين يكون التطبيق في الخلفية static Future show(FoodDeliveryOffer offer, {String? distanceText}) { return TripOverlayPlugin.showFoodOverlay( FoodOrderOverlayData( orderId: offer.orderId.toString(), merchantName: offer.merchantNameAr, merchantAddress: offer.merchantAddress ?? '', // التنسيق يتم هنا لا في Kotlin — العملة والوحدة الصغرى منطق تطبيق deliveryFeeText: foodFormatPrice(offer.deliveryFee), cashToCollectText: offer.cashToCollect > 0 ? foodFormatPrice(offer.cashToCollect) : '', distanceText: distanceText ?? '', itemsCount: offer.itemsCount, isCash: offer.isCash, ), autoCloseSeconds: offer.secondsRemaining.clamp(5, 20), ); } static Future hide() => TripOverlayPlugin.hideOverlay(); static void detach() { _acceptSub?.cancel(); _rejectSub?.cancel(); _acceptSub = null; _rejectSub = null; } }