// // socket_service.dart // import 'dart:async'; // import 'dart:convert'; // import 'dart:developer'; // import 'package:wanisdriver/Provider/utils_singleton.dart'; // import 'package:flutter/material.dart'; // import 'package:stomp_dart_client/stomp.dart'; // import 'package:stomp_dart_client/stomp_config.dart'; // import 'package:stomp_dart_client/stomp_frame.dart'; // import '../config/constants/app_constant.dart'; // // typedef RideUpdateCallback = void Function(Map data); // // class SocketService { // final String jwtToken; // final RideUpdateCallback onRideUpdate; // // // ───────────────────────── state ───────────────────────── // late StompClient _stompClient; // bool _connected = false; // bool _manualDisconnect = false; // final List<_PendingMessage> _pending = []; // int _reconnectAttempt = 0; // Timer? _reconnectTimer; // // // ───────────────────────── life-cycle ───────────────────────── // SocketService({ // required this.jwtToken, // required this.onRideUpdate, // }); // // void connect({VoidCallback? onConnected}) { // _manualDisconnect = false; // // _stompClient = StompClient( // config: StompConfig( // url: socketUrlNative, // webSocketConnectHeaders: {'Authorization': 'Bearer $jwtToken'}, // stompConnectHeaders: {'Authorization': 'Bearer $jwtToken'}, // // // ✨ THE FIX IS HERE ✨ // onConnect: (frame) { // log('🟢 DRIVER STOMP connected'); // _connected = true; // _reconnectAttempt = 0; // _reconnectTimer?.cancel(); // _flushPending(); // // // Add a very short delay to prevent a race condition in the STOMP library. // // This gives the client time to stabilize before we send subscribe commands. // Future.delayed(const Duration(milliseconds: 100), () { // if (_connected) { // // Check if still connected // _subscribeToDestinations(); // onConnected?.call(); // } // }); // }, // // onWebSocketError: (error) { // log('🔴 DRIVER WebSocket error: ${error ?? "unknown"}'); // _scheduleReconnect(); // }, // onStompError: (frame) { // log('🔴 DRIVER STOMP error: ${frame.body ?? frame.headers}'); // _scheduleReconnect(); // }, // onWebSocketDone: () { // log('🔴 DRIVER WebSocket closed by server'); // _connected = false; // _scheduleReconnect(); // }, // heartbeatIncoming: const Duration(seconds: 10), // heartbeatOutgoing: const Duration(seconds: 10), // ), // ); // _stompClient.activate(); // } // // void disconnect() { // log('⚪️ DRIVER Socket service disconnecting manually.'); // _manualDisconnect = true; // _reconnectTimer?.cancel(); // if (_stompClient.connected) { // _stompClient.deactivate(); // } // _connected = false; // _pending.clear(); // } // // // ───────────────────────── public send helpers ───────────────────────── // void sendAcceptRide({required int rideId}) { // _sendOrQueue( // '/app/accept_ride', // json.encode({ // 'rideId': rideId, // 'accepted': true, // 'driverid': utils.user?.driver.id // })); // } // // void sendDeclineRide({required int rideId}) { // _sendOrQueue( // '/app/accept_ride', // json.encode({ // 'rideId': rideId, // 'accepted': false, // 'driverid': utils.user?.driver.id // })); // } // // // ───────────────────────── internals ───────────────────────── // // ✨ Cleaned up for clarity - these are the correct destinations // void _subscribeToDestinations() { // log('Subscribing to driver destinations...'); // // // 1. Subscribe to PUBLIC broadcasts for new, unassigned ride offers. // _stompClient.subscribe( // destination: '/topic/ride-offers', // callback: _handleFrame, // headers: {'id': 'sub-offers'}); // // // 2. Subscribe to PRIVATE queues for messages sent directly to this driver. // _stompClient.subscribe( // destination: '/user/queue/driver', // callback: _handleFrame, // headers: {'id': 'sub-driver-private'}); // _stompClient.subscribe( // destination: '/user/queue/ride_updates', // callback: _handleFrame, // headers: {'id': 'sub-ride-updates-private'}); // } // // void _handleFrame(StompFrame frame) { // if (frame.body == null) return; // // log('📨 DRIVER Received message on ${frame.headers['destination']}: ${frame.body}'); // // try { // final raw = json.decode(frame.body!); // final data = raw is Map // ? raw // : {'type': 'MESSAGE', 'message': raw.toString()}; // onRideUpdate(data); // } catch (e, st) { // log('🚨 DRIVER Message parse error: $e\n$st'); // } // } // // void _sendOrQueue(String destination, String body, // {Map? headers}) { // headers ??= {}; // headers.putIfAbsent('content-type', () => 'application/json'); // if (_connected) { // try { // _stompClient.send( // destination: destination, // body: body, // headers: headers, // ); // } catch (e) { // log('🔴 DRIVER Send failed – queueing: $e'); // _pending.add(_PendingMessage( // destination: destination, body: body, headers: headers)); // } // } else { // log('🟡 DRIVER Connection unavailable – queueing message for $destination'); // _pending.add(_PendingMessage( // destination: destination, body: body, headers: headers)); // } // } // // void _flushPending() { // if (_pending.isEmpty) return; // log('📦 DRIVER Flushing ${_pending.length} pending messages...'); // for (var m in _pending) { // _sendOrQueue(m.destination, m.body, headers: m.headers); // } // _pending.clear(); // } // // void _scheduleReconnect() { // if (_manualDisconnect || (_reconnectTimer?.isActive ?? false)) return; // // _connected = false; // final delayMs = (1000 * (1 << _reconnectAttempt.clamp(0, 5))); // _reconnectAttempt++; // log('🔄 DRIVER Scheduling reconnect in ${delayMs ~/ 1000}s'); // // _reconnectTimer = Timer(Duration(milliseconds: delayMs), () { // if (!_manualDisconnect && !_stompClient.connected) { // log('⏳ DRIVER Attempting to reconnect...'); // _stompClient.activate(); // } // }); // } // } // // // ───────────────────────── model ───────────────────────── // class _PendingMessage { // final String destination; // final String body; // final Map? headers; // // _PendingMessage({ // required this.destination, // required this.body, // this.headers, // }); // }