taafee-mobile/lib/features/chat/business logic layer/chat_controller.dart
2023-10-25 12:22:06 +03:00

955 lines
28 KiB
Dart

import 'dart:async';
import 'dart:developer' as dev;
import 'dart:io';
import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:get/get.dart';
import 'package:taafee_mobile/core/utils/pagination_list.dart';
import 'package:taafee_mobile/core/utils/rx_futures.dart';
import 'package:taafee_mobile/core/utils/utils.dart';
import 'package:taafee_mobile/features/chat/data_layer/model/chat_user.dart';
import 'package:taafee_mobile/features/chat/data_layer/model/room.dart';
import 'package:taafee_mobile/features/chat/data_layer/service/chat_resource.dart'
as resource;
// import 'package:path_provider/path_provider.dart';
import 'package:rx_future/rx_future.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../core/errors/custom_exception.dart';
import '../../../core/local_storage/local_storage.dart';
import '../../../core/network/socket/socket.dart';
import '../data_layer/model/message.dart';
import '../data_layer/service/chat_files_source.dart';
import '../data_layer/service/chat_listener.dart';
enum SocketConnectionState { connected, connecting, notConnected, error }
class ChatController extends GetxController {
//sources
resource.ChatResource chatResource = resource.ChatResource();
ChatListener chatListener = ChatListener();
ChatFilesSource chatFilesSource = ChatFilesSource();
LocalStorage localStorage = LocalStorage();
//initialization
Future<void> init(
{dynamic Function(MessageModel)? handler,
required Function(dynamic) onSessionTerminated}) async {
if (isInitialized) return;
chatUser = ChatUser(
id: localStorage.getChatUserId() ?? 0,
name: localStorage.getUser()!.firstName);
await socketInit();
receiveMessagesInRoom();
receiveMessages(handler: handler);
listenForMessagesReading();
listenOnRoomState();
listenOnSessionTerminated(onSessionTerminated: onSessionTerminated);
}
//socket
SocketIO io = Get.find<SocketIO>();
bool isInitialized = false;
Future<void> socketInit() async {
if (isInitialized) return;
io.init();
io.setAuthentication(localStorage.getChatToken() ?? "");
io.setOnConnect(() {
connectionState.update((val) {
connectionState.value = SocketConnectionState.connected;
});
connectionState.refresh();
dev.log("Connected Successfully");
});
io.setOnDisconnect(() {
dev.log("Socket disconnected");
connectionState.update((val) {
val = SocketConnectionState.notConnected;
});
connectionState.refresh();
});
io.setOnConnecting(() {
connectionState.update((val) {
val = SocketConnectionState.connecting;
});
connectionState.refresh();
dev.log("Connecting to socket");
});
io.setOnConnectionError(() {
dev.log("Error Connecting to socket");
connectionState.update((val) {
val = SocketConnectionState.error;
});
connectionState.refresh();
throw GenericException(
type: ExceptionType.connectionError,
errorMessage: "You Have no Internet Connection",
);
});
isInitialized = true;
await io.connect();
}
// user
ChatUser chatUser = ChatUser.zero();
/// ------------------- connection -----------///
RxFuture<void> serverState = RxFuture(null);
Rx<SocketConnectionState> connectionState =
SocketConnectionState.notConnected.obs;
bool get shouldConnect =>
(connectionState.value == SocketConnectionState.error) |
(connectionState.value == SocketConnectionState.notConnected);
Future<void> connectToServer({Function? onSuccess}) async {
if (!shouldConnect) return;
serverState.observe((_) async {
return await io.boot();
}, onSuccess: ((p0) {
onSuccess?.call();
}));
}
/// ------------------- Rooms ----------------///
RxFuture<Pagination<Room>> rooms = RxFuture(Pagination());
int? get currentRoomId => currentRoom.value?.id;
ChatUser? get currentChatUser => currentRoom.value!.user;
Rx<Room?> currentRoom = null.obs;
//private
void setCurrentRoom(Room room) {
currentRoom = room.obs;
currentRoom.refresh();
}
Room? getRoomById(int? id) => id == null
? null
: rooms.result.data.firstWhereOrNull((element) => element.id == id);
// Future<void> getRooms({int? specificPage}) async {
// await connectToServer();
// await rooms.observe(
// (value) async {
// await value!.nextPage(
// (currentPage) async {
// return chatResource.getRooms(currentPage);
// },
// place: InsertPlace.end,
// );
// return value; // there is no way that this would be null, there is a default value in rooms declaration.
// },
// multipleCallsBehavior: MultipleCallsBehavior.abortNew,
// );
// }
Future<void> getRooms({String searchQuery = '', int? specificPage}) async {
await connectToServer();
if (specificPage != null) {
rooms.update((val) {
val!.value.clear();
});
rooms.refresh();
}
await rooms.observe(
(value) async {
await value!.nextPage(
(currentPage) async {
return chatResource.getRooms(
searchQuery: searchQuery,
currentPage: specificPage ?? currentPage);
},
place: InsertPlace.end,
);
return value; // there is no way that this would be null, there is a default value in rooms declaration.
},
multipleCallsBehavior: MultipleCallsBehavior.abortNew,
);
}
void updateRoom() {
int index = rooms.result.data
.indexWhere((element) => (element.id == currentRoomId));
// rooms = Pagination(
// data: rooms.result.data.insert(0, Room.copy(currentRoom.value!)));
rooms.update((val) {
if (index == -1) {
val!.value.data.insert(0, Room.copy(currentRoom.value!));
index = 0;
}
if (currentRoom.value!.messages.data.isNotEmpty) {
val!.value.data[index].lastMessage =
currentRoom.value!.messages.data[0];
val.value.data[index].lastMessageDate =
currentRoom.value!.messages.data[0].createdAt;
}
if (index != 0) {
Room room = val!.value.data[index];
val.value.data.removeAt(index);
val.value.data.insert(0, room);
}
});
rooms.refresh();
}
RxFuture<Room?> createRoomState = RxFuture(null);
void createRoom(
{required int chatUserId, void Function(Room?)? onSuccess}) async {
await createRoomState.observe((value) async {
return await chatResource.createRoom(id: chatUserId);
}, onSuccess: (room) async {
await getRooms();
Room? roomById = getRoomById(room!.id);
if (roomById != null) {
room = roomById;
}
currentRoom = room.obs;
// currentRoom.update((val) {
// val = room;
// });
// currentRoom.value = room;
currentRoom.refresh();
onSuccess?.call(room);
}, onError: (err) {
if (err.toString() == 'email_already_exists') {
// getRooms()
// get the room that contains two users (chatUserId)
// setCurrentRoom
//
}
});
}
RxFuture<void> blockRoomState = RxFuture(null);
void blockRoom(
{void Function()? onSuccess, void Function(Object)? onError}) async {
blockRoomState.observe((value) async {
return await chatResource.blockRoom(id: currentRoomId!);
}, onSuccess: (value) async {
if (rooms.result.data.isEmpty) {
await getRooms();
}
int index = rooms.result.data
.indexWhere((element) => element.id == currentRoomId);
rooms.update((val) {
val!.value.data[index].state = RoomState.blocked;
});
currentRoom.update((val) {
val!.state = RoomState.blocked;
});
currentRoom.refresh();
rooms.refresh();
onSuccess?.call();
}, onError: (err) {
onError?.call(err);
});
}
RxFuture<void> unblockRoomState = RxFuture(null);
void unblockRoom(
{void Function()? onSuccess, void Function(Object)? onError}) async {
blockRoomState.observe((value) async {
return await chatResource.unblockRoom(id: currentRoomId!);
}, onSuccess: (value) {
int index = rooms.result.data
.indexWhere((element) => element.id == currentRoomId);
rooms.update((val) {
val!.value.data[index].state = RoomState.active;
});
currentRoom.update((val) {
val!.state = RoomState.active;
});
currentRoom.refresh();
rooms.refresh();
onSuccess?.call();
}, onError: (err) {
onError?.call(err);
});
}
void listenOnRoomState() {
listenOnRoomBlocked();
listenOnRoomBlocking();
listenOnRoomUnblocked();
// chatListener.updatedRoom((roomId, roomStatus) {
// if (rooms.result.data.isNotEmpty) {
// int index = rooms.result.data
// .indexWhere((element) => element.id == currentRoomId);
// rooms.update((val) {
// val!.value.data[index].state = Room.getRoomStateByString(roomStatus)!;
// });
// rooms.refresh();
// if (currentRoomId == roomId) {
// currentRoom.update((val) {
// val!.state = Room.getRoomStateByString(roomStatus)!;
// });
// currentRoom.refresh();
// }
// }
// });
}
void listenOnRoomUnblocked() {
chatListener.unblockedRoom((roomId) {
if (rooms.result.data.isNotEmpty) {
int index = rooms.result.data
.indexWhere((element) => element.id == currentRoomId);
rooms.update((val) {
val!.value.data[index].state = RoomState.active;
});
rooms.refresh();
if (currentRoomId == roomId) {
currentRoom.update((val) {
val!.state = RoomState.active;
});
currentRoom.refresh();
}
}
});
}
void listenOnRoomBlocked() {
chatListener.blockedRoom((roomId) {
if (rooms.result.data.isNotEmpty) {
int index = rooms.result.data
.indexWhere((element) => element.id == currentRoomId);
rooms.update((val) {
val!.value.data[index].state = RoomState.blocking;
});
rooms.refresh();
if (currentRoomId == roomId) {
currentRoom.update((val) {
val!.state = RoomState.blocking;
});
currentRoom.refresh();
}
}
});
}
void listenOnRoomBlocking() {
chatListener.blockingRoom((roomId) {
if (rooms.result.data.isNotEmpty) {
int index = rooms.result.data
.indexWhere((element) => element.id == currentRoomId);
rooms.update((val) {
val!.value.data[index].state = RoomState.blocked;
});
rooms.refresh();
if (currentRoomId == roomId) {
currentRoom.update((val) {
val!.state = RoomState.blocked;
});
currentRoom.refresh();
}
}
});
}
void listenOnSessionTerminated(
{required Function(dynamic) onSessionTerminated}) {
chatListener.termminatedSession(onSessionTerminated);
}
//support
RxFuture<Room?> supportRoomState = RxFuture(null);
Future<void> checkSupportRoomStatus(
{void Function(Room?)? onSuccess, void Function(Object)? onError}) async {
supportRoomState.observe((value) async {
return await chatResource.checkSupportRoomStatus();
}, onSuccess: (room) async {
if (room == null) {
Room newSupportRoom = await chatResource.createSupportRoom();
setCurrentRoom(newSupportRoom);
//creat support room
//set current room
} else {
//set current room
setCurrentRoom(room);
}
onSuccess?.call(currentRoom.value);
}, onError: (err) {
onError?.call(err);
});
}
/// ------------- messages -------------- ///
// getMessages
RxFutures<void> messagesState = RxFutures();
Future<void> getCurrentRoomMessages({int? specificPage}) async {
if (currentRoom.value == null ||
(currentRoom.value!.messagesStateId != null &&
messagesState.loading(currentRoom.value!.messagesStateId!))) return;
String id = messagesState.init(RxFuture(null));
currentRoom.value!.messagesStateId = id;
await messagesState.observe(
id,
(value) async {
return await currentRoom.value!.messages.nextPage((currentPage) async {
return await chatResource.getCurrentRoomMessages(
roomId: currentRoomId!, page: specificPage ?? currentPage);
});
},
onSuccess: (value) {
currentRoom.refresh();
},
multipleCallsBehavior: MultipleCallsBehavior.abortNew,
);
}
//read Message
RxFutures<void> readMessageState = RxFutures();
void readMessage({required int messageId}) async {
dev.log('i am reading message with id : $messageId');
String sendingStateId = readMessageState.init(RxFuture(null));
readMessageState.observe(sendingStateId, (value) async {
await chatResource.readMessage(messageId: messageId);
}, onSuccess: (value) {
int index = currentRoom.value!.messages.data
.indexWhere((element) => element.id == messageId);
currentRoom.update((val) {
val!.messages.data[index].readBy!.ids
.insert(0, val.messages.data[index].user.id);
});
currentRoom.refresh();
});
}
void listenForMessagesReading() {
chatListener.messagesReaded((messageId, userId) {
if (currentRoom.value != null &&
currentRoom.value!.type == RoomType.private) {
currentRoom.update((val) {
int index = val!.messages.data
.indexWhere((element) => element.id == messageId);
val.messages.data[index].readBy!.ids.insert(0, userId);
});
currentRoom.refresh();
}
});
}
// sendMessages
RxFutures<MessageModel> sendingMessageState = RxFutures();
RxBool isMessageLoading(String? sendingMessageStateId) {
if (sendingMessageStateId != null) {
return messagesState.loading(sendingMessageStateId).obs;
} else {
return false.obs;
}
}
Future<void> sendMessage(MessageModel message,
{int? roomId, String? filePath, void Function(Object e)? onError}) async {
if (roomId == null && currentRoom.value == null) {
throw Exception(
"You must either provide room id, or select a room to send message",
);
}
message.user = chatUser;
message.userId = chatUser.id;
if (shouldConnect) {
try {
await io.boot();
await getCurrentRoomMessages(specificPage: 1);
} catch (err) {
onError?.call(err);
}
}
String sendingStateId = sendingMessageState.init(RxFuture(message));
message.sendingStateId = sendingStateId;
await sendingMessageState.observe(sendingStateId, (_) async {
message.sendingStateId = sendingStateId;
// currentRoom.value!.messages.data.insert(0, MessageModel.copy(message));
if (message.type != MessageType.voice) {
currentRoom.update((val) {
val!.messages.data.insert(0, MessageModel.copy(message));
});
}
if (message.type != MessageType.text) {
await uploadFile(filePath: filePath!);
if (uploadFileState.result != null) {
int id = uploadFileState.result!;
message.content = "$id";
} else {
onError?.call(e);
currentRoom.update((val) {
val!.messages.data.removeAt(0);
});
return message;
}
}
if (message.type == MessageType.voice) {
currentRoom.update((val) {
// message.temporaryFile = null;
val!.messages.data.insert(0, MessageModel.copy(message));
val.messages.data[0].content = '${uploadFileState.result}';
// val.messages.data[0].temporaryFile = null;
});
currentRoom.refresh();
}
return await chatResource.sendMessage(
messageModel: message, roomId: currentRoomId!);
}, onSuccess: (value) {
currentRoom.update((val) {
val!.messages.data[0].id = value.id;
val.messages.data[0].sendingStateId = null;
val.messages.data[0].content = value.content;
val.messages.data[0].readBy = value.readBy;
if (message.type != MessageType.voice) {
val.messages.data[0].temporaryFile = null;
}
});
rooms.refresh();
}, onError: (err) {
currentRoom.update((val) {
val!.messages.data.removeAt(0);
});
onError?.call(err);
});
}
// cancel sending
void cancelSendingMessage(MessageModel message) {
if (message.sendingStateId == null ||
!sendingMessageState.loading(message.sendingStateId!)) return;
sendingMessageState.cancel(message.sendingStateId!);
}
// receive Messages
void receiveMessagesInRoom() async {
connectToServer();
chatListener.receiveMessage((message) {
currentRoom.update((val) {
val?.messages.data.insert(0, message);
});
});
}
void receiveMessages({dynamic Function(MessageModel)? handler}) async {
connectToServer();
chatListener.receiveMessage((message) async {
if (rooms.result.data.isEmpty) {
await getRooms();
}
if (rooms.result.data
.lastIndexWhere((element) => element.id == message.roomId) ==
-1) {
await getRooms();
}
int index = rooms.result.data
.lastIndexWhere((element) => element.id == message.roomId);
if (index == -1) {
rooms.update((val) {
val!.value.data.insert(
0,
Room(
id: message.roomId,
user: message.user,
lastMessageDate: message.createdAt));
});
index = 0;
}
rooms.update((val) {
val!.value.data[index].lastMessage = message;
});
rooms.refresh();
//assign when user tap .
// currentRoom.value = rooms.result.data
// .firstWhere((element) => (element.id == message.roomId));
handler?.call(message);
});
}
// upload Files
RxFuture<int?> uploadFileState = RxFuture(null);
Future<void> uploadFile({required String filePath}) async {
await uploadFileState.observe((value) async {
return await chatFilesSource.uploadFile(
roomId: currentRoomId!, filePath: filePath);
});
}
// Chat Details scroll controller
Rx<ScrollController> scrollController = ScrollController().obs;
void scrollControllerJump() {
scrollController.update((val) {
val!.jumpTo(0);
});
scrollController.refresh();
}
// keyBoard opening detection
RxBool keyBoardOpened = false.obs;
void setkeyBoardOpened(bool newValue) {
keyBoardOpened.value = newValue;
keyBoardOpened.refresh();
}
//reply senario handling
RxBool isReplying = false.obs;
void toggleIsReplying() => isReplying.value = !isReplying.value;
Rx<MessageModel> replyModel = MessageModel.zero().obs;
void updateReplyModel({
required MessageModel messageModel,
}) {
replyModel.value = messageModel;
replyModel.refresh();
}
//voice message
FlutterSoundRecorder voiceRecorder = FlutterSoundRecorder();
File recordFile = File('record.wav');
bool isRecordReady = false;
RxBool isRecording = false.obs;
RxString recordingTime = '00:00'.obs;
File? audioFile;
toggleIsRecording() {
if (isRecording.value == false) {
recordingTime.value = '00:00';
recordingTime.refresh();
}
isRecording.value = !isRecording.value;
isRecording.refresh();
}
Future<File?> getVoiceFile({required String id}) async {
return await chatFilesSource.getVoiceFile(id);
}
void timer() {
var startTime = DateTime.now();
Timer.periodic(const Duration(seconds: 1), (t) {
var diff = DateTime.now().difference(startTime);
recordingTime.value =
'${diff.inHours == 0 ? '' : '${diff.inHours.toString().padLeft(2, "0")}:'}${(diff.inMinutes % 60).floor().toString().padLeft(2, "0")}:${(diff.inSeconds % 60).floor().toString().padLeft(2, '0')}';
recordingTime.refresh();
if (!voiceRecorder.isRecording) {
t.cancel();
}
});
}
Future initRecorder() async {
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw 'dfaf';
}
await Permission.storage.request();
await Permission.manageExternalStorage.request();
await voiceRecorder.openRecorder();
isRecordReady = true;
voiceRecorder.setSubscriptionDuration(const Duration(microseconds: 500));
}
Future record() async {
if (!isRecordReady) return;
await voiceRecorder.startRecorder(
toFile: '${Utils.randomString(5)}${Random().nextInt(100000)}');
timer();
}
Future cancelRecording() async {
recordingTime.value = '00:00';
recordingTime.refresh();
await voiceRecorder.stopRecorder();
}
Future stopRecording({void Function(Object)? onError}) async {
recordingTime.value = '00:00';
recordingTime.refresh();
if (!isRecordReady) return;
String? path = await voiceRecorder.stopRecorder();
audioFile = File(path!);
await sendMessage(
MessageModel(
user: chatUser,
userId: chatUser.id,
roomId: currentRoomId!,
content: '',
createdAt: DateTime.now(),
temporaryFile: File(audioFile!.path),
type: MessageType.voice,
),
filePath: audioFile!.path,
roomId: currentRoomId, onError: (err) {
onError?.call(err);
});
}
RxMap<String, File> voiceFiles = <String, File>{}.obs;
Map<String, Rx<PlayerController>> voicePlayers = {};
RxMap<String, bool> isPlayingMap = <String, bool>{}.obs;
RxMap<String, bool> isLoadingMap = <String, bool>{}.obs;
void addVoiceFile(String id, File file) {
voiceFiles[id] = file;
voiceFiles.refresh();
}
Rx<PlayerController> getVoicePlayer(String id) {
if (voicePlayers[id] != null) {
return voicePlayers[id]!;
}
voicePlayers[id] = PlayerController().obs;
return voicePlayers[id]!;
}
void updateIsLoading(String id, bool newValue) {
isLoadingMap[id] = newValue;
isLoadingMap.refresh();
}
void updateIsPlaying(String id, bool newValue) {
isPlayingMap[id] = newValue;
isPlayingMap.refresh();
}
void clear() {
isInitialized = false;
connectionState = SocketConnectionState.notConnected.obs;
voiceFiles.clear();
voicePlayers.clear();
isPlayingMap.clear();
isLoadingMap.clear();
voiceRecorder = FlutterSoundRecorder();
recordFile = File('record.wav');
isRecordReady = false;
isRecording = false.obs;
recordingTime = '00:00'.obs;
isReplying = false.obs;
replyModel = MessageModel.zero().obs;
keyBoardOpened = false.obs;
scrollController = ScrollController().obs;
messagesState.clear();
rooms = RxFuture(Pagination());
currentRoom = null.obs;
io.clearListeners();
}
// RxBool isPlaying(String id) {
// if (isPlayingMap[id] != null) {
// return isPlayingMap[id]!;
// }
// isPlayingMap[id] = false.obs;
// return isPlayingMap[id]!;
// }
// RxBool isLoading(String id) {
// if (isLoadingMap[id] != null) {
// return isLoadingMap[id]!;
// }
// isLoadingMap[id] = false.obs;
// return isLoadingMap[id]!;
// }
}
//dummy
// RxList<MessageModel> messages = [
// MessageModel(
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// user: ChatUser(id: 0, name: 'name'),
// ),
// MessageModel(
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// user: ChatUser(id: 0, name: 'name'),
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// roomId: 0,
// user: ChatUser(id: 0, name: 'name'),
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// direction: MessageDirection.received,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// direction: MessageDirection.received,
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: networkImageTest,
// type: MessageType.image,
// createdAt: DateTime.now().toString(),
// direction: MessageDirection.received,
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: networkImageTest,
// type: MessageType.image,
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// repliedMessage: MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: networkImageTest,
// type: MessageType.image,
// createdAt: DateTime.now().toString(),
// direction: MessageDirection.received,
// ),
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// repliedMessage: MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// ),
// createdAt: DateTime.now().toString(),
// ),
// MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// repliedMessage: MessageModel(
// user: ChatUser(id: 0, name: 'name'),
// roomId: 0,
// userId: 0,
// content: 'message,hi',
// createdAt: DateTime.now().toString(),
// direction: MessageDirection.received,
// ),
// createdAt: DateTime.now().toString(),
// ),
// ].obs;