feat: Implement socket-first message sending with REST fallback, enhance chat room re-joining on socket reconnect, and update 'Categories' to 'Specialization' in the hero section.
This commit is contained in:
@@ -31,9 +31,9 @@ class _HeroSectionState extends ConsumerState<HeroSection> {
|
||||
String? _selectedLocation;
|
||||
String _selectedLocationName = 'Location';
|
||||
String? _selectedCategory;
|
||||
String _selectedCategoryName = 'Categories';
|
||||
String _selectedCategoryName = 'Specialization';
|
||||
|
||||
/// Cached filter fields fetched on-demand for Location/Categories pickers.
|
||||
/// Cached filter fields fetched on-demand for Location/Specialization pickers.
|
||||
List<FilterableField>? _cachedFilterFields;
|
||||
|
||||
@override
|
||||
@@ -371,22 +371,22 @@ class _HeroSectionState extends ConsumerState<HeroSection> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
// Categories — opens picker
|
||||
// Specialization — opens picker
|
||||
GestureDetector(
|
||||
onTap: () => _showFilterPicker(
|
||||
title: 'Select Category',
|
||||
title: 'Select Specialization',
|
||||
fieldSlug: 'specialization',
|
||||
currentValue: _selectedCategory,
|
||||
onSelect: (value, label) {
|
||||
setState(() {
|
||||
_selectedCategory = value;
|
||||
_selectedCategoryName = label ?? 'Categories';
|
||||
_selectedCategoryName = label ?? 'Specialization';
|
||||
});
|
||||
},
|
||||
onClear: () {
|
||||
setState(() {
|
||||
_selectedCategory = null;
|
||||
_selectedCategoryName = 'Categories';
|
||||
_selectedCategoryName = 'Specialization';
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@@ -291,7 +291,20 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
|
||||
);
|
||||
|
||||
try {
|
||||
final realMessage = await _repository.sendMessage(
|
||||
// Send via socket so the backend broadcasts new_message to the other user.
|
||||
// The REST endpoint does NOT emit socket events, so using REST here
|
||||
// would prevent real-time delivery to the other participant.
|
||||
ChatMessage? realMessage;
|
||||
|
||||
if (_socket.isConnected) {
|
||||
realMessage = await _socket.sendMessage(conversationId, {
|
||||
'content': content,
|
||||
'messageType': 'TEXT',
|
||||
});
|
||||
}
|
||||
|
||||
// Fallback to REST if socket send failed or not connected
|
||||
realMessage ??= await _repository.sendMessage(
|
||||
conversationId,
|
||||
content: content,
|
||||
);
|
||||
@@ -311,7 +324,7 @@ class MessagingNotifier extends StateNotifier<MessagingState> {
|
||||
final updatedConversations = state.conversations.map((c) {
|
||||
if (c.id == conversationId) {
|
||||
return c.copyWith(
|
||||
lastMessageText: realMessage.content,
|
||||
lastMessageText: realMessage!.content,
|
||||
lastMessageAt: realMessage.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
@@ -28,6 +30,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
|
||||
int _previousMessageCount = 0;
|
||||
|
||||
final SocketService _socket = SocketService();
|
||||
StreamSubscription<bool>? _connectionSub;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -37,17 +40,17 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
|
||||
notifier.setActiveConversation(widget.conversationId);
|
||||
notifier.loadMessages(widget.conversationId);
|
||||
notifier.markAsRead(widget.conversationId);
|
||||
// Join socket room for real-time updates (socket is connected at auth time)
|
||||
// Join socket room for real-time updates
|
||||
if (_socket.isConnected) {
|
||||
_socket.joinConversation(widget.conversationId);
|
||||
} else {
|
||||
// Wait for socket connection then join
|
||||
_socket.onConnectionChange.first.then((connected) {
|
||||
}
|
||||
// Listen for ALL connection changes (initial connect + reconnects)
|
||||
// so we re-join the room after any reconnection
|
||||
_connectionSub = _socket.onConnectionChange.listen((connected) {
|
||||
if (connected && mounted) {
|
||||
_socket.joinConversation(widget.conversationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
_scrollController.addListener(_onScroll);
|
||||
_messageController.addListener(_onTextChanged);
|
||||
@@ -55,6 +58,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_connectionSub?.cancel();
|
||||
// Stop typing and leave room before disposing
|
||||
if (_isComposing) {
|
||||
_socket.stopTyping(widget.conversationId);
|
||||
|
||||
Reference in New Issue
Block a user