Разделение навигации и реактивности

This commit is contained in:
Халимов Рустам
2026-05-15 11:00:38 +03:00
parent 40589dbb75
commit 4d4bc8edd1
9 changed files with 541 additions and 96 deletions
@@ -13,6 +13,13 @@ class SignalRService {
SignalRService(this.prefs);
Future<void> init() async {
if (_hubConnection?.state == HubConnectionState.Connected ||
_hubConnection?.state == HubConnectionState.Connecting) {
// ignore: avoid_print
print('[SignalR] Connection already active or connecting. Skipping init.');
return;
}
if (_hubConnection != null) {
await stop();
}
@@ -22,27 +29,48 @@ class SignalRService {
if (token == null) return;
final hubUrl = '${apiUrl.replaceAll(RegExp(r'/+$'), '')}/hubs/chat';
// ignore: avoid_print
print('[SignalR] Initializing connection to: $hubUrl');
_hubConnection = HubConnectionBuilder()
.withUrl(hubUrl, options: HttpConnectionOptions(
accessTokenFactory: () async => token,
// logging: (level, message) => print('[SignalR] $level: $message'),
))
.withAutomaticReconnect()
.build();
_hubConnection?.onclose(({error}) {
// print('[SignalR] Connection closed: $error');
// ignore: avoid_print
print('[SignalR] Connection closed. Error: $error');
});
_hubConnection?.onreconnecting(({error}) {
// ignore: avoid_print
print('[SignalR] Reconnecting... Error: $error');
});
_hubConnection?.onreconnected(({connectionId}) {
// ignore: avoid_print
print('[SignalR] Reconnected! ID: $connectionId');
});
// Handle multiple possible event names to be safe
_hubConnection?.on('ReceiveMessage', _handleReceiveMessage);
_hubConnection?.on('receiveMessage', _handleReceiveMessage);
_hubConnection?.on('UserTyping', _handleUserTyping);
_hubConnection?.on('user_typing', _handleUserTyping);
_hubConnection?.on('UserStoppedTyping', _handleUserStoppedTyping);
_hubConnection?.on('user_stopped_typing', _handleUserStoppedTyping);
try {
// ignore: avoid_print
print('[SignalR] Starting connection...');
await _hubConnection?.start();
// ignore: avoid_print
print('[SignalR] Connection started successfully. State: ${_hubConnection?.state}');
} catch (e) {
// ignore
// ignore: avoid_print
print('[SignalR] Connection failed: $e');
}
}
@@ -50,6 +78,8 @@ class SignalRService {
Stream<Map<String, dynamic>> get typingUpdates => _typingController.stream;
void _handleUserTyping(List<dynamic>? arguments) {
// ignore: avoid_print
print('[SignalR] _handleUserTyping: $arguments');
if (arguments != null && arguments.isNotEmpty) {
final data = arguments[0] as Map<String, dynamic>;
_typingController.add({'chatId': data['ChatId'] ?? data['chatId'], 'userId': data['UserId'] ?? data['userId'], 'isTyping': true});
@@ -57,6 +87,8 @@ class SignalRService {
}
void _handleUserStoppedTyping(List<dynamic>? arguments) {
// ignore: avoid_print
print('[SignalR] _handleUserStoppedTyping: $arguments');
if (arguments != null && arguments.isNotEmpty) {
final data = arguments[0] as Map<String, dynamic>;
_typingController.add({'chatId': data['ChatId'] ?? data['chatId'], 'userId': data['UserId'] ?? data['userId'], 'isTyping': false});
@@ -72,13 +104,34 @@ class SignalRService {
}
void _handleReceiveMessage(List<dynamic>? arguments) {
// ignore: avoid_print
print('[SignalR] _handleReceiveMessage: $arguments');
if (arguments != null && arguments.isNotEmpty) {
final data = arguments[0] as Map<String, dynamic>;
// Map JSON to Message entity and add to stream
// I'll need a Message.fromJson mapper
final message = Message(
id: data['id'] ?? '',
chatId: data['chatId'] ?? '',
senderId: data['userId'] ?? (data['senderId'] ?? ''),
content: data['content'] ?? '',
messageType: data['type']?.toString().toLowerCase() ?? 'text',
createdAt: _parseDateTime(data['createdAt']),
updatedAt: _parseDateTime(data['updatedAt']),
isRead: data['isRead'] ?? false,
media: data['media'] is Map<String, dynamic> ? data['media'] as Map<String, dynamic> : null,
);
_messageController.add(message);
}
}
DateTime? _parseDateTime(String? dateStr) {
if (dateStr == null) return null;
String normalized = dateStr;
if (!normalized.endsWith('Z') && !normalized.contains('+')) {
normalized += 'Z';
}
return DateTime.parse(normalized).toLocal();
}
Future<void> stop() async {
await _hubConnection?.stop();
_hubConnection = null;