diff --git a/lib/features/about/presentation/screens/about_screen.dart b/lib/features/about/presentation/screens/about_screen.dart index 871b92a..db7369a 100644 --- a/lib/features/about/presentation/screens/about_screen.dart +++ b/lib/features/about/presentation/screens/about_screen.dart @@ -117,10 +117,8 @@ class _AboutCta { // ── Defaults ── -const _defaultStats = [ - _AboutStatItem(value: '15k+', label: 'Verified Agents'), - _AboutStatItem(value: '98%', label: 'Customer Satisfaction'), -]; +// Empty defaults — only CMS-provided content should render (matches web behavior) +const _defaultStats = <_AboutStatItem>[]; const _defaultFeatures = [ _AboutFeatureItem( diff --git a/lib/features/agents/presentation/screens/agent_edit_profile_screen.dart b/lib/features/agents/presentation/screens/agent_edit_profile_screen.dart index b74d253..bbac60b 100644 --- a/lib/features/agents/presentation/screens/agent_edit_profile_screen.dart +++ b/lib/features/agents/presentation/screens/agent_edit_profile_screen.dart @@ -1651,12 +1651,13 @@ class _AgentEditProfileScreenState await showModalBottomSheet( context: context, + useRootNavigator: true, isScrollControlled: true, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), - builder: (ctx) { + builder: (sheetCtx) { return StatefulBuilder( builder: (ctx, setSheetState) { final filtered = options.where((o) { @@ -1754,7 +1755,7 @@ class _AgentEditProfileScreenState Expanded( child: OutlinedButton( onPressed: () => - Navigator.pop(ctx), + Navigator.of(ctx).pop(), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric( @@ -1772,7 +1773,7 @@ class _AgentEditProfileScreenState child: ElevatedButton( onPressed: () { onChanged(workingSelected); - Navigator.pop(ctx); + Navigator.of(ctx).pop(); }, style: ElevatedButton.styleFrom( backgroundColor: diff --git a/lib/features/messaging/presentation/providers/messaging_provider.dart b/lib/features/messaging/presentation/providers/messaging_provider.dart index 632afa8..f415f75 100644 --- a/lib/features/messaging/presentation/providers/messaging_provider.dart +++ b/lib/features/messaging/presentation/providers/messaging_provider.dart @@ -585,6 +585,24 @@ class MessagingNotifier extends StateNotifier { } } + /// Refetch a single conversation — used when opening chat to get fresh + /// presence (isOnline/lastSeenAt) since the list may be stale. Mirrors + /// web's selectConversation which calls getConversation on open. + Future refreshConversation(String conversationId) async { + try { + final fresh = await _repository.getConversation(conversationId); + if (!mounted) return; + final updated = state.conversations.map((c) { + return c.id == conversationId + ? c.copyWith(otherParty: fresh.otherParty) + : c; + }).toList(); + state = state.copyWith(conversations: updated); + } catch (_) { + // Non-fatal — existing cached data remains in place + } + } + Future loadUnreadCount() async { try { final count = await _repository.getUnreadCount(); diff --git a/lib/features/messaging/presentation/screens/chat_screen.dart b/lib/features/messaging/presentation/screens/chat_screen.dart index c4240aa..929a630 100644 --- a/lib/features/messaging/presentation/screens/chat_screen.dart +++ b/lib/features/messaging/presentation/screens/chat_screen.dart @@ -54,6 +54,9 @@ class _ChatScreenState extends ConsumerState final notifier = ref.read(messagingProvider.notifier); notifier.setActiveConversation(widget.conversationId); notifier.loadMessages(widget.conversationId); + // Refetch conversation to get fresh presence (isOnline/lastSeenAt) — + // the list snapshot may be stale. Matches web's selectConversation flow. + notifier.refreshConversation(widget.conversationId); // Initialize mute/star state from conversation data final conversations = ref.read(messagingProvider).conversations; final conv = conversations.where((c) => c.id == widget.conversationId).firstOrNull; @@ -120,6 +123,9 @@ class _ChatScreenState extends ConsumerState }); // Refresh messages to catch anything missed while in background ref.read(messagingProvider.notifier).loadMessages(widget.conversationId); + // Refresh conversation to pick up any presence changes that happened + // while the app was backgrounded (socket status events were missed) + ref.read(messagingProvider.notifier).refreshConversation(widget.conversationId); // Now that user is actively viewing, mark as read ref.read(messagingProvider.notifier).markAsRead(widget.conversationId); } diff --git a/lib/features/support_chat/presentation/providers/support_chat_provider.dart b/lib/features/support_chat/presentation/providers/support_chat_provider.dart index 6d2fc28..93a2fd3 100644 --- a/lib/features/support_chat/presentation/providers/support_chat_provider.dart +++ b/lib/features/support_chat/presentation/providers/support_chat_provider.dart @@ -77,6 +77,9 @@ class SupportChatNotifier extends StateNotifier { } } + // Track message IDs we've sent via REST to avoid socket echo duplicates + final Set _sentMessageIds = {}; + Future sendMessage(String content) async { final chat = state.chat; if (chat == null || content.trim().isEmpty) return; @@ -85,6 +88,7 @@ class SupportChatNotifier extends StateNotifier { state = state.copyWith(isSending: true); try { final message = await _repo.sendMessage(chat.id, content.trim()); + _sentMessageIds.add(message.id); // Dedup — socket broadcast may have already added this message if (!state.messages.any((m) => m.id == message.id)) { state = state.copyWith( @@ -121,7 +125,9 @@ class SupportChatNotifier extends StateNotifier { void addIncomingMessage(SupportMessage message) { if (state.chat == null || message.chatId != state.chat!.id) return; + // Skip if already in list OR if we sent this via REST (socket echo) if (state.messages.any((m) => m.id == message.id)) return; + if (_sentMessageIds.contains(message.id)) return; state = state.copyWith(messages: [...state.messages, message]); _repo.markAsRead(state.chat!.id); diff --git a/lib/features/support_chat/presentation/screens/support_chat_screen.dart b/lib/features/support_chat/presentation/screens/support_chat_screen.dart index 683d05c..ba78637 100644 --- a/lib/features/support_chat/presentation/screens/support_chat_screen.dart +++ b/lib/features/support_chat/presentation/screens/support_chat_screen.dart @@ -333,7 +333,17 @@ class _SupportChatScreenState extends ConsumerState { } Widget _buildMessageArea(SupportChatState state, String currentUserId) { - final groups = _groupByDate(state.messages); + // Filter out any messages with empty content (phantom/blank cards) + // and deduplicate by ID (socket echo + REST response race condition) + final seen = {}; + final cleanMessages = []; + for (final msg in state.messages) { + if (msg.content.trim().isEmpty) continue; + if (seen.contains(msg.id)) continue; + seen.add(msg.id); + cleanMessages.add(msg); + } + final groups = _groupByDate(cleanMessages); return Container( color: const Color(0xFFF9F9F9),