Получение и отправка текста, статусы сообщения

This commit is contained in:
Халимов Рустам
2026-05-15 14:59:54 +03:00
parent 4d4bc8edd1
commit a426b63b7d
22 changed files with 3777 additions and 358 deletions
@@ -7,8 +7,14 @@ class SignalRService {
final SharedPreferences prefs;
HubConnection? _hubConnection;
final _messageController = StreamController<Message>.broadcast();
final _typingController = StreamController<Map<String, dynamic>>.broadcast();
final _statusController = StreamController<Map<String, dynamic>>.broadcast();
final _readReceiptController = StreamController<Map<String, dynamic>>.broadcast();
Stream<Message> get messages => _messageController.stream;
Stream<Map<String, dynamic>> get typingUpdates => _typingController.stream;
Stream<Map<String, dynamic>> get statusUpdates => _statusController.stream;
Stream<Map<String, dynamic>> get readReceipts => _readReceiptController.stream;
SignalRService(this.prefs);
@@ -54,13 +60,46 @@ class SignalRService {
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);
// Event names verified from backend source code (MessageNotifier.cs)
_hubConnection?.on('new_message', _handleReceiveMessage);
_hubConnection?.on('new_message_received', _handleReceiveMessage); // Optional fallback
_hubConnection?.on('user_typing', _handleUserTyping);
_hubConnection?.on('UserStoppedTyping', _handleUserStoppedTyping);
_hubConnection?.on('user_stopped_typing', _handleUserStoppedTyping);
_hubConnection?.on('user_online', (args) {
// ignore: avoid_print
print('[SignalR] user_online: $args');
if (args != null && args.isNotEmpty) {
final data = args[0] as Map<String, dynamic>;
_statusController.add({'userId': data['userId'] ?? data['UserId'], 'isOnline': true});
}
});
_hubConnection?.on('user_offline', (args) {
// ignore: avoid_print
print('[SignalR] user_offline: $args');
if (args != null && args.isNotEmpty) {
final data = args[0] as Map<String, dynamic>;
_statusController.add({
'userId': data['userId'] ?? data['UserId'],
'isOnline': false,
'lastSeen': data['lastSeen'] ?? data['LastSeen']
});
}
});
_hubConnection?.on('messages_read', (args) {
// ignore: avoid_print
print('[SignalR] messages_read: $args');
if (args != null && args.isNotEmpty) {
final data = args[0] as Map<String, dynamic>;
_readReceiptController.add({
'chatId': data['chatId'] ?? data['ChatId'],
'userId': data['userId'] ?? data['UserId'],
'lastReadMessageId': data['lastReadMessageId'] ?? data['LastReadMessageId'],
'lastReadSequenceId': data['lastReadSequenceId'] ?? data['LastReadSequenceId'],
});
}
});
try {
// ignore: avoid_print
@@ -74,8 +113,17 @@ class SignalRService {
}
}
final _typingController = StreamController<Map<String, dynamic>>.broadcast();
Stream<Map<String, dynamic>> get typingUpdates => _typingController.stream;
Future<void> markMessagesAsRead(String chatId, String lastReadMessageId, int lastReadSequenceId) async {
// ignore: avoid_print
print('[SignalR] markMessagesAsRead: chatId=$chatId, msgId=$lastReadMessageId, seqId=$lastReadSequenceId');
await _hubConnection?.invoke('read_messages', args: [
{
'ChatId': chatId,
'LastReadMessageId': lastReadMessageId,
'LastReadSequenceId': lastReadSequenceId,
}
]);
}
void _handleUserTyping(List<dynamic>? arguments) {
// ignore: avoid_print
@@ -109,15 +157,18 @@ class SignalRService {
if (arguments != null && arguments.isNotEmpty) {
final data = arguments[0] as Map<String, dynamic>;
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,
id: (data['id'] ?? data['Id'] ?? '').toString(),
chatId: (data['chatId'] ?? data['ChatId'] ?? '').toString(),
senderId: (data['senderId'] ?? data['SenderId'] ?? data['userId'] ?? data['UserId'] ?? '').toString(),
content: data['content'] ?? data['Content'] ?? '',
messageType: (data['type'] ?? data['Type'] ?? 'text').toString().toLowerCase(),
sequenceId: int.tryParse((data['sequenceId'] ?? data['SequenceId'] ?? '0').toString()) ?? 0,
createdAt: _parseDateTime(data['createdAt'] ?? data['CreatedAt']),
updatedAt: _parseDateTime(data['updatedAt'] ?? data['UpdatedAt']),
status: (data['isRead'] ?? data['IsRead'] ?? false) ? MessageStatus.read : MessageStatus.delivered,
media: (data['media'] ?? data['Media']) is Map<String, dynamic>
? (data['media'] ?? data['Media']) as Map<String, dynamic>
: null,
);
_messageController.add(message);
}