-
Notifications
You must be signed in to change notification settings - Fork 195
Open
Description
I will explain you the problem with simple words. When I send a message "hello" to user1, and then change the chatroom, if I send a message "Good Morning" to user2, after sending this message, I will see my "Hello" message on the screen too. but that message doesn't belong to this chatroom. When I reload the chatroom, then it will show correctly.
here is my Sockit_Logic:
class SocketLogic extends GetxController {
late SocketIOUtility _socketUtility;
late Timer heartbeatTimer;
final fileUploadLogic = Get.put(FileUploadLogic());
RxList<MessageHistory> messageHistory = <MessageHistory>[].obs;
RxList<SocketOnlineUsers> onlineUsers = <SocketOnlineUsers>[].obs;
RxList<SocketOnlineUsers> activeUsers = <SocketOnlineUsers>[].obs;
RxSet<MessageHistory> messages = <MessageHistory>{}.obs;
RxSet<MessageHistory> supportMessages = <MessageHistory>{}.obs;
RxSet<TypingModel> typingUsers = <TypingModel>{}.obs;
Rx<ChatRoomModel> singleChatRoom = ChatRoomModel().obs;
Rx<ChatRoomModel> oldChatroom = ChatRoomModel().obs;
Rx<String> supportId = ''.obs;
RxBool seenValue = false.obs;
RxInt unReadCount = 0.obs;
int currentMessageCount = 0;
@override
void onInit() {
super.onInit();
_socketUtility = SocketIOUtility.instance;
_socketUtility.initSocketIO(
onConnect: _onConnect,
onMessage: (data) => _log('Received message: $data'),
onDisconnect: () => _log('Disconnected from socket'),
onError: (error) => _log('Socket error: $error'),
);
}
SocketStatus? get socketStatus => _socketUtility.socketStatus;
void sendMessage({
required String event,
required dynamic message,
}) {
_socketUtility.sendMessage(event: event, message: message);
}
void getDataFromSocket({required String event, required Function data}) {
_socketUtility.on(event, data);
}
void connect() {
_socketUtility.onConnect!();
}
void reConnect() {
_socketUtility.reconnect();
}
void disconnect() {
_socketUtility.closeSocket();
heartbeatTimer.cancel();
}
void getAllChatRooms() {
getDataFromSocket(
event: SocketConstants.chatRoomsResponse,
data: (data) {
unReadCount.value = 0;
List<MessageModel> allMessages =
Get.find<MessageLogic>().allMessagesList;
print(
"From socket logic message ________________________________________ $allMessages");
allMessages.clear();
List<MessageModel> dateSortedMessages = [];
if (data["data"] != null) {
// for (var message in data["data"]) {
// MessageModel jsonValue = MessageModel.fromJson(message);
// if (jsonValue.otherUser?.userRole != RoleConstants.admin) {
// dateSortedMessages.add(jsonValue);
// unReadCount += jsonValue.unreadMessagesCount ?? 0;
// } else {
// supportId.value = jsonValue.otherUser?.id ?? '';
// printColored(supportId.value, PrintedColors.cyan);
// }
// }
for (var message in data["data"]) {
print("📩 Incoming message data: $message");
try {
if (message['lastMessage'] == null) {
print(
"⚠ Warning: Message with ID ${message['_id']} has no lastMessage. Assigning default.");
message['lastMessage'] = {
"_id": "",
"text": "",
"seen": false,
"timestamp": "",
"senderId": "",
"recipientId": ""
};
}
MessageModel jsonValue = MessageModel.fromJson(message);
print("✅ Parsed message: ${jsonValue.lastMessage?.text}");
// 🚀 Now we include ALL messages, including Admins
dateSortedMessages.add(jsonValue);
unReadCount += jsonValue.unreadMessagesCount ?? 0;
// Keeping track of Admins separately (optional)
if (jsonValue.otherUser?.userRole == RoleConstants.admin) {
supportId.value = jsonValue.otherUser?.id ?? '';
printColored(
"🔹 Admin message included: ${jsonValue.lastMessage?.text}",
PrintedColors.green);
}
} catch (e) {
print("❌ Error parsing message: $message, Error: $e");
}
}
dateSortedMessages.sort((a, b) {
final String? timeStampA = a.lastMessage?.timeStamp;
final String? timeStampB = b.lastMessage?.timeStamp;
if (timeStampA == null && timeStampB == null) {
return 0;
} else if (timeStampA == null) {
return 1;
} else if (timeStampB == null) {
return -1;
} else {
return timeStampB.compareTo(timeStampA);
}
});
allMessages.addAll(dateSortedMessages);
}
});
}
RxString? newChatroomId;
void createChatRoomResponse() {
getDataFromSocket(
event: SocketConstants.chatRoomCreated,
data: (data) {
newChatroomId?.value = '';
newChatroomId?.value = data["chatRoomId"];
joinRoom(chatRoomId: data["chatRoomId"]);
});
}
void createChatRoom({required String otherUserId}) {
sendMessage(event: SocketConstants.createChatRoom, message: {
"otherUserId": otherUserId,
"userId": storageBox.read(Storage.userId)
});
}
Future<void> requestSingleChatRoom({required String chatRoomId}) async {
print("Requesting chat room with ID: $chatRoomId");
sendMessage(event: SocketConstants.singleChatRoom, message: {
// "userRole": storageBox.read(Storage.role),
"chatRoomId": chatRoomId,
"userId": storageBox.read(Storage.userId)
});
}
void requestMoreSingleChatRoom({required String chatRoomId}) {
sendMessage(event: SocketConstants.fetchMoreMessages, message: {
"chatRoomId": chatRoomId,
"userId": storageBox.read(Storage.userId),
'currentMessageCount': currentMessageCount
});
}
void getSingleChatRoom() {
getDataFromSocket(
event: SocketConstants.chatRoomResponse,
data: (data) {
try {
var newChatRoom = ChatRoomModel.fromJson(data['data']);
var newMessages = newChatRoom.messageHistory ?? [];
if (singleChatRoom.value != null &&
singleChatRoom.value.messageHistory != null) {
// Append new messages to the existing message history
List<MessageHistory> a = [
...newMessages,
...singleChatRoom.value.messageHistory!
];
singleChatRoom.value.messageHistory!.assignAll(a);
singleChatRoom.refresh();
} else {
singleChatRoom.value = newChatRoom;
singleChatRoom.refresh();
}
currentMessageCount += newMessages.length;
} catch (e) {
rethrow;
}
if (unReadCount.value != 0) {
requestChatRooms();
}
});
}
Future<void> sendMessageToChatRoom(
{required String messageText,
required String recipientId,
required String replyTo}) async {
sendMessage(event: SocketConstants.sendMessage, message: {
"text": messageText,
"recipientId": recipientId,
"senderId": storageBox.read(Storage.userId),
"files": [],
"replyTo": replyTo.isEmpty ? null : replyTo,
});
}
void getMessage() {
_getMessage(event: SocketConstants.getMessage);
}
void getMessageOutSide() {
_getMessage(event: SocketConstants.getMessageOutSide);
}
void _getMessage({required String event}) {
getDataFromSocket(
event: event,
data: (data) async {
MessageHistory message = MessageHistory.fromJson(data);
bool isDuplicate = messages.any((msg) => msg.id == message.id);
if (!isDuplicate) {
await requestChatRooms();
if (seenValue.value == true) {
message.seen = true;
}
messages.add(message);
if (Platform.isIOS) {
if (message.senderId?.id != storageBox.read(Storage.userId)) {
LocalNotificationService.localNotificationService
.showSimpleNotification(
id: messageHistory.indexWhere((e) => e.id == message.id),
title: message.senderId?.fullName ?? '',
body: message.text ?? '',
);
}
}
}
});
}
Future<void> deleteMessage({required String messageId}) async {
sendMessage(event: SocketConstants.deleteMessage, message: {
"messageId": messageId,
"userId": storageBox.read(Storage.userId)
});
}
Future<void> joinRoom({required String chatRoomId}) async {
sendMessage(event: SocketConstants.joinRoom, message: {
"chatRoomId": chatRoomId,
"userId": storageBox.read(Storage.userId),
});
}
void messageRead() {
getDataFromSocket(
event: SocketConstants.messageRead,
data: (data) async {
seenValue.value = true;
});
}
void joinedRoom() {
getDataFromSocket(
event: SocketConstants.joinedRoom,
data: (data) async {
seenValue.value = true;
for (MessageHistory i in messages) {
i.seen = true;
}
});
}
void leftRoom() {
getDataFromSocket(
event: SocketConstants.leftRoom,
data: (data) async {
seenValue.value = false;
});
}
Future<void> leaveRoom({required String chatRoomId}) async {
seenValue.value = false;
sendMessage(event: SocketConstants.leaveRoom, message: {
"chatRoomId": chatRoomId,
"userId": storageBox.read(Storage.userId)
});
}
Future<void> deleteChatRoom({required String chatRoomId}) async {
sendMessage(event: SocketConstants.deleteChatRoom, message: {
"chatRoomId": chatRoomId,
"userId": storageBox.read(Storage.userId)
});
}
void deleteChatRoomResponse() {
getDataFromSocket(
event: SocketConstants.deleteChatRoomResponse,
data: (data) async {
requestChatRooms();
});
}
void chatRoomDeleted() {
getDataFromSocket(
event: SocketConstants.chatRoomDeleted, data: (data) async {});
}
void startHeartbeat() {
void heartBeat() {
sendMessage(
event: SocketConstants.heartbeat,
message: storageBox.read(Storage.userId));
}
heartBeat();
heartbeatTimer = Timer.periodic(const Duration(seconds: 20), (timer) {
heartBeat();
});
}
void _initializeSocketListeners() {
getOnlineUsers();
getMessage();
getMessageOutSide();
requestChatRooms();
getAllChatRooms();
getUserTyping();
getSingleChatRoom();
getSupportAllMessages();
getUploadedUrls();
deleteMessageResponse();
deleteMessageNotification();
messageRead();
deleteChatRoomResponse();
chatRoomDeleted();
joinedRoom();
leftRoom();
createChatRoomResponse();
}
}
and here is my message_chat_logic:
class MessageChatLogic extends GetxController {
final MessageChatState state = MessageChatState();
// final MessagesRepository repository = Get.put(MessagesRepository());
final DataBaseHelper database = Get.put(DataBaseHelper.instance);
final SocketLogic socket = Get.find<SocketLogic>();
TextEditingController textEditingController = TextEditingController();
RxList<File> files = <File>[].obs;
String optionsViewId = 'optionsViewId';
var text = '';
final isFocus = false.obs;
final isLoading = false.obs;
bool isShowPotions = false;
Rx<MessageModel> messageModel = MessageModel().obs;
RxSet<MessageHistory> messageHistory = <MessageHistory>{}.obs;
RxString name = ''.obs;
RxString recipientId = ''.obs;
String senderId = storageBox.read(Storage.userId);
Rx<MessageHistory>? replyMessage = MessageHistory().obs;
@override
void onInit() {
listenToChanges();
try {
if (Get.arguments["messageModel"] != null) {
messageModel.value = Get.arguments["messageModel"];
name.value = messageModel.value.otherUser?.fullName ?? 'Name';
recipientId.value = messageModel.value.otherUser?.id ?? '';
startJoinRoomTimer();
// getChatRoom();
} else {
name.value = Get.arguments["name"];
recipientId.value = Get.arguments["recipientId"];
socket.createChatRoom(
otherUserId: Get.arguments["recipientId"] ?? recipientId.value);
if (Get.arguments["chatRoomId"] != null) {
if (socket.newChatroomId?.value.isNotEmpty ?? false) {
// getChatRoom(socket.newChatroomId.value);
}
}
}
} catch (e) {
rethrow;
}
super.onInit();
}
int countLastFourMessagesSentByCurrentUser() {
int messageCount = 0;
List<MessageHistory> uniqueMessages = [];
Set<String> seenIds = {};
for (MessageHistory message in messageHistory) {
if (!seenIds.contains(message.id)) {
uniqueMessages.add(message);
seenIds.add(message.id ?? '');
}
}
for (var message in uniqueMessages.toList().sublist(
messageHistory.length > 4
? messageHistory.length - 4
: uniqueMessages.length)) {
if (message.senderId?.id == storageBox.read(Storage.userId)) {
messageCount++;
}
}
return messageCount;
}
void startJoinRoomTimer() {
socket.joinRoom(chatRoomId: messageModel.value.id ?? '');
// print("timer");
// });
}
void endJoinRoomTimer() {
socket.leaveRoom(chatRoomId: messageModel.value.id ?? '');
// joinRoomTimer?.cancel();
}
void listenToChanges() {
isLoading.value = true;
socket.messages.listen((_) async {
try {
List<MessageHistory> messagesToRemove = [];
for (MessageHistory message in socket.messages) {
if (message.senderId != null && message.recipientId != null) {
if ((message.recipientId == senderId &&
message.senderId?.id == recipientId.value) ||
(message.senderId?.id == senderId &&
message.recipientId == recipientId.value)) {
// Match the pending message based on its unique ID
MessageHistory? pendingMessage = socket
.singleChatRoom.value.messageHistory
?.firstWhereOrNull((e) {
return e.text == message.text &&
e.status == MessageStatus.pending;
});
// Remove the pending message if it exists
if (pendingMessage != null) {
socket.singleChatRoom.value.messageHistory
?.remove(pendingMessage);
// pendingMessage.status = MessageStatus.sent;
// socket.singleChatRoom.value.messageHistory?.add(pendingMessage);
}
// Add the new message received from the socket
socket.singleChatRoom.value.messageHistory?.add(message);
socket.singleChatRoom.refresh();
}
if (socket.singleChatRoom.value != null) {
var chatRoomMessages =
socket.singleChatRoom.value.messageHistory ?? [];
if (!chatRoomMessages.any((msg) => msg.id == message.id)) {
chatRoomMessages.add(message);
socket.singleChatRoom.value.messageHistory = chatRoomMessages;
socket.singleChatRoom.refresh();
}
}
isLoading.value = false;
update();
}
}
// Safely remove pending messages after iteration
if (messagesToRemove.isNotEmpty) {
socket.singleChatRoom.value.messageHistory?.removeWhere((e) {
database.deleteMessageById(e.id ?? '');
return messagesToRemove.contains(e);
});
socket.singleChatRoom.refresh();
}
} catch (e) {
rethrow;
}
});
socket.singleChatRoom.listen((_) {
try {
messageHistory.clear();
List<MessageHistory> messagesToRemove = [];
for (MessageHistory message
in socket.singleChatRoom.value.messageHistory ?? []) {
if (message.chatRoom ==
(messageModel.value.id ?? Get.arguments['chatRoomId'])) {
MessageHistory? pendingMessage = socket
.singleChatRoom.value.messageHistory
?.firstWhereOrNull((e) =>
e.text == message.text &&
e.status == MessageStatus.pending);
if (pendingMessage != null) {
// sendPendingMessage(pendingMessage);
messagesToRemove.add(pendingMessage);
}
messageHistory.add(message);
}
}
// Safely remove pending messages after iteration
// if (messagesToRemove.isNotEmpty) {
// socket.singleChatRoom.value.messageHistory?.removeWhere((e) {
// database.deleteMessageById(e.id ?? '');
// return messagesToRemove.contains(e);
// });
// socket.singleChatRoom.refresh();
// }
isLoading.value = false;
update();
} catch (e) {
rethrow;
}
});
}
// void getChatRoom([String? chatRoomId]) async {
// isLoading.value = true;
// print("Loading started");
// try {
// print("Before requestSingleChatRoom");
// await socket.requestSingleChatRoom(
// chatRoomId: chatRoomId ??
// (messageModel.value.id ?? Get.arguments['chatRoomId']));
// print("Chat room request completed");
// } catch (e) {
// print("Error fetching chat room: $e");
// } finally {
// if (isLoading.value) {
// print("Setting loading to false");
// isLoading.value = false;
// }
// }
// }
Future<FormzSubmissionStatus> getMoreChatRoom() async {
socket.requestMoreSingleChatRoom(
chatRoomId: messageModel.value.id ?? Get.arguments['chatRoomId']);
return FormzSubmissionStatus.success;
}
void sendTyping() async {
await socket.sendTypingStatus(chatRoomId: messageModel.value.id ?? '');
}
String parseTimestamp(DateTime timestamp) {
DateTime dateTime = timestamp;
String formattedTime = DateFormat.Hm().format(dateTime.toLocal());
return formattedTime;
}
void getFromDatabase() async {
// Fetch all messages from the database
List<MessageHistory> list = await database.getAllMessages();
// Sort the list by the timestamp field in ascending order
list.sort((a, b) => a.timestamp!.compareTo(b.timestamp!));
// Add the sorted messages to messageHistory
socket.singleChatRoom.value.messageHistory?.addAll(list);
}
void sendPendingMessage(MessageHistory pendingMessage) async {
try {
await socket.sendMessageToChatRoom(
messageText: pendingMessage.text ?? '',
recipientId: pendingMessage.recipientId ?? '',
replyTo: pendingMessage.replyMessage?.id ?? '',
);
// newMessage.status = MessageStatus.sent;
} catch (e) {
rethrow;
}
}
void sendMessage() async {
text = text.trim();
List<FileUploadModel> localFileUrls = [];
for (File i in files) {
localFileUrls.add(FileUploadModel(
url: i.path,
name: i.path,
type: "image",
originalName: '',
size: i.lengthSync()));
}
final MessageHistory newMessage = MessageHistory(
id: UniqueKey().toString(),
text: text,
senderId: SenderId(id: storageBox.read(Storage.userId)),
recipientId: recipientId.value,
replyTo: replyMessage?.value.id ?? '',
status: MessageStatus.pending,
fileUrls: localFileUrls,
deleted: false,
seen: false,
timestamp: DateTime.now());
// database.insertMessageHistory(newMessage);
// getFromDatabase();
socket.singleChatRoom.value.messageHistory?.add(newMessage);
socket.singleChatRoom.refresh();
textEditingController.clear();
if (files.isNotEmpty) {
await socket.uploadFiles(
files: files,
senderId: senderId,
recipientId: recipientId.value,
chatroomId: messageModel.value.id ?? Get.arguments['chatRoomId'],
text: text,
replyTo: replyMessage?.value.id ?? '',
onProgress: (progress) {
List<FileUploadModel>? fileUrls;
newMessage.uploadProgress = progress;
print("File upload progress$progress");
messageHistory.refresh();
},
);
files.clear();
} else if (text.isNotEmpty) {
try {
await socket.sendMessageToChatRoom(
messageText: text.trim(),
recipientId: recipientId.value,
replyTo: replyMessage?.value.id ?? '',
);
// newMessage.status = MessageStatus.sent;
} catch (e) {
newMessage.status = MessageStatus.error;
} finally {
messageHistory.refresh();
text = '';
replyMessage?.value = MessageHistory();
}
}
}
String formatBytes(int bytes) {
if (bytes < 0) {
throw ArgumentError("Value of bytes cannot be negative");
}
if (bytes < 1024) {
return "$bytes B";
} else if (bytes < 1048576) {
double kb = bytes / 1024;
return "${kb.toStringAsFixed(2)} KB";
} else {
double mb = bytes / 1048576;
return "${mb.toStringAsFixed(2)} MB";
}
}
void sendVoice() {}
void takePhoto() {
ImagePickTool.takePhoto(callBlock: (xFile) {
File file = File(xFile.path);
files.add(file);
});
}
void pickVideo() {
ImagePickTool.pickVideo(callBlock: (xFile) {
File file = File(xFile.path);
files.add(file);
});
}
void getImage() {
ImagePickTool.pickMultiImage(callBlock: (xFiles) {
if (xFiles is List<XFile>) {
List<File> fileList = xFiles.map((xFile) => File(xFile.path)).toList();
files.addAll(fileList);
} else {
print(
'Error: Expected a List<XFile>, but received ${xFiles.runtimeType}');
}
});
}
void pickAllFile() {
ImagePickTool.pickAllFile(successCallBack: (xFiles) {
if (xFiles is List<XFile>) {
List<File> fileList = xFiles.map((xFile) => File(xFile.path)).toList();
files.addAll(fileList);
} else if (xFiles is File) {
files.add(xFiles);
} else {
printColored(
'Error: Expected a List<XFile>, but received ${xFiles.runtimeType}',
PrintedColors.red);
}
});
}
@override
void onClose() {
textEditingController.dispose();
super.onClose();
}
}
Metadata
Metadata
Assignees
Labels
No labels