import 'dart:async'; import 'dart:io' show Platform; import 'package:firebase_messaging/firebase_messaging.dart'; import '../../../core/api/api_client.dart'; import '../../../core/di.dart'; import 'trip_event_merger.dart'; /// القناة الثانية للزمن الحقيقي — FCM data-message (docs/23 §15). /// /// تستقبل رسائل data-only من السيرفر حول تحديثات الرحلة وتُمرّرها /// إلى TripEventMerger بنفس شكل WebSocket — أيّهما يسبق يفوز، /// والـ merger يتكفّل بالتصفية الصامتة. class FcmService { final TripEventMerger _merger; StreamSubscription? _onMessageSub; StreamSubscription? _onOpenSub; FcmService(this._merger); /// تهيئة FCM بعد تسجيل الدخول — تُستدعى من نقطة واحدة. Future init() async { final messaging = FirebaseMessaging.instance; await messaging.requestPermission( alert: false, badge: false, sound: false, provisional: false, ); final token = await messaging.getToken(); if (token != null) { _registerToken(token); } messaging.onTokenRefresh.listen(_registerToken); _onMessageSub = FirebaseMessaging.onMessage.listen(_handleMessage); _onOpenSub = FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage); } void dispose() { _onMessageSub?.cancel(); _onOpenSub?.cancel(); } void _handleMessage(RemoteMessage message) { final data = message.data; final type = data['type'] as String?; final tripId = data['tripId'] as String?; if (type == null || tripId == null || tripId.isEmpty) return; final status = type.startsWith('trip_') ? type.substring(5) : null; if (status == null) return; final extra = Map.from(data) ..remove('type') ..remove('tripId') ..remove('title') ..remove('body'); _merger.addUpdate(TripUpdate( tripId: tripId, status: status, driverId: data['driverId'] as String?, extra: extra, )); } static Future _registerToken(String token) async { try { final api = getIt(); await api.dio.post('/notifications/token', data: { 'token': token, 'platform': Platform.isIOS ? 'ios' : 'android', }); } catch (_) {} } }