diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index 91b622a..41f941f 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -147,7 +147,7 @@ class ChatApiClient { } Future uploadMedia(String userId, File file) async { - dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatServerBaseApiUrl}upload')); + dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatMediaImageUploadUrl}upload')); request.fields.addAll({'userId': userId, 'fileSource': '1'}); request.files.add(await MultipartFile.fromPath('files', file.path)); request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'}); diff --git a/lib/models/chat/get_single_user_chat_list_model.dart b/lib/models/chat/get_single_user_chat_list_model.dart index 6a35f0e..07b2f51 100644 --- a/lib/models/chat/get_single_user_chat_list_model.dart +++ b/lib/models/chat/get_single_user_chat_list_model.dart @@ -1,32 +1,35 @@ import 'dart:convert'; +import 'package:flutter/foundation.dart'; + List singleUserChatModelFromJson(String str) => List.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x))); String singleUserChatModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); class SingleUserChatModel { - SingleUserChatModel({ - this.userChatHistoryId, - this.userChatHistoryLineId, - this.contant, - this.contantNo, - this.currentUserId, - this.currentUserName, - this.targetUserId, - this.targetUserName, - this.encryptedTargetUserId, - this.encryptedTargetUserName, - this.chatEventId, - this.fileTypeId, - this.isSeen, - this.isDelivered, - this.createdDate, - this.chatSource, - this.conversationId, - this.fileTypeResponse, - this.userChatReplyResponse, - this.isReplied, - }); + SingleUserChatModel( + {this.userChatHistoryId, + this.userChatHistoryLineId, + this.contant, + this.contantNo, + this.currentUserId, + this.currentUserName, + this.targetUserId, + this.targetUserName, + this.encryptedTargetUserId, + this.encryptedTargetUserName, + this.chatEventId, + this.fileTypeId, + this.isSeen, + this.isDelivered, + this.createdDate, + this.chatSource, + this.conversationId, + this.fileTypeResponse, + this.userChatReplyResponse, + this.isReplied, + this.isImageLoaded, + this.image}); int? userChatHistoryId; int? userChatHistoryLineId; @@ -48,29 +51,32 @@ class SingleUserChatModel { FileTypeResponse? fileTypeResponse; UserChatReplyResponse? userChatReplyResponse; bool? isReplied; + bool? isImageLoaded; + Uint8List? image; factory SingleUserChatModel.fromJson(Map json) => SingleUserChatModel( - userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], - userChatHistoryLineId: json["userChatHistoryLineId"] == null ? null : json["userChatHistoryLineId"], - contant: json["contant"] == null ? null : json["contant"], - contantNo: json["contantNo"] == null ? null : json["contantNo"], - currentUserId: json["currentUserId"] == null ? null : json["currentUserId"], - currentUserName: json["currentUserName"] == null ? null : json["currentUserName"], - targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], - targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], - encryptedTargetUserId: json["encryptedTargetUserId"] == null ? null : json["encryptedTargetUserId"], - encryptedTargetUserName: json["encryptedTargetUserName"] == null ? null : json["encryptedTargetUserName"], - chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], - fileTypeId: json["fileTypeId"], - isSeen: json["isSeen"] == null ? null : json["isSeen"], - isDelivered: json["isDelivered"] == null ? null : json["isDelivered"], - createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), - chatSource: json["chatSource"] == null ? null : json["chatSource"], - conversationId: json["conversationId"] == null ? null : json["conversationId"], - fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), - userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]), - isReplied: false, - ); + userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], + userChatHistoryLineId: json["userChatHistoryLineId"] == null ? null : json["userChatHistoryLineId"], + contant: json["contant"] == null ? null : json["contant"], + contantNo: json["contantNo"] == null ? null : json["contantNo"], + currentUserId: json["currentUserId"] == null ? null : json["currentUserId"], + currentUserName: json["currentUserName"] == null ? null : json["currentUserName"], + targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], + targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], + encryptedTargetUserId: json["encryptedTargetUserId"] == null ? null : json["encryptedTargetUserId"], + encryptedTargetUserName: json["encryptedTargetUserName"] == null ? null : json["encryptedTargetUserName"], + chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], + fileTypeId: json["fileTypeId"], + isSeen: json["isSeen"] == null ? null : json["isSeen"], + isDelivered: json["isDelivered"] == null ? null : json["isDelivered"], + createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), + chatSource: json["chatSource"] == null ? null : json["chatSource"], + conversationId: json["conversationId"] == null ? null : json["conversationId"], + fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), + userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]), + isReplied: false, + isImageLoaded: false, + image: null); Map toJson() => { "userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId, @@ -138,6 +144,8 @@ class UserChatReplyResponse { this.targetUserId, this.targetUserName, this.fileTypeResponse, + this.isImageLoaded, + this.image, }); int? userChatHistoryId; @@ -149,18 +157,21 @@ class UserChatReplyResponse { int? targetUserId; String? targetUserName; FileTypeResponse? fileTypeResponse; + bool? isImageLoaded; + Uint8List? image; factory UserChatReplyResponse.fromJson(Map json) => UserChatReplyResponse( - userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], - chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], - contant: json["contant"] == null ? null : json["contant"], - contantNo: json["contantNo"] == null ? null : json["contantNo"], - fileTypeId: json["fileTypeId"], - createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), - targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], - targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], - fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), - ); + userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], + chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], + contant: json["contant"] == null ? null : json["contant"], + contantNo: json["contantNo"] == null ? null : json["contantNo"], + fileTypeId: json["fileTypeId"], + createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), + targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], + targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], + fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), + isImageLoaded: false, + image: null); Map toJson() => { "userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId, diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 31106c1..95a990f 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -125,6 +125,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { userChatHistory, receiverUID, ); + generateConvId(); } @@ -177,16 +178,14 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { try { StreamedResponse response = await ChatApiClient().uploadMedia(userId, file); if (response.statusCode == 200) { - result = jsonDecode( - await response.stream.bytesToString(), - ); + result = jsonDecode(await response.stream.bytesToString()); } else { result = []; } } catch (e) { - print(e); + throw e; } - ; + return result; } @@ -292,8 +291,22 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { data.first.targetUserName = temp.first.currentUserName; data.first.currentUserId = temp.first.targetUserId; data.first.currentUserName = temp.first.targetUserName; + 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; + } + } + } } + userChatHistory.insert(0, data.first); + var list = [ { "userChatHistoryId": data.first.userChatHistoryId, @@ -389,7 +402,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } Future sendChatToServer( - {required int chatEventId, required fileTypeId, required int targetUserId, required String targetUserName, required chatReplyId, required bool isAttachment, required bool isReply}) async { + {required int chatEventId, + required fileTypeId, + required int targetUserId, + required String targetUserName, + required chatReplyId, + required bool isAttachment, + required bool isReply, + Uint8List? image, + required bool isImageLoaded}) async { Uuid uuid = const Uuid(); var msg = message.text; @@ -416,6 +437,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()), ) : null, + image: image, + isImageLoaded: isImageLoaded, ); userChatHistory.insert(0, data); isFileSelected = false; @@ -442,39 +465,63 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } if (!isFileSelected && !isMsgReply) { + 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); - } + sendChatToServer( + chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false, isImageLoaded: false, image: null); + } // normal Text msg if (isFileSelected && !isMsgReply) { + print("Normal Attachment Msg"); Utils.showLoading(context); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); + logger.d(value); 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); - } + sendChatToServer( + chatEventId: 2, + fileTypeId: getFileType(ext.toString()), + targetUserId: targetUserId, + targetUserName: targetUserName, + isAttachment: true, + chatReplyId: null, + isReply: false, + isImageLoaded: true, + image: selectedFile.readAsBytesSync()); + } // normal attachemnt msg if (!isFileSelected && isMsgReply) { + 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); - } + 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) { + 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, - ); + chatEventId: 2, + fileTypeId: getFileType(ext.toString()), + targetUserId: targetUserId, + targetUserName: targetUserName, + isAttachment: true, + chatReplyId: repliedMsg.first.userChatHistoryId, + isReply: true, + isImageLoaded: true, + image: selectedFile.readAsBytesSync()); } } diff --git a/lib/ui/chat/chat_bubble.dart b/lib/ui/chat/chat_bubble.dart index 8382585..62156c8 100644 --- a/lib/ui/chat/chat_bubble.dart +++ b/lib/ui/chat/chat_bubble.dart @@ -3,161 +3,46 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; 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/colors.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart'; +import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; // todo: @aamir use extension methods, and use correct widgets. class ChatBubble extends StatelessWidget { - const ChatBubble( - {Key? key, - required this.text, - required this.replyText, - required this.isCurrentUser, - required this.isSeen, - required this.isDelivered, - required this.dateTime, - required this.isReplied, - required this.userName, - this.fileTypeID, - this.fileTypeDescription}) - : super(key: key); - final String text; - final String replyText; - final bool isCurrentUser; - final bool isSeen; - final bool isDelivered; + ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key); final String dateTime; - final bool isReplied; - final String userName; - final int? fileTypeID; - final String? fileTypeDescription; + final SingleUserChatModel cItem; + + bool isCurrentUser = false; + bool isSeen = false; + bool isReplied = false; + int? fileTypeID; + + String? fileTypeDescription; + bool isDelivered = false; + String userName = ''; + + void makeAssign() { + isCurrentUser = cItem.currentUserId == AppState().chatDetails!.response!.id ? true : false; + isSeen = cItem.isSeen == true ? true : false; + isReplied = cItem.userChatReplyResponse != null ? true : false; + fileTypeID = cItem.fileTypeId; + fileTypeDescription = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeDescription : ""; + isDelivered = cItem.currentUserId == AppState().chatDetails!.response!.id && cItem.isDelivered == true ? true : false; + userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString(); + } @override Widget build(BuildContext context) { + makeAssign(); return isCurrentUser ? currentUser(context) : receiptUser(context); - - return Padding( - // padding: EdgeInsets.zero, - padding: EdgeInsets.only( - left: isCurrentUser ? 110 : 20, - right: isCurrentUser ? 20 : 110, - bottom: 9, - ), - - child: Align( - alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft, - child: DecoratedBox( - decoration: BoxDecoration( - color: MyColors.white, - gradient: isCurrentUser - ? null - : const LinearGradient( - transform: GradientRotation( - .46, - ), - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ], - ), - borderRadius: BorderRadius.circular( - 10, - ), - ), - child: Padding( - padding: EdgeInsets.only( - top: isReplied ? 8 : 5, - right: 8, - left: 8, - bottom: 5, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - if (isReplied) - ClipRRect( - borderRadius: BorderRadius.circular( - 5.0, - ), - child: Container( - decoration: BoxDecoration( - border: Border( - left: BorderSide( - width: 6, - color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white, - ), - ), - color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), - ), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (userName) - .toText12( - color: MyColors.gradiantStartColor, - isBold: false, - ) - .paddingOnly( - right: 5, - top: 5, - bottom: 0, - left: 5, - ), - replyText - .toText10( - color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), - isBold: false, - maxlines: 4, - ) - .paddingOnly( - right: 5, - top: 5, - bottom: 8, - left: 5, - ), - ], - ), - ), - ], - ), - ), - ), - if (isReplied) 8.height, - text.toText12( - color: isCurrentUser ? MyColors.grey57Color : MyColors.white, - ), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - dateTime.toText12( - color: isCurrentUser ? MyColors.grey41Color.withOpacity(.5) : MyColors.white.withOpacity(0.7), - ), - if (isCurrentUser) 5.width, - if (isCurrentUser) - Icon( - isDelivered ? Icons.done_all : Icons.done_all, - color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, - size: 14, - ), - ], - ), - ], - ), - ), - ), - ), - ); } Widget currentUser(context) { @@ -166,9 +51,7 @@ class ChatBubble extends StatelessWidget { children: [ if (isReplied) ClipRRect( - borderRadius: BorderRadius.circular( - 5.0, - ), + borderRadius: BorderRadius.circular(5.0), child: Container( width: double.infinity, decoration: BoxDecoration( @@ -177,29 +60,49 @@ class ChatBubble extends StatelessWidget { ), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5), - replyText.toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4).paddingOnly(right: 5, top: 5, bottom: 8, left: 5), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5), + (cItem.userChatReplyResponse != null ? cItem.userChatReplyResponse!.contant.toString() : "") + .toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4) + .paddingOnly(right: 5, top: 5, bottom: 8, left: 5), + ], + ).expanded, + if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || + cItem.userChatReplyResponse!.fileTypeId == 3 || + cItem.userChatReplyResponse!.fileTypeId == 4) + ClipRRect( + borderRadius: BorderRadius.circular( + 8, + ), + child: showImage( + isReplyPreview: true, + fileName: cItem.userChatReplyResponse!.contant!, + fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg") + .paddingOnly(left: 10, right: 10, bottom: 16, top: 16), + ) ], - ).expanded, + ), ), ).paddingOnly(right: 5, bottom: 7), - if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) showImage().paddingOnly(right: 5), - if (fileTypeID != 12 || fileTypeID != 4 || fileTypeID != 3) (text).toText12(), + if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) + showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription).paddingOnly(right: 5).onPress(() { + showDialog(context: context, builder: (index) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!)); + }), + cItem.contant!.toText12(), Align( alignment: Alignment.centerRight, child: Row( mainAxisSize: MainAxisSize.min, children: [ - dateTime.toText10(color: MyColors.grey41Color.withOpacity(.5)), - 7.width, - Icon( - isDelivered ? Icons.done_all : Icons.done_all, - color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, - size: 14, + dateTime.toText10( + color: MyColors.grey41Color.withOpacity(.5), ), + 7.width, + Icon(isDelivered ? Icons.done_all : Icons.done_all, color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, size: 14), ], ), ), @@ -227,27 +130,45 @@ class ChatBubble extends StatelessWidget { children: [ if (isReplied) ClipRRect( - borderRadius: BorderRadius.circular( - 5.0, - ), + borderRadius: BorderRadius.circular(5.0), child: Container( width: double.infinity, decoration: BoxDecoration( - border: Border( - left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white), - ), + border: Border(left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white)), color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5), - replyText.toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4).paddingOnly(right: 5, top: 5, bottom: 8, left: 5), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5), + (cItem.userChatReplyResponse != null ? cItem.userChatReplyResponse!.contant.toString() : "") + .toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4) + .paddingOnly(right: 5, top: 5, bottom: 8, left: 5), + ], + ).expanded, + if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || + cItem.userChatReplyResponse!.fileTypeId == 3 || + cItem.userChatReplyResponse!.fileTypeId == 4) + ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: showImage( + isReplyPreview: true, + fileName: cItem.userChatReplyResponse!.contant!, + fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg") + .paddingOnly(left: 10, right: 10, bottom: 16, top: 16), + ) ], - ).expanded, + ), ), ).paddingOnly(right: 5, bottom: 7), - if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) showImage().paddingOnly(right: 5) else (text).toText12(color: Colors.white), + if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) + showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription ?? "image/jpg").paddingOnly(right: 5).onPress(() { + showDialog(context: context, builder: (index) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!)); + }) + else + (cItem.contant! ?? "").toText12(color: Colors.white), Align( alignment: Alignment.centerRight, child: dateTime.toText10( @@ -259,25 +180,41 @@ class ChatBubble extends StatelessWidget { ).paddingOnly(right: MediaQuery.of(context).size.width * 0.3); } - Widget showImage() { - return FutureBuilder( - future: ChatApiClient().downloadURL(fileName: text, fileTypeDescription: fileTypeDescription!), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState != ConnectionState.waiting) { - if (snapshot.data == null) { - return (text).toText12(color: Colors.white); + Widget showImage({required bool isReplyPreview, required String fileName, required String fileTypeDescription}) { + if (cItem.isImageLoaded! && cItem.image != null) { + return Image.memory( + cItem.image!, + height: isReplyPreview ? 32 : 140, + width: isReplyPreview ? 32 : 227, + fit: BoxFit.cover, + ); + } else { + return FutureBuilder( + future: ChatApiClient().downloadURL(fileName: fileName, fileTypeDescription: fileTypeDescription), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState != ConnectionState.waiting) { + if (snapshot.data == null) { + return SizedBox(); + } else { + //data = image; + cItem.image = snapshot.data; + cItem.isImageLoaded = true; + return Image.memory( + snapshot.data, + height: isReplyPreview ? 32 : 140, + width: isReplyPreview ? 32 : 227, + fit: BoxFit.cover, + ); + } } else { - return Image.memory( - snapshot.data, - height: 140, - width: 227, - fit: BoxFit.cover, + return SizedBox( + height: isReplyPreview ? 32 : 140, + width: isReplyPreview ? 32 : 227, + child: const Center(child: CircularProgressIndicator()), ); } - } else { - return const SizedBox(height: 140, width: 227, child: Center(child: CircularProgressIndicator())); - } - }, - ); + }, + ); + } } } diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index 4dc7c35..643cc19 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -12,6 +12,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/call.dart'; +import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/ui/chat/call/chat_outgoing_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart'; @@ -115,16 +116,8 @@ class _ChatDetailScreenState extends State { return SwipeTo( iconColor: MyColors.lightGreenColor, child: ChatBubble( - text: m.userChatHistory[i].contant.toString(), - replyText: m.userChatHistory[i].userChatReplyResponse != null ? m.userChatHistory[i].userChatReplyResponse!.contant.toString() : "", - isSeen: m.userChatHistory[i].isSeen == true ? true : false, - isCurrentUser: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id ? true : false, - isDelivered: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id && m.userChatHistory[i].isDelivered == true ? true : false, dateTime: m.dateFormte(m.userChatHistory[i].createdDate!), - isReplied: m.userChatHistory[i].userChatReplyResponse != null ? true : false, - userName: AppState().chatDetails!.response!.userName == m.userChatHistory[i].currentUserName.toString() ? "You" : m.userChatHistory[i].currentUserName.toString(), - fileTypeID: m.userChatHistory[i].fileTypeId, - fileTypeDescription: m.userChatHistory[i].fileTypeResponse!.fileTypeDescription, + cItem: m.userChatHistory[i], ), onRightSwipe: () { m.chatReply( @@ -159,6 +152,8 @@ class _ChatDetailScreenState extends State { ], ).expanded, 12.width, + if (m.isMsgReply && m.repliedMsg.isNotEmpty) showReplyImage(m.repliedMsg), + 12.width, const Icon(Icons.cancel, size: 23, color: MyColors.grey7BColor).onPress(m.closeMe), ], ), @@ -224,6 +219,22 @@ class _ChatDetailScreenState extends State { ); } + Widget showReplyImage(List data) { + if (data.first.isImageLoaded! && data.first.image != null) { + return ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: Image.memory( + data.first.image!, + height: 43, + width: 43, + fit: BoxFit.cover, + ), + ); + } else { + return const SizedBox(); + } + } + void makeCall({required String callType, required HubConnection con}) async { print("================== Make call Triggered ============================"); Map json = { diff --git a/lib/ui/chat/chat_full_image_preview.dart b/lib/ui/chat/chat_full_image_preview.dart new file mode 100644 index 0000000..2eaa09d --- /dev/null +++ b/lib/ui/chat/chat_full_image_preview.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; + +class ChatImagePreviewScreen extends StatelessWidget { + const ChatImagePreviewScreen({Key? key, required this.imgTitle, required this.img}) : super(key: key); + + final String imgTitle; + final Uint8List img; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.of(context).pop(); + }, + child: Dialog( + backgroundColor: Colors.transparent, + insetPadding: const EdgeInsets.all(10), + child: Stack( + alignment: Alignment.center, + children: [ + Image.memory( + img, + fit: BoxFit.cover, + + ).paddingAll(10), + const Positioned( + right: 0, + top: 0, + child: Icon(Icons.cancel, color: MyColors.redA3Color, size: 35), + ) + ], + ), + ), + ); + } +}