chat voice message implementation

merge-requests/116/head
Aamir Muhammad 3 years ago
parent f60394cc38
commit abd1fa9375

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#F15642;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M88.368,384c-4.096-2.304-6.656-6.912-4.096-12.288l36.72-71.744c3.456-6.784,12.656-7.04,15.856,0
l36.08,71.744c5.248,9.984-10.24,17.904-14.848,7.936l-5.632-11.248h-47.2l-5.52,11.248C97.712,384,92.992,384.912,88.368,384z
M143.392,351.52l-14.464-31.616l-15.744,31.616H143.392z"/>
<path style="fill:#FFFFFF;" d="M189.184,384c-4.096-2.304-6.656-6.912-4.096-12.288l36.704-71.744
c3.456-6.784,12.672-7.04,15.872,0l36.064,71.744c5.248,9.984-10.24,17.904-14.832,7.936l-5.648-11.248h-47.2l-5.504,11.248
C198.512,384,193.776,384.912,189.184,384z M244.192,351.52l-14.448-31.616l-15.728,31.616H244.192z"/>
<path style="fill:#FFFFFF;" d="M282.416,339.088c0-24.688,15.488-45.904,44.912-45.904c11.136,0,19.952,3.312,29.296,11.376
c3.456,3.184,3.84,8.832,0.384,12.4c-3.456,3.056-8.704,2.688-11.76-0.368c-5.248-5.504-10.624-7.024-17.92-7.024
c-19.696,0-29.168,13.936-29.168,29.536c0,15.872,9.344,30.464,29.168,30.464c7.296,0,14.08-2.96,19.952-8.192
c3.968-3.072,9.472-1.552,11.776,1.536c2.048,2.816,3.056,7.536-1.408,12.016c-8.96,8.336-19.696,9.984-30.336,9.984
C296.368,384.912,282.416,363.792,282.416,339.088z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.616,14.4,32,32,32h320c17.6,0,32-14.384,32-32V128L352,0H128z
"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#50BEE8;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M117.184,327.84v47.344c0,5.632-4.592,8.832-9.216,8.832c-4.096,0-7.664-3.2-7.664-8.832v-72.032
c0-6.64,5.632-8.832,7.664-8.832c3.712,0,5.888,2.192,8.064,4.608l28.16,38l29.152-39.408c4.24-5.248,14.592-3.2,14.592,5.632
v72.032c0,5.632-3.6,8.832-7.68,8.832c-4.592,0-8.192-3.2-8.192-8.832V327.84l-21.232,26.88c-4.592,5.632-10.352,5.632-14.576,0
L117.184,327.84z"/>
<path style="fill:#FFFFFF;" d="M210.288,303.152c0-4.224,3.328-8.832,8.704-8.832h29.552c16.64,0,31.616,11.136,31.616,32.496
c0,20.224-14.976,31.472-31.616,31.472h-21.36v16.896c0,5.632-3.584,8.832-8.192,8.832c-4.224,0-8.704-3.2-8.704-8.832V303.152z
M227.168,310.448v31.856h21.36c8.576,0,15.36-7.552,15.36-15.488c0-8.96-6.784-16.368-15.36-16.368L227.168,310.448
L227.168,310.448z"/>
<path style="fill:#FFFFFF;" d="M322.064,311.472h-21.872c-10.736,0-10.096-15.984,0-15.984h39.152c7.792,0,11.376,8.96,5.632,14.72
l-21.232,19.824c15.616-1.152,27.888,10.48,27.888,24.816c0,15.728-11.136,29.168-34.544,29.168
c-10.24,0-20.336-4.224-26.224-13.44c-6.144-9.072,7.024-17.776,13.936-8.832c3.328,4.352,8.704,6.528,14.448,6.528
c7.808,0,15.488-3.328,15.488-13.44c0-13.296-16.256-11.248-25.072-10.352c-10.752,2.048-13.936-9.6-7.664-14.448L322.064,311.472z
"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -96,6 +96,7 @@ class ChatApiClient {
}
return response;
} catch (e) {
getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
throw e;
}
}
@ -119,7 +120,6 @@ class ChatApiClient {
if (!kReleaseMode) {
logger.i("res: " + response.body);
}
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
return favoriteChatUser;
} catch (e) {
@ -128,29 +128,28 @@ class ChatApiClient {
}
}
Future<StreamedResponse> uploadMedia(String userId, File file) async {
Future<Object?> uploadMedia(String userId, File file) async {
print("${ApiConsts.chatMediaImageUploadUrl}upload");
print(AppState().chatDetails!.response!.token);
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}'});
StreamedResponse response = await request.send();
if (!kReleaseMode) {}
return response;
String data = await response.stream.bytesToString();
if (!kReleaseMode) {
logger.i("res: " + data);
}
return jsonDecode(data);
}
// Download File For Chat
Future<Uint8List> downloadURL({required String fileName, required String fileTypeDescription}) async {
print(fileName);
print(fileTypeDescription);
print("${ApiConsts.chatMediaImageUploadUrl}download");
print(AppState().chatDetails!.response!.token);
Response response = await ApiClient().postJsonForResponse(
"${ApiConsts.chatMediaImageUploadUrl}download",
{"fileType": fileTypeDescription, "fileName": fileName, "fileSource": 1},
token: AppState().chatDetails!.response!.token,
);
Uint8List data = Uint8List.fromList(response.bodyBytes);
return data;
}

@ -32,7 +32,8 @@ class SingleUserChatModel {
this.userChatReplyResponse,
this.isReplied,
this.isImageLoaded,
this.image});
this.image,
this.voice});
int? userChatHistoryId;
int? userChatHistoryLineId;
@ -58,6 +59,7 @@ class SingleUserChatModel {
bool? isReplied;
bool? isImageLoaded;
Uint8List? image;
Uint8List? voice;
factory SingleUserChatModel.fromJson(Map<String, dynamic> json) => SingleUserChatModel(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
@ -83,7 +85,8 @@ class SingleUserChatModel {
userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]),
isReplied: false,
isImageLoaded: false,
image: null);
image: null,
voice: null);
Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,
@ -143,19 +146,19 @@ class FileTypeResponse {
}
class UserChatReplyResponse {
UserChatReplyResponse({
this.userChatHistoryId,
this.chatEventId,
this.contant,
this.contantNo,
this.fileTypeId,
this.createdDate,
this.targetUserId,
this.targetUserName,
this.fileTypeResponse,
this.isImageLoaded,
this.image,
});
UserChatReplyResponse(
{this.userChatHistoryId,
this.chatEventId,
this.contant,
this.contantNo,
this.fileTypeId,
this.createdDate,
this.targetUserId,
this.targetUserName,
this.fileTypeResponse,
this.isImageLoaded,
this.image,
this.voice});
int? userChatHistoryId;
int? chatEventId;
@ -168,19 +171,22 @@ class UserChatReplyResponse {
FileTypeResponse? fileTypeResponse;
bool? isImageLoaded;
Uint8List? image;
Uint8List? voice;
factory UserChatReplyResponse.fromJson(Map<String, dynamic> 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"]),
isImageLoaded: false,
image: null);
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,
voice: null,
);
Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,

@ -85,12 +85,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void registerEvents() {
chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus);
// chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
// hubConnection.on("OnSeenChatUserAsync", onChatSeen);
chatHubConnection.on("OnSubmitChatAsync", OnSubmitChatAsync);
chatHubConnection.on("OnUserTypingAsync", onUserTyping);
chatHubConnection.on("OnUserCountAsync", userCountAsync);
// hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
// chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
print("Alll Registered");
}
void getUserRecentChats() async {
@ -107,9 +108,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
if (favUList.response != null && recentChat.response != null) {
favUsersList = favUList.response!;
favUsersList.sort(
(ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(
b.userName!.toLowerCase(),
),
(ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()),
);
for (dynamic user in recentChat.response!) {
for (dynamic favUser in favUList.response!) {
@ -230,16 +229,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<dynamic> uploadAttachments(String userId, File file) async {
dynamic result;
try {
StreamedResponse response = await ChatApiClient().uploadMedia(userId, file);
if (response.statusCode == 200) {
result = jsonDecode(await response.stream.bytesToString());
Object? response = await ChatApiClient().uploadMedia(userId, file);
if (response != null) {
result = response;
} else {
result = [];
}
} catch (e) {
throw e;
}
return result;
}
@ -365,6 +363,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
ChatUser(
id: data.first.currentUserId,
userName: data.first.currentUserName,
email: data.first.currentUserEmail,
unreadMessageCount: 0,
isImageLoading: false,
image: chatImages!.first.profilePicture ?? "",
@ -404,6 +403,28 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
void OnSubmitChatAsync(List<Object?>? parameters) {
logger.d(parameters);
List<SingleUserChatModel> 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!),
@ -454,6 +475,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return 2;
case ".rar":
return 2;
case ".aac":
return 13;
case ".mp3":
return 14;
default:
return 0;
}
@ -487,6 +512,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return "application/octet-stream";
case ".rar":
return "application/octet-stream";
case ".aac":
return "audio/aac";
case ".mp3":
return "audio/mp3";
default:
return "";
}
@ -501,11 +530,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
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,
@ -530,7 +561,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
: null,
image: image,
isImageLoaded: isImageLoaded,
voice: voice,
);
print("Model data---------------------------");
logger.d(data.toJson());
userChatHistory.insert(0, data);
isFileSelected = false;
isMsgReply = false;
@ -569,9 +603,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
isReply: false,
isImageLoaded: true,
image: selectedFile.readAsBytesSync());
} // normal attachemnt msg
}
if (!isFileSelected && isMsgReply) {
print("Normal Text To Text Reply");
if (kDebugMode) {
print("Normal Text To Text Reply");
}
if (message.text == null || message.text.isEmpty) {
return;
}
@ -723,6 +759,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
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";
}
@ -889,6 +929,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
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);
@ -956,11 +997,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
void userTypingInvoke({required int currentUser, required int reciptUser}) async {
logger.d([reciptUser, currentUser]);
await chatHubConnection.invoke("UserTypingAsync", args: [reciptUser, currentUser]);
}
// Audio Recoding Work
// Audio Recoding Work
Timer? _timer;
int _recodeDuration = 0;
bool isRecoding = false;
@ -972,13 +1012,18 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
late RecorderController recorderController;
late PlayerController playerController;
//////// Audio Recoding Work ////////////////////
//////// Audio Recoding Work ////////////////////
Future<void> 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()
@ -986,6 +1031,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 6000
..updateFrequency = const Duration(milliseconds: 100)
..bitRate = 18000;
playerController = PlayerController();
}
@ -1014,15 +1060,23 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
}
void _startTimer() {
Future<void> _startTimer() async {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) async {
_recodeDuration++;
buildTimer();
notifyListeners();
if (_recodeDuration <= 59) {
applyCounter();
} else {
pauseRecoding();
}
});
}
void applyCounter() {
buildTimer();
notifyListeners();
}
Future<void> pauseRecoding() async {
isPause = true;
isPlaying = true;
@ -1030,27 +1084,16 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
path = await recorderController.stop(false);
File file = File(path!);
file.readAsBytesSync();
path = file.path;
await playerController.preparePlayer(file.path, 1.0);
// var tempDuration = _recodeDuration;
// _recodeDuration = tempDuration;
_timer?.cancel();
notifyListeners();
}
void resumeRecoding() {
isPause = false;
isPlaying = false;
isRecoding = true;
recorderController.record(path);
_startTimer();
}
Future<void> deleteRecoding() async {
_recodeDuration = 0;
_timer?.cancel();
// path = await recorderController.stop(true);
recorderController.reset();
print(path);
recorderController.stop(true);
if (path != null && path!.isNotEmpty) {
File delFile = File(path!);
double fileSizeInKB = delFile.lengthSync() / 1024;
@ -1095,8 +1138,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
void sendVoiceMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async {
//recorderController.pause();
path = await recorderController.stop(false);
if (!isPause) {
path = await recorderController.stop(false);
}
if (kDebugMode) {
print(path);
}
@ -1110,17 +1154,156 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), voiceFile);
logger.d(value);
String? ext = getFileExtension(voiceFile.path);
logger.d(voiceFile.path!.split("/").last);
Utils.hideLoading(context);
// sendChatToServer(
// chatEventId: 2,
// fileTypeId: getFileType(ext.toString()),
// targetUserId: targetUserId,
// targetUserName: targetUserName,
// isAttachment: true,
// chatReplyId: null,
// isReply: false,
// isImageLoaded: true,
// image: voiceFile.readAsBytesSync());
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: false,
voicFile: voiceFile,
);
notifyListeners();
}
Future<void> 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: <Object>[json.decode(chatData)]);
if (searchedChats != null) {
dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) {
List<String> emails = [];
emails.add(await EmailImageEncryption().encrypt(val: userEmail!));
List<ChatUserImageModel> 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<String> emails = [];
emails.add(await EmailImageEncryption().encrypt(val: userEmail!));
List<ChatUserImageModel> 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);
logger.d(path);
File file = File(path!);
file.readAsBytesSync();
Utils.hideLoading(context);
await playerController.preparePlayer(file.path, 1.0);
notifyListeners();
playerController.startPlayer(finishMode: FinishMode.pause);
} catch (e) {
Utils.showToast("Cannot open file.");
}
}
Future<String> 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,
// );
}

@ -20,8 +20,6 @@ import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:open_file/open_file.dart';
import 'package:provider/provider.dart';
// todo: @aamir use extension methods, and use correct widgets.
class ChatBubble extends StatelessWidget {
ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key);
final String dateTime;
@ -102,7 +100,8 @@ class ChatBubble extends StatelessWidget {
],
),
),
).paddingOnly(bottom: 7),
).paddingOnly(bottom: 7).onPress(() {
}),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
ClipRRect(
borderRadius: BorderRadius.circular(5.0),
@ -117,18 +116,23 @@ class ChatBubble extends StatelessWidget {
);
}),
),
).paddingOnly(bottom: 4)
).paddingOnly(bottom: 4),
if (fileTypeID == 13)
currentWaveBubble(context).onPress(() {
data.playVoice(context, data: cItem);
})
else
Row(
children: [
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
// || fileTypeID == 2
// || fileTypeID == 2
)
SvgPicture.asset(data.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 0, right: 10),
(cItem.contant ?? "").toText12().expanded,
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
//|| fileTypeID == 2
) const Icon(Icons.remove_red_eye, size: 16)
//|| fileTypeID == 2
)
const Icon(Icons.remove_red_eye, size: 16)
],
),
Align(
@ -157,10 +161,7 @@ class ChatBubble extends StatelessWidget {
transform: GradientRotation(.83),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
],
colors: <Color>[MyColors.gradiantEndColor, MyColors.gradiantStartColor],
),
),
child: Column(
@ -203,7 +204,8 @@ class ChatBubble extends StatelessWidget {
],
),
),
).paddingOnly(bottom: 7),
).paddingOnly(bottom: 7).onPress(() {
}),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
ClipRRect(
borderRadius: BorderRadius.circular(5.0),
@ -218,7 +220,11 @@ class ChatBubble extends StatelessWidget {
);
}),
),
).paddingOnly(bottom: 4)
).paddingOnly(bottom: 4),
if (fileTypeID == 13)
recipetWaveBubble(context).onPress(() {
data.playVoice(context, data: cItem);
})
else
Row(
children: [
@ -283,6 +289,102 @@ class ChatBubble extends StatelessWidget {
);
}
}
Widget currentWaveBubble(BuildContext context) {
return Container(
margin: const EdgeInsets.all(0),
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),
// gradient: const LinearGradient(
// transform: GradientRotation(.83),
// begin: Alignment.topRight,
// end: Alignment.bottomLeft,
// colors: <Color>[
// MyColors.gradiantEndColor,
// MyColors.gradiantStartColor,
// ],
// ),
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
const Icon(
Icons.play_arrow,
color: MyColors.lightGreenColor,
).paddingAll(10),
AudioFileWaveforms(
size: Size(MediaQuery.of(context).size.width * 0.3, 10),
playerController: data.playerController,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
enableSeekGesture: true,
density: 1,
playerWaveStyle: const PlayerWaveStyle(
fixedWaveColor: Colors.white,
liveWaveColor: MyColors.greenColor,
showTop: true,
showBottom: true,
waveCap: StrokeCap.round,
seekLineThickness: 2,
visualizerHeight: 4,
backgroundColor: Colors.transparent,
),
).expanded,
],
),
).circle(5);
}
Widget recipetWaveBubble(BuildContext context) {
return Container(
margin: const EdgeInsets.all(0),
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),
// gradient: const LinearGradient(
// transform: GradientRotation(.83),
// begin: Alignment.topRight,
// end: Alignment.bottomLeft,
// colors: <Color>[
// MyColors.gradiantEndColor,
// MyColors.gradiantStartColor,
// ],
// ),
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
const Icon(
Icons.play_arrow,
color: MyColors.white,
).paddingAll(10),
AudioFileWaveforms(
size: Size(MediaQuery.of(context).size.width * 0.3, 10),
playerController: data.playerController,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
enableSeekGesture: true,
density: 1,
playerWaveStyle: const PlayerWaveStyle(
fixedWaveColor: Colors.white,
liveWaveColor: MyColors.greenColor,
showTop: true,
showBottom: true,
waveCap: StrokeCap.round,
seekLineThickness: 2,
visualizerHeight: 4,
backgroundColor: Colors.transparent,
),
).expanded,
],
),
).circle(5);
}
}
class WaveBubble extends StatelessWidget {
@ -329,15 +431,15 @@ class WaveBubble extends StatelessWidget {
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
enableSeekGesture: true,
density: 2,
density: 1,
playerWaveStyle: const PlayerWaveStyle(
fixedWaveColor: Colors.white,
liveWaveColor:MyColors.greenColor,
liveWaveColor: MyColors.greenColor,
showTop: true,
showBottom: true,
waveCap: StrokeCap.round,
seekLineThickness: 2,
visualizerHeight: 5,
visualizerHeight: 4,
backgroundColor: Colors.transparent,
),
),

@ -144,7 +144,13 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
);
},
).onPress(() async {
if (m.userChatHistory[i].fileTypeResponse != null) {
logger.d(m.userChatHistory[i].toJson());
if (m.userChatHistory[i].fileTypeResponse != null && m.userChatHistory[i].fileTypeId! == 1 ||
m.userChatHistory[i].fileTypeId! == 5 ||
m.userChatHistory[i].fileTypeId! == 7 ||
m.userChatHistory[i].fileTypeId! == 6 ||
m.userChatHistory[i].fileTypeId! == 8 ||
m.userChatHistory[i].fileTypeId! == 2) {
m.getChatMedia(context,
fileTypeName: m.userChatHistory[i].fileTypeResponse!.fileTypeName ?? "", fileTypeID: m.userChatHistory[i].fileTypeId!, fileName: m.userChatHistory[i].contant!);
}

Loading…
Cancel
Save