import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart'; import 'package:just_audio/just_audio.dart' as JustAudio; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/encryption.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as userLoginToken; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart'; import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/signalr_client.dart'; import 'package:uuid/uuid.dart'; class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { ScrollController scrollController = ScrollController(); TextEditingController message = TextEditingController(); TextEditingController search = TextEditingController(); List userChatHistory = []; List? pChatHistory, searchedChats; String chatCID = ''; bool isLoading = true; bool isChatScreenActive = false; int receiverID = 0; late File selectedFile; bool isFileSelected = false; String sFileType = ""; bool isMsgReply = false; List repliedMsg = []; List favUsersList = []; int paginationVal = 0; bool currentUserTyping = false; int? cTypingUserId = 0; //Chat Home Page Counter int chatUConvCounter = 0; Future getUserAutoLoginToken() async { userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); if (userLoginResponse.response != null) { AppState().setchatUserDetails = userLoginResponse; } else { AppState().setchatUserDetails = userLoginResponse; Utils.showToast( userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", ); } } Future buildHubConnection() async { chatHubConnection = await getHubConnection(); await chatHubConnection.start(); if (kDebugMode) { logger.i("Hub Conn: Startedddddddd"); } chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion); } Future getHubConnection() async { HubConnection hub; HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); hub = HubConnectionBuilder() .withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=${AppState().chatDetails!.response!.id}&source=Web&access_token=${AppState().chatDetails!.response!.token}", options: httpOp) .withAutomaticReconnect(retryDelays: [2000, 5000, 10000, 20000]).build(); return hub; } void registerEvents() { chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus); // chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); chatHubConnection.on("OnSubmitChatAsync", OnSubmitChatAsync); chatHubConnection.on("OnUserTypingAsync", onUserTyping); chatHubConnection.on("OnUserCountAsync", userCountAsync); // chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); if (kDebugMode) { logger.i("All listeners registered"); } } void getUserRecentChats() async { if (chatHubConnection.state != HubConnectionState.Connected) { getUserAutoLoginToken().whenComplete(() async { await buildHubConnection(); getUserRecentChats(); }); return; } ChatUserModel recentChat = await ChatApiClient().getRecentChats(); ChatUserModel favUList = await ChatApiClient().getFavUsers(); if (favUList.response != null && recentChat.response != null) { favUsersList = favUList.response!; favUsersList.sort( (ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()), ); for (dynamic user in recentChat.response!) { for (dynamic favUser in favUList.response!) { if (user.id == favUser.id) { user.isFav = favUser.isFav; } } } } pChatHistory = recentChat.response ?? []; pChatHistory!.sort( (ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()), ); searchedChats = pChatHistory; isLoading = false; await invokeUserChatHistoryNotDeliveredAsync( userId: int.parse( AppState().chatDetails!.response!.id.toString(), ), ); sort(); notifyListeners(); if (searchedChats!.isNotEmpty || favUsersList.isNotEmpty) { getUserImages(); } } Future invokeUserChatHistoryNotDeliveredAsync({required int userId}) async { await chatHubConnection.invoke("GetUserChatHistoryNotDeliveredAsync", args: [userId]); return ""; } void getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false}) async { isLoading = true; if (isNewChat) userChatHistory = []; if (!loadMore) paginationVal = 0; isChatScreenActive = true; receiverID = receiverUID; Response response = await ChatApiClient().getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal); if (response.statusCode == 204) { if (isNewChat) { userChatHistory = []; } else if (loadMore) { Utils.showToast("No More Data To Load"); } } else { if (loadMore) { List temp = getSingleUserChatModel(response.body).reversed.toList(); userChatHistory.addAll(temp); } else { userChatHistory = getSingleUserChatModel(response.body).reversed.toList(); } } isLoading = false; notifyListeners(); if (isChatScreenActive && receiverUID == receiverID) { markRead(userChatHistory, receiverUID); } generateConvId(); } void generateConvId() async { Uuid uuid = const Uuid(); chatCID = uuid.v4(); } void markRead(List data, int receiverID) { if (data != null) { for (SingleUserChatModel element in data!) { if (AppState().chatDetails!.response!.id! == element.targetUserId) { if (element.isSeen != null) { if (!element.isSeen!) { element.isSeen = true; dynamic data = [ { "userChatHistoryId": element.userChatHistoryId, "TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId, "isDelivered": true, "isSeen": true, } ]; updateUserChatHistoryStatusAsync(data); notifyListeners(); } } for (ChatUser element in searchedChats!) { if (element.id == receiverID) { element.unreadMessageCount = 0; chatUConvCounter = 0; } } } } notifyListeners(); } } void updateUserChatHistoryStatusAsync(List data) { try { chatHubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); } catch (e) { throw e; } } void updateUserChatHistoryOnMsg(List data) { try { chatHubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); } catch (e) { throw e; } } List getSingleUserChatModel(String str) => List.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x))); Future uploadAttachments(String userId, File file) async { dynamic result; try { Object? response = await ChatApiClient().uploadMedia(userId, file); if (response != null) { result = response; } else { result = []; } } catch (e) { throw e; } return result; } void updateUserChatStatus(List? args) { dynamic items = args!.toList(); for (var cItem in items[0]) { for (SingleUserChatModel chat in userChatHistory) { if (cItem["contantNo"].toString() == chat.contantNo.toString()) { chat.isSeen = cItem["isSeen"]; chat.isDelivered = cItem["isDelivered"]; } } } notifyListeners(); } void onChatSeen(List? args) { dynamic items = args!.toList(); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { // user.userStatus = items.first["userStatus"]; // } // } // notifyListeners(); } void userCountAsync(List? args) { dynamic items = args!.toList(); // logger.d(items); //logger.d("---------------------------------User Count Async -------------------------------------"); //logger.d(items); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { // user.userStatus = items.first["userStatus"]; // } // } // notifyListeners(); } void updateChatHistoryWindow(List? args) { dynamic items = args!.toList(); if (kDebugMode) { logger.i("---------------------------------Update Chat History Windows Async -------------------------------------"); } logger.d(items); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { // user.userStatus = items.first["userStatus"]; // } // } // notifyListeners(); } void chatNotDelivered(List? args) { dynamic items = args!.toList(); for (dynamic item in items[0]) { for (ChatUser element in searchedChats!) { if (element.id == item["currentUserId"]) { int? val = element.unreadMessageCount ?? 0; element.unreadMessageCount = val! + 1; } } } notifyListeners(); } void changeStatus(List? args) { dynamic items = args!.toList(); for (ChatUser user in searchedChats!) { if (user.id == items.first["id"]) { user.userStatus = items.first["userStatus"]; } } notifyListeners(); } void filter(String value) async { List? tmp = []; if (value.isEmpty || value == "") { tmp = pChatHistory; } else { for (ChatUser element in pChatHistory!) { if (element.userName!.toLowerCase().contains(value.toLowerCase())) { tmp.add(element); } } } searchedChats = tmp; notifyListeners(); } Future onMsgReceived(List? parameters) async { List data = [], temp = []; for (dynamic msg in parameters!) { data = getSingleUserChatModel(jsonEncode(msg)); temp = getSingleUserChatModel(jsonEncode(msg)); data.first.targetUserId = temp.first.currentUserId; data.first.targetUserName = temp.first.currentUserName; data.first.targetUserEmail = temp.first.currentUserEmail; data.first.currentUserId = temp.first.targetUserId; data.first.currentUserName = temp.first.targetUserName; data.first.currentUserEmail = temp.first.targetUserEmail; if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) { data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg"); } if (data.first.userChatReplyResponse != null) { if (data.first.fileTypeResponse != null) { if (data.first.userChatReplyResponse!.fileTypeId == 12 || data.first.userChatReplyResponse!.fileTypeId == 4 || data.first.userChatReplyResponse!.fileTypeId == 3) { data.first.userChatReplyResponse!.image = await ChatApiClient().downloadURL(fileName: data.first.userChatReplyResponse!.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg"); data.first.userChatReplyResponse!.isImageLoaded = true; } } } } if (searchedChats != null) { dynamic contain = searchedChats!.where((ChatUser element) => element.id == data.first.currentUserId); if (contain.isEmpty) { List emails = []; emails.add(await EmailImageEncryption().encrypt(val: data.first.currentUserEmail!)); List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( ChatUser( id: data.first.currentUserId, userName: data.first.currentUserName, email: data.first.currentUserEmail, unreadMessageCount: 0, isImageLoading: false, image: chatImages!.first.profilePicture ?? "", isImageLoaded: true, userStatus: 1, isTyping: false, userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, data.first.currentUserId.toString()), ), ); } } setMsgTune(); if (isChatScreenActive && data.first.currentUserId == receiverID) { userChatHistory.insert(0, data.first); } else { if (searchedChats != null) { for (ChatUser user in searchedChats!) { if (user.id == data.first.currentUserId) { int tempCount = user.unreadMessageCount ?? 0; user.unreadMessageCount = tempCount + 1; } } sort(); } } List list = [ { "userChatHistoryId": data.first.userChatHistoryId, "TargetUserId": temp.first.targetUserId, "isDelivered": true, "isSeen": isChatScreenActive && data.first.currentUserId == receiverID ? true : false } ]; updateUserChatHistoryOnMsg(list); invokeChatCounter(userId: AppState().chatDetails!.response!.id!); notifyListeners(); } void OnSubmitChatAsync(List? parameters) { logger.i(parameters); List data = [], temp = []; for (dynamic msg in parameters!) { data = getSingleUserChatModel(jsonEncode(msg)); temp = getSingleUserChatModel(jsonEncode(msg)); data.first.targetUserId = temp.first.currentUserId; data.first.targetUserName = temp.first.currentUserName; data.first.targetUserEmail = temp.first.currentUserEmail; data.first.currentUserId = temp.first.targetUserId; data.first.currentUserName = temp.first.targetUserName; data.first.currentUserEmail = temp.first.targetUserEmail; } if (isChatScreenActive && data.first.currentUserId == receiverID) { int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == 0); logger.d(index); userChatHistory[index] = data.first; } notifyListeners(); } void sort() { searchedChats!.sort( (ChatUser a, ChatUser b) => b.unreadMessageCount!.compareTo(a.unreadMessageCount!), ); } void onUserTyping(List? parameters) { for (ChatUser user in searchedChats!) { if (user.id == parameters![1] && parameters[0] == true) { user.isTyping = parameters[0] as bool?; Future.delayed( const Duration(seconds: 2), () { user.isTyping = false; notifyListeners(); }, ); } } notifyListeners(); } int getFileType(String value) { switch (value) { case ".pdf": return 1; case ".png": return 3; case ".txt": return 5; case ".jpg": return 12; case ".jpeg": return 4; case ".xls": return 7; case ".xlsx": return 7; case ".doc": return 6; case ".docx": return 6; case ".ppt": return 8; case ".pptx": return 8; case ".zip": return 2; case ".rar": return 2; case ".aac": return 13; case ".mp3": return 14; default: return 0; } } String getFileTypeDescription(String value) { switch (value) { case ".pdf": return "application/pdf"; case ".png": return "image/png"; case ".txt": return "text/plain"; case ".jpg": return "image/jpg"; case ".jpeg": return "image/jpeg"; case ".ppt": return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; case ".pptx": return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; case ".doc": return "application/vnd.openxmlformats-officedocument.wordprocessingm"; case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingm"; case ".xls": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; case ".xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; case ".zip": return "application/octet-stream"; case ".rar": return "application/octet-stream"; case ".aac": return "audio/aac"; case ".mp3": return "audio/mp3"; default: return ""; } } Future sendChatToServer( {required int chatEventId, required fileTypeId, required int targetUserId, required String targetUserName, required chatReplyId, required bool isAttachment, required bool isReply, Uint8List? image, Uint8List? voice, required bool isImageLoaded}) async { Uuid uuid = const Uuid(); String contentNo = uuid.v4(); String msg = message.text; SingleUserChatModel data = SingleUserChatModel( userChatHistoryId: 0, chatEventId: chatEventId, chatSource: 1, contant: msg, contantNo: contentNo, conversationId: chatCID, createdDate: DateTime.now(), currentUserId: AppState().chatDetails!.response!.id, currentUserName: AppState().chatDetails!.response!.userName, targetUserId: targetUserId, targetUserName: targetUserName, isReplied: false, fileTypeId: fileTypeId, userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null, fileTypeResponse: isAttachment ? FileTypeResponse( fileTypeId: fileTypeId, fileTypeName: getFileExtension(selectedFile.path).toString(), fileKind: "file", fileName: selectedFile.path.split("/").last, fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()), ) : null, image: image, isImageLoaded: isImageLoaded, voice: voice, ); if (kDebugMode) { logger.i("model data: " + jsonEncode(data)); } userChatHistory.insert(0, data); isFileSelected = false; isMsgReply = false; sFileType = ""; message.clear(); notifyListeners(); String chatData = '{"contant":"$msg","contantNo":"$contentNo","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}'; await chatHubConnection.invoke("AddChatUserAsync", args: [json.decode(chatData)]); } void sendChatMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async { if (!isFileSelected && !isMsgReply) { if (kDebugMode) { print("Normal Text Msg"); } if (message.text == null || message.text.isEmpty) { return; } sendChatToServer( chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false, isImageLoaded: false, image: null); } if (isFileSelected && !isMsgReply) { if (kDebugMode) { logger.i("Normal Attachment Msg"); } Utils.showLoading(context); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); String? ext = getFileExtension(selectedFile.path); Utils.hideLoading(context); sendChatToServer( chatEventId: 2, fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: true, chatReplyId: null, isReply: false, isImageLoaded: true, image: selectedFile.readAsBytesSync()); } if (!isFileSelected && isMsgReply) { if (kDebugMode) { print("Normal Text To Text Reply"); } if (message.text == null || message.text.isEmpty) { return; } sendChatToServer( chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, chatReplyId: repliedMsg.first.userChatHistoryId, isAttachment: false, isReply: true, isImageLoaded: repliedMsg.first.isImageLoaded!, image: repliedMsg.first.image); } // reply msg over image && normal if (isFileSelected && isMsgReply) { if (kDebugMode) { print("Reply With File"); } Utils.showLoading(context); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); String? ext = getFileExtension(selectedFile.path); Utils.hideLoading(context); sendChatToServer( chatEventId: 2, fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: true, chatReplyId: repliedMsg.first.userChatHistoryId, isReply: true, isImageLoaded: true, image: selectedFile.readAsBytesSync()); } if (searchedChats != null) { dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); if (contain.isEmpty) { List emails = []; emails.add(await EmailImageEncryption().encrypt(val: userEmail)); List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( ChatUser( id: targetUserId, userName: targetUserName, unreadMessageCount: 0, email: userEmail, isImageLoading: false, image: chatImages.first.profilePicture ?? "", isImageLoaded: true, isTyping: false, isFav: false, userStatus: userStatus, userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()), ), ); notifyListeners(); } } else { List emails = []; emails.add(await EmailImageEncryption().encrypt(val: userEmail)); List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( ChatUser( id: targetUserId, userName: targetUserName, unreadMessageCount: 0, email: userEmail, isImageLoading: false, image: chatImages.first.profilePicture ?? "", isImageLoaded: true, isTyping: false, isFav: false, userStatus: userStatus, userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()), ), ); notifyListeners(); } } void selectImageToUpload(BuildContext context) { ImageOptions.showImageOptionsNew(context, true, (String image, File file) async { if (checkFileSize(file.path)) { selectedFile = file; isFileSelected = true; sFileType = getFileExtension(file.path)!; message.text = file.path.split("/").last; Navigator.of(context).pop(); } else { Utils.showToast("Max 1 mb size is allowed to upload"); } notifyListeners(); }); } void removeAttachment() { isFileSelected = false; sFileType = ""; message.text = ''; notifyListeners(); } String? getFileExtension(String fileName) { try { if (kDebugMode) { logger.i("ext: " + "." + fileName.split('.').last); } return "." + fileName.split('.').last; } catch (e) { return null; } } bool checkFileSize(String path) { int fileSizeLimit = 1024; File f = File(path); double fileSizeInKB = f.lengthSync() / 1024; double fileSizeInMB = fileSizeInKB / 1024; if (fileSizeInKB > fileSizeLimit) { return false; } else { return true; } } String getType(String type) { switch (type) { case ".pdf": return "assets/images/pdf.svg"; case ".png": return "assets/images/png.svg"; case ".txt": return "assets/icons/chat/txt.svg"; case ".jpg": return "assets/images/jpg.svg"; case ".jpeg": return "assets/images/jpg.svg"; case ".xls": return "assets/icons/chat/xls.svg"; case ".xlsx": return "assets/icons/chat/xls.svg"; case ".doc": return "assets/icons/chat/doc.svg"; case ".docx": return "assets/icons/chat/doc.svg"; case ".ppt": return "assets/icons/chat/ppt.svg"; case ".pptx": return "assets/icons/chat/ppt.svg"; case ".zip": return "assets/icons/chat/zip.svg"; case ".rar": return "assets/icons/chat/zip.svg"; case ".aac": return "assets/icons/chat/aac.svg"; case ".mp3": return "assets/icons/chat/zip.mp3"; default: return "assets/images/thumb.svg"; } } void chatReply(SingleUserChatModel data) { repliedMsg = []; data.isReplied = true; isMsgReply = true; repliedMsg.add(data); notifyListeners(); } void closeMe() { repliedMsg = []; isMsgReply = false; notifyListeners(); } String dateFormte(DateTime data) { DateFormat f = DateFormat('hh:mm a dd MMM yyyy'); f.format(data); return f.format(data); } Future favoriteUser({required int userID, required int targetUserID}) async { fav.FavoriteChatUser favoriteChatUser = await ChatApiClient().favUser(userID: userID, targetUserID: targetUserID); if (favoriteChatUser.response != null) { for (ChatUser user in searchedChats!) { if (user.id == favoriteChatUser.response!.targetUserId!) { user.isFav = favoriteChatUser.response!.isFav; dynamic contain = favUsersList!.where((ChatUser element) => element.id == favoriteChatUser.response!.targetUserId!); if (contain.isEmpty) { favUsersList.add(user); } } } } notifyListeners(); } Future unFavoriteUser({required int userID, required int targetUserID}) async { fav.FavoriteChatUser favoriteChatUser = await ChatApiClient().unFavUser(userID: userID, targetUserID: targetUserID); if (favoriteChatUser.response != null) { for (ChatUser user in searchedChats!) { if (user.id == favoriteChatUser.response!.targetUserId!) { user.isFav = favoriteChatUser.response!.isFav; } } favUsersList.removeWhere( (ChatUser element) => element.id == targetUserID, ); } notifyListeners(); } void clearSelections() { searchedChats = pChatHistory; search.clear(); isChatScreenActive = false; receiverID = 0; paginationVal = 0; message.text = ''; isFileSelected = false; repliedMsg = []; sFileType = ""; isMsgReply = false; notifyListeners(); } void clearAll() { searchedChats = pChatHistory; search.clear(); isChatScreenActive = false; receiverID = 0; paginationVal = 0; message.text = ''; isFileSelected = false; repliedMsg = []; sFileType = ""; } void disposeData() { search.clear(); isChatScreenActive = false; receiverID = 0; paginationVal = 0; message.text = ''; isFileSelected = false; repliedMsg = []; sFileType = ""; deleteData(); favUsersList.clear(); searchedChats!.clear(); pChatHistory!.clear(); chatHubConnection.stop(); AppState().chatDetails = null; } void deleteData() { List exists = [], unique = []; exists.addAll(searchedChats!); exists.addAll(favUsersList!); Map profileMap = {}; for (ChatUser item in exists) { profileMap[item.email!] = item; } unique = profileMap.values.toList(); for (ChatUser element in unique!) { deleteFile(element.id.toString()); } } void getUserImages() async { List emails = []; List exists = [], unique = []; exists.addAll(searchedChats!); exists.addAll(favUsersList!); Map profileMap = {}; for (ChatUser item in exists) { profileMap[item.email!] = item; } unique = profileMap.values.toList(); for (ChatUser element in unique!) { emails.add(await EmailImageEncryption().encrypt(val: element.email!)); } List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); for (ChatUser user in searchedChats!) { for (ChatUserImageModel uImage in chatImages) { if (user.email == uImage.email) { user.image = uImage.profilePicture ?? ""; user.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, user.id.toString()); user.isImageLoading = false; user.isImageLoaded = true; } } } for (ChatUser favUser in favUsersList) { for (ChatUserImageModel uImage in chatImages) { if (favUser.email == uImage.email) { favUser.image = uImage.profilePicture ?? ""; favUser.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, favUser.id.toString()); favUser.isImageLoading = false; favUser.isImageLoaded = true; } } } notifyListeners(); } Future downloadImageLocal(String? encodedBytes, String userID) async { File? myfile; if (encodedBytes == null) { return myfile; } else { await deleteFile(userID); Uint8List decodedBytes = base64Decode(encodedBytes); Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); String dirPath = '${appDocumentsDirectory.path}/chat_images'; if (!await Directory(dirPath).exists()) { await Directory(dirPath).create(); await File('$dirPath/.nomedia').create(); } late File imageFile = File("$dirPath/$userID.jpg"); imageFile.writeAsBytesSync(decodedBytes); return imageFile; } } Future deleteFile(String userID) async { Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); String dirPath = '${appDocumentsDirectory.path}/chat_images'; late File imageFile = File('$dirPath/$userID.jpg'); if (await imageFile.exists()) { await imageFile.delete(); } } Future downChatMedia(Uint8List bytes, String ext) async { String dir = (await getApplicationDocumentsDirectory()).path; File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext); await file.writeAsBytes(bytes); return file.path; } void setMsgTune() async { JustAudio.AudioPlayer player = JustAudio.AudioPlayer(); await player.setVolume(1.0); String audioAsset = ""; if (Platform.isAndroid) { audioAsset = "assets/audio/pulse_tone_android.mp3"; } else { audioAsset = "assets/audio/pulse_tune_ios.caf"; } try { await player.setAsset(audioAsset); await player.load(); player.play(); } catch (e) { print("Error: $e"); } } Future getChatMedia(BuildContext context, {required String fileName, required String fileTypeName, required int fileTypeID}) async { Utils.showLoading(context); if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) { Uint8List encodedString = await ChatApiClient().downloadURL(fileName: fileName, fileTypeDescription: getFileTypeDescription(fileTypeName)); try { String path = await downChatMedia(encodedString, fileTypeName ?? ""); Utils.hideLoading(context); OpenFile.open(path); } catch (e) { Utils.showToast("Cannot open file."); } } } void onNewChatConversion(List? params) { dynamic items = params!.toList(); chatUConvCounter = items[0]["singleChatCount"] ?? 0; notifyListeners(); } Future invokeChatCounter({required int userId}) async { await chatHubConnection.invoke("GetChatCounversationCount", args: [userId]); return ""; } void userTypingInvoke({required int currentUser, required int reciptUser}) async { await chatHubConnection.invoke("UserTypingAsync", args: [reciptUser, currentUser]); } // Audio Recoding Work Timer? _timer; int _recodeDuration = 0; bool isRecoding = false; bool isPause = false; bool isPlaying = false; String? path; String? musicFile; late Directory appDirectory; late RecorderController recorderController; late PlayerController playerController; //////// Audio Recoding Work //////////////////// Future initAudio({required int receiverId}) async { // final dir = Directory((Platform.isAndroid // ? await getExternalStorageDirectory() //FOR ANDROID // : await getApplicationSupportDirectory() //FOR IOS // )! appDirectory = await getApplicationDocumentsDirectory(); String dirPath = '${appDirectory.path}/chat_audios'; if (!await Directory(dirPath).exists()) { await Directory(dirPath).create(); await File('$dirPath/.nomedia').create(); } path = "$dirPath/${AppState().chatDetails!.response!.id}-$receiverID-${DateTime.now().microsecondsSinceEpoch}.aac"; recorderController = RecorderController() ..androidEncoder = AndroidEncoder.aac ..androidOutputFormat = AndroidOutputFormat.mpeg4 ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC ..sampleRate = 6000 ..updateFrequency = const Duration(milliseconds: 100) ..bitRate = 18000; playerController = PlayerController(); } void disposeAudio() { isRecoding = false; isPlaying = false; isPause = false; recorderController.dispose(); playerController.dispose(); } void startRecoding() async { PermissionStatus status = await Permission.microphone.request(); if (status.isDenied == true) { startRecoding(); } else { recorderController.reset(); await recorderController.record(path); _recodeDuration = 0; _startTimer(); isRecoding = !isRecoding; notifyListeners(); } } Future _startTimer() async { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) async { _recodeDuration++; if (_recodeDuration <= 59) { applyCounter(); } else { pauseRecoding(); } }); } void applyCounter() { buildTimer(); notifyListeners(); } Future pauseRecoding() async { isPause = true; isPlaying = true; recorderController.pause(); path = await recorderController.stop(false); File file = File(path!); file.readAsBytesSync(); path = file.path; await playerController.preparePlayer(file.path, 1.0); _timer?.cancel(); notifyListeners(); } Future deleteRecoding() async { _recodeDuration = 0; _timer?.cancel(); if (path == null) { path = await recorderController.stop(true); } else { await recorderController.stop(true); } if (path != null && path!.isNotEmpty) { File delFile = File(path!); double fileSizeInKB = delFile.lengthSync() / 1024; double fileSizeInMB = fileSizeInKB / 1024; if (kDebugMode) { debugPrint("Deleted file size: ${delFile.lengthSync()}"); debugPrint("Deleted file size in KB: " + fileSizeInKB.toString()); debugPrint("Deleted file size in MB: " + fileSizeInMB.toString()); } if (await delFile.exists()) { delFile.delete(); } isPause = false; isRecoding = false; isPlaying = false; notifyListeners(); } } String buildTimer() { String minutes = _formatNum(_recodeDuration ~/ 60); String seconds = _formatNum(_recodeDuration % 60); return '$minutes : $seconds'; } String _formatNum(int number) { String numberStr = number.toString(); if (number < 10) { numberStr = '0' + numberStr; } return numberStr; } void playRecoding() async { isPlaying = true; await playerController.startPlayer(finishMode: FinishMode.pause); } void playOrPause() async { playerController.playerState == PlayerState.playing ? await playerController.pausePlayer() : playRecoding(); notifyListeners(); } void sendVoiceMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async { if (!isPause) { path = await recorderController.stop(false); } if (kDebugMode) { logger.i("path:" + path!); } File voiceFile = File(path!); voiceFile.readAsBytesSync(); _timer?.cancel(); isPause = false; isPlaying = false; isRecoding = false; Utils.showLoading(context); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), voiceFile); String? ext = getFileExtension(voiceFile.path); Utils.hideLoading(context); sendVoiceMessageToServer( msgText: voiceFile.path!.split("/").last, chatEventId: 2, fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, isVoiceAttached: true, voice: voiceFile.readAsBytesSync(), userEmail: userEmail, userStatus: userStatus, chatReplyId: null, isAttachment: true, isReply: isMsgReply, voicFile: voiceFile, ); notifyListeners(); } Future sendVoiceMessageToServer( {String? msgText, int? chatEventId, int? fileTypeId, int? targetUserId, String? targetUserName, bool? isVoiceAttached, Uint8List? voice, String? userEmail, int? userStatus, bool? isReply, bool? isAttachment, int? chatReplyId, File? voicFile}) async { Uuid uuid = const Uuid(); String contentNo = uuid.v4(); String msg = msgText!; SingleUserChatModel data = SingleUserChatModel( chatEventId: chatEventId, chatSource: 1, contant: msg, contantNo: contentNo, conversationId: chatCID, createdDate: DateTime.now(), currentUserId: AppState().chatDetails!.response!.id, currentUserName: AppState().chatDetails!.response!.userName, targetUserId: targetUserId, targetUserName: targetUserName, isReplied: false, fileTypeId: fileTypeId, userChatReplyResponse: isReply! ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null, fileTypeResponse: isAttachment! ? FileTypeResponse( fileTypeId: fileTypeId, fileTypeName: getFileExtension(voicFile!.path).toString(), fileKind: "file", fileName: msgText, fileTypeDescription: getFileTypeDescription(getFileExtension(voicFile!.path).toString()), ) : null, image: null, isImageLoaded: false, voice: voice, ); userChatHistory.insert(0, data); notifyListeners(); String chatData = '{"contant":"$msg","contantNo":"$contentNo","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}'; await chatHubConnection.invoke("AddChatUserAsync", args: [json.decode(chatData)]); if (searchedChats != null) { dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); if (contain.isEmpty) { List emails = []; emails.add(await EmailImageEncryption().encrypt(val: userEmail!)); List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( ChatUser( id: targetUserId, userName: targetUserName, unreadMessageCount: 0, email: userEmail, isImageLoading: false, image: chatImages.first.profilePicture ?? "", isImageLoaded: true, isTyping: false, isFav: false, userStatus: userStatus, userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()), ), ); notifyListeners(); } } else { List emails = []; emails.add(await EmailImageEncryption().encrypt(val: userEmail!)); List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( ChatUser( id: targetUserId, userName: targetUserName, unreadMessageCount: 0, email: userEmail, isImageLoading: false, image: chatImages.first.profilePicture ?? "", isImageLoaded: true, isTyping: false, isFav: false, userStatus: userStatus, userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()), ), ); notifyListeners(); } } void playVoice( BuildContext context, { required SingleUserChatModel data, }) async { Utils.showLoading(context); Uint8List encodedString = await ChatApiClient().downloadURL(fileName: data.contant!, fileTypeDescription: getFileTypeDescription(data.fileTypeResponse!.fileTypeName ?? "")); try { String path = await downChatVoice(encodedString, data.fileTypeResponse!.fileTypeName ?? "", data); File file = File(path!); file.readAsBytesSync(); Utils.hideLoading(context); await data.voiceController!.preparePlayer(file.path, 1.0); data.voiceController!.startPlayer(finishMode: FinishMode.pause); notifyListeners(); } catch (e) { Utils.showToast("Cannot open file."); } } void stopPlaying(BuildContext context, {required SingleUserChatModel data}) async { await data.voiceController!.pausePlayer(); notifyListeners(); } Future downChatVoice(Uint8List bytes, String ext, SingleUserChatModel data) async { String dirPath = '${(await getApplicationDocumentsDirectory()).path}/chat_audios'; if (!await Directory(dirPath).exists()) { await Directory(dirPath).create(); await File('$dirPath/.nomedia').create(); } File file = File("$dirPath/${data.currentUserId}-${data.targetUserId}-${DateTime.now().microsecondsSinceEpoch}." + ext); await file.writeAsBytes(bytes); return file.path; } // data.scrollController.animateTo( // data.scrollController.position.maxScrollExtent, // duration: const Duration(milliseconds: 100), // curve: Curves.easeOut, // ); }