import 'dart:async'; import 'dart:convert'; import 'package:chepuhagram/domain/services/api_service.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/status.dart' as status; import 'package:chepuhagram/core/constants.dart'; class SocketService { static final SocketService _instance = SocketService._internal(); factory SocketService() { return _instance; } SocketService._internal(); WebSocketChannel? _channel; final StreamController> _messageController = StreamController>.broadcast(); // Поток, который будут слушать провайдеры Stream> get messages => _messageController.stream; Future connect(ApiService apiService) async { final token = await apiService.getAccessToken(); if (_channel != null) return; // Уже подключены if (token == null || token.isEmpty) { print('❌ SocketService.connect: no access token, skipping connect'); return; } // В FastAPI эндпоинт обычно ожидает токен в URL или подзаголовке final uri = Uri.parse("ws://${AppConstants.baseUrl.split('//')[1]}/ws?token=$token"); _channel = WebSocketChannel.connect(uri); _channel!.stream.listen( (data) { final decoded = jsonDecode(data); print("🚀 СООБЩЕНИЕ ПОЛУЧЕНО ИЗ SINK: $decoded"); _messageController.add(decoded); }, onError: (error) => _reconnect(apiService), onDone: () => _reconnect(apiService), ); } Future _reconnect(ApiService apiService) async { _channel = null; Future.delayed(const Duration(seconds: 5), () => connect(apiService)); } bool sendMessage(Map data, {int retryCnt = 0}) { if (_channel == null) { //print("❌ ОШИБКА: Попытка отправить сообщение через NULL канал."); sendMessage(data, retryCnt: retryCnt + 1); return false; } try { final encodedData = jsonEncode(data); // 1. Проверяем, не закрыт ли sink (у некоторых провайдеров это доступно) _channel!.sink.add(encodedData); // 2. Добавляем принт подтверждения print("🚀 СООБЩЕНИЕ ОТПРАВЛЕНО В SINK: $encodedData"); return true; } catch (e) { print("❌ КРИТИЧЕСКАЯ ОШИБКА ПРИ ОТПРАВКЕ: $e"); return false; } } bool sendReadReceipt(int messageId) { return sendMessage({ 'type': 'read_receipt', 'message_id': messageId, }); } void disconnect() { _channel?.sink.close(status.goingAway); _channel = null; } }