diff --git a/mobile/lib/controllers/chat.dart b/mobile/lib/controllers/chat.dart index 8893310..812a9be 100644 --- a/mobile/lib/controllers/chat.dart +++ b/mobile/lib/controllers/chat.dart @@ -1,13 +1,12 @@ +import 'dart:async'; import 'dart:convert'; +import 'dart:isolate'; import 'package:flutter/material.dart'; import '../models/message.dart'; import '../models/conversation.dart'; import '../services/api.dart'; import '../services/ws.dart'; -import 'dart:async'; -import 'dart:isolate'; - class ChatController extends ChangeNotifier { final ApiService _api = ApiService(); final WebSocketService _ws = WebSocketService(); @@ -15,29 +14,23 @@ class ChatController extends ChangeNotifier { List inbox = []; List activeChat = []; List contactSearchResults = []; + String? currentChatUserId; bool isPeerTyping = false; bool isPeerOnline = false; bool isSearchLoading = false; Future initSession(String token) async { - _ws.disconnect(); - - inbox.clear(); - activeChat.clear(); - contactSearchResults.clear(); - currentChatUserId = null; - isPeerTyping = false; - isPeerOnline = false; - notifyListeners(); - await _ws.connect(token); _ws.stream?.listen( (rawFrame) { try { - _handleIncomingWebSocketEvent(jsonDecode(rawFrame)); + final decoded = jsonDecode(rawFrame); + if (decoded is Map) { + _handleIncomingWebSocketEvent(decoded); + } } catch (e) { - debugPrint("WebSocket stream payload error: $e"); + debugPrint("WebSocket payload error: $e"); } }, onError: (err) => debugPrint("WS Pipeline Error: $err"), @@ -46,32 +39,13 @@ class ChatController extends ChangeNotifier { await loadInbox(); } - void clearSessionData() { - _ws.disconnect(); - inbox.clear(); - activeChat.clear(); - contactSearchResults.clear(); - currentChatUserId = null; - isPeerTyping = false; - isPeerOnline = false; - notifyListeners(); - } - Future loadInbox() async { try { final res = await _api.getConversations(); - dynamic targetData; - - if (res.data is List) { - targetData = res.data; - } else if (res.data is Map) { - targetData = res.data['data'] ?? res.data['conversations'] ?? res.data; - } + final targetList = _extractDataList(res.data, ['conversations']); - if (targetData is List) { - inbox = targetData.map((json) => Conversation.fromJson(json)).toList(); - notifyListeners(); - } + inbox = targetList.map((json) => Conversation.fromJson(json)).toList(); + notifyListeners(); } catch (e) { debugPrint("Inbox read error: $e"); } @@ -86,7 +60,7 @@ class ChatController extends ChangeNotifier { try { final res = await _api.getChatHistory(targetUid); - dynamic targetData; + final targetList = _extractDataList(res.data, ['messages']); activeChat = await Isolate.run(() { return targetList.reversed @@ -102,12 +76,19 @@ class ChatController extends ChangeNotifier { unawaited(loadInbox()); } catch (e) { debugPrint("Timeline tracking fail: $e"); + notifyListeners(); } - notifyListeners(); } Future queryUsers(String term) async { final String cleanTerm = term.trim(); + + if (cleanTerm.isEmpty) { + contactSearchResults.clear(); + notifyListeners(); + return; + } + if (cleanTerm.length < 3) { contactSearchResults.clear(); notifyListeners(); @@ -118,22 +99,10 @@ class ChatController extends ChangeNotifier { notifyListeners(); try { - final res = await _api.searchUsers(cleanTerm); - if (res.data == null) { - contactSearchResults = []; - } else if (res.data is List) { - contactSearchResults = res.data; - } else if (res.data is Map) { - if (res.data['data'] != null && res.data['data'] is List) { - contactSearchResults = res.data['data']; - } else if (res.data['users'] != null && res.data['users'] is List) { - contactSearchResults = res.data['users']; - } else { - contactSearchResults = []; - } - } + final res = await _api.searchUsers(term); + contactSearchResults = _extractDataList(res.data, ['users']); } catch (e) { - debugPrint("User query pipeline failure: $e"); + debugPrint("User query failure: $e"); contactSearchResults.clear(); } finally { isSearchLoading = false; @@ -150,16 +119,15 @@ class ChatController extends ChangeNotifier { } void sendTextMessage(String text) { - if (currentChatUserId == null || text.trim().isEmpty) return; + final cleanContent = text.trim(); + if (currentChatUserId == null || cleanContent.isEmpty) return; - final String clientMessageId = - "cli_${DateTime.now().millisecondsSinceEpoch}"; - final String targetId = currentChatUserId!; - final String cleanContent = text.trim(); + final targetId = currentChatUserId!; + final clientMessageId = "cli_${DateTime.now().millisecondsSinceEpoch}"; _ws.sendChat(messageId: "", targetId: targetId, content: cleanContent); - final Message optimisticMsg = Message( + final optimisticMsg = Message( id: clientMessageId, senderId: "me", receiverId: targetId, @@ -170,6 +138,7 @@ class ChatController extends ChangeNotifier { activeChat.add(optimisticMsg); notifyListeners(); + loadInbox(); } @@ -181,13 +150,14 @@ class ChatController extends ChangeNotifier { void _handleIncomingWebSocketEvent(Map data) { final String? type = data['type']; - final String? senderId = - data['sender_id'] ?? data['sender'] ?? data['receiver_id']; - if (type == null) return; + final String? senderId = + data['sender_id'] ?? data['sender'] ?? data['receiver_id']; final String? cleanSender = senderId?.trim().toLowerCase(); final String? cleanCurrentChat = currentChatUserId?.trim().toLowerCase(); + final bool isCurrentChat = + cleanSender != null && cleanSender == cleanCurrentChat; switch (type) { case 'user_status': @@ -201,35 +171,54 @@ class ChatController extends ChangeNotifier { notifyListeners(); } break; + case 'chat': case 'message': - if (cleanSender != null && cleanSender == cleanCurrentChat) { + if (isCurrentChat) { activeChat.add(Message.fromJson(data)); _ws.sendReadReceipt(targetId: currentChatUserId!); notifyListeners(); - } else { - loadInbox(); } + loadInbox(); break; + case 'typing': - if (cleanSender != null && cleanSender == cleanCurrentChat) { + if (isCurrentChat) { isPeerTyping = data['content'] == 'true'; notifyListeners(); } break; + case 'read_receipt': - if (cleanSender != null && cleanSender == cleanCurrentChat) { + if (isCurrentChat) { + bool updated = false; activeChat = activeChat.map((msg) { final String sId = msg.senderId.trim().toLowerCase(); - if (sId == 'me' || sId != cleanCurrentChat) { + if (!msg.isRead && (sId == 'me' || sId != cleanCurrentChat)) { + updated = true; return msg.copyWith(isRead: true); } return msg; }).toList(); - notifyListeners(); - loadInbox(); + + if (updated) { + notifyListeners(); + loadInbox(); + } } break; } } + + List _extractDataList(dynamic data, List fallbackKeys) { + if (data == null) return []; + if (data is List) return data; + if (data is Map) { + if (data['data'] is List) return data['data']; + for (final key in fallbackKeys) { + if (data[key] is List) return data[key]; + } + } + return []; + } } diff --git a/mobile/lib/pages/settings pages/accounts.dart b/mobile/lib/pages/settings pages/accounts.dart index 4e41cf8..416a23b 100644 --- a/mobile/lib/pages/settings pages/accounts.dart +++ b/mobile/lib/pages/settings pages/accounts.dart @@ -45,7 +45,7 @@ class AccountsSettings extends StatelessWidget { await context.read().logOutSave(); if (context.mounted) { - context.read().clearSessionData(); + // context.read().clearSessionData(); Navigator.pushAndRemoveUntil( context,