Chat Error Handling & Voice

sultan-dev
Aamir Muhammad 2 years ago
parent f62253374d
commit e40cd2c5c8

@ -33,11 +33,12 @@ class ChatApiClient {
},
);
if (!kReleaseMode) {
print("Status Code is ================" + response.statusCode.toString());
logger.i("res: " + response.body);
}
if (response.statusCode == 200) {
userLoginResponse = user.userAutoLoginModelFromJson(response.body);
} else if (response.statusCode == 504) {
} else if (response.statusCode == 501 || response.statusCode == 502 || response.statusCode == 503 || response.statusCode == 504) {
getUserLoginToken();
} else {
userLoginResponse = user.userAutoLoginModelFromJson(response.body);
@ -141,11 +142,16 @@ class ChatApiClient {
// 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;
}
@ -159,11 +165,10 @@ class ChatApiClient {
);
if (!kReleaseMode) {
logger.i("res: " + response.body);
print("Images Status Coe is ============== " + response.statusCode.toString());
}
if (response.statusCode == 200) {
imagesData = chatUserImageModelFromJson(response.body);
} else if (response.statusCode == 504 || response.statusCode == 500) {
} else if (response.statusCode == 500 || response.statusCode == 504) {
getUsersImages(encryptedEmails: encryptedEmails);
} else {
Utils.showToast("Something went wrong while loading images");

@ -3,8 +3,8 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts {
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
// static String baseUrl = "https://hmgwebservices.com"; // Live server
//static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";

@ -2,18 +2,20 @@ 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';
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/app_permissions.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_count_conversation_model.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';
@ -23,6 +25,7 @@ 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';
@ -37,7 +40,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
bool isLoading = true;
bool isChatScreenActive = false;
int receiverID = 0;
late File selectedFile;
bool isFileSelected = false;
String sFileType = "";
@ -53,9 +55,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> getUserAutoLoginToken() async {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
print("======================================= Chat Login Token Check =====================================");
logger.d(userLoginResponse.toJson());
print("======================================= Chat Login Token Check =====================================");
if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse;
} else {
AppState().setchatUserDetails = userLoginResponse;
Utils.showToast(
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
);
@ -903,7 +909,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
void setMsgTune() async {
AudioPlayer player = AudioPlayer();
JustAudio.AudioPlayer player = JustAudio.AudioPlayer();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
@ -949,4 +955,133 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
logger.d([reciptUser, currentUser]);
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<void> initAudio() async {
appDirectory = await getApplicationDocumentsDirectory();
path = "${appDirectory.path}/${AppState().chatDetails!.response!.id}-${DateTime.now().microsecondsSinceEpoch}.aac";
recorderController = RecorderController()
..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 8000
..bitRate = 32000;
playerController = PlayerController();
}
void disposeAudio() {
isRecoding = false;
isPlaying = false;
isPause = false;
recorderController.dispose();
playerController.dispose();
}
void startRecoding() async {
PermissionStatus status = await Permission.microphone.request();
print(status);
if (status.isDenied == true) {
startRecoding();
} else {
recorderController.reset();
await recorderController.record(path);
_recodeDuration = 0;
_startTimer();
isRecoding = !isRecoding;
notifyListeners();
}
}
void _startTimer() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
_recodeDuration++;
buildTimer();
notifyListeners();
});
}
Future<void> pauseRecoding() async {
isPause = true;
isPlaying = true;
recorderController.pause();
path = await recorderController.stop(false);
print(path);
File file = File(path!);
file.readAsBytesSync();
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 {
print(path);
_recodeDuration = 0;
_timer?.cancel();
// path = await recorderController.stop(false);
recorderController.reset();
print(path);
if (path != null && path!.isNotEmpty) {
File delFile = File(path!);
double fileSizeInKB = delFile.lengthSync() / 1024;
double fileSizeInMB = fileSizeInKB / 1024;
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.stop);
}
void playOrPause() async {
playerController.playerState == PlayerState.playing ? await playerController.pausePlayer() : playRecoding();
notifyListeners();
}
}

@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/api/api_client.dart';
@ -120,10 +121,14 @@ class ChatBubble extends StatelessWidget {
else
Row(
children: [
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2)
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
// || 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)
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
//|| fileTypeID == 2
) const Icon(Icons.remove_red_eye, size: 16)
],
),
Align(
@ -217,10 +222,15 @@ class ChatBubble extends StatelessWidget {
else
Row(
children: [
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2)
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
// || 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(color: Colors.white).expanded,
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) const Icon(Icons.remove_red_eye, color: Colors.white, size: 16)
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
//|| fileTypeID == 2
)
const Icon(Icons.remove_red_eye, color: Colors.white, size: 16)
],
),
Align(
@ -274,3 +284,63 @@ class ChatBubble extends StatelessWidget {
}
}
}
class WaveBubble extends StatelessWidget {
final PlayerController playerController;
final VoidCallback onTap;
final bool isPlaying;
const WaveBubble({
Key? key,
required this.playerController,
required this.onTap,
required this.isPlaying,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
gradient: const LinearGradient(
transform: GradientRotation(.83),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
],
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: onTap,
icon: Icon(isPlaying ? Icons.stop : Icons.play_arrow),
color: Colors.white,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
AudioFileWaveforms(
size: Size(MediaQuery.of(context).size.width / 2, 10),
playerController: playerController,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
playerWaveStyle: const PlayerWaveStyle(
fixedWaveColor: Colors.white,
liveWaveColor:MyColors.lightGreenColor,
showTop: true,
showBottom: true,
waveCap: StrokeCap.round,
seekLineThickness: 3,
visualizerHeight: 6,
backgroundColor: Colors.transparent
),
),
],
),
);
}
}

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -62,6 +63,12 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
_rc.loadComplete();
}
@override
void dispose() {
data.disposeAudio();
super.dispose();
}
@override
Widget build(BuildContext context) {
params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams;
@ -73,6 +80,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
loadMore: false,
isNewChat: params!.isNewChat!,
);
data.initAudio();
}
return Scaffold(
@ -181,62 +189,140 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
height: 1,
color: MyColors.lightGreyEFColor,
),
TextField(
controller: m.message,
decoration: InputDecoration(
hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(),
hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
filled: true,
fillColor: MyColors.white,
contentPadding: const EdgeInsets.only(
left: 21,
top: 20,
bottom: 20,
),
prefixIconConstraints: const BoxConstraints(),
prefixIcon: m.sFileType.isNotEmpty
? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15)
: null,
suffixIcon: SizedBox(
width: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center, // added line
children: <Widget>[
if (m.sFileType.isNotEmpty)
Row(
children: <Widget>[
const Icon(Icons.cancel, size: 15, color: MyColors.redA3Color).paddingOnly(right: 5),
("Clear").toText11(color: MyColors.redA3Color, isUnderLine: true).paddingOnly(left: 0),
],
).onPress(() => m.removeAttachment()).paddingOnly(right: 25),
if (m.sFileType.isEmpty)
RotationTransition(
turns: const AlwaysStoppedAnimation(45 / 360),
child: const Icon(Icons.attach_file_rounded, size: 26, color: MyColors.grey3AColor).onPress(
() => m.selectImageToUpload(context),
if (m.isRecoding)
Column(
children: <Widget>[
Row(
children: [
Text(m.buildTimer()).paddingAll(10),
if (m.isRecoding && m.isPlaying)
WaveBubble(
playerController: m.playerController,
onTap: () {
m.playOrPause();
},
isPlaying: m.playerController.playerState == PlayerState.playing)
.expanded
else
AudioWaveforms(
waveStyle: const WaveStyle(
waveColor: MyColors.lightGreenColor,
middleLineColor: Colors.transparent,
extendWaveform: true,
showBottom: true,
showTop: true,
waveThickness: 2,
showMiddleLine: false,
middleLineThickness: 0,
),
).paddingOnly(right: 25),
SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26).onPress(
padding: const EdgeInsets.all(5),
shouldCalculateScrolledPosition: false,
margin: EdgeInsets.zero,
size: const Size(double.infinity, 30.0),
recorderController: m.recorderController,
backgroundColor: Colors.white,
).expanded,
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(
Icons.delete_outlined,
size: 26,
color: MyColors.lightGreenColor,
).paddingAll(10).onPress(() {
m.deleteRecoding();
}),
if (m.isPause)
const Icon(
Icons.mic,
size: 26,
color: MyColors.lightGreenColor,
).paddingOnly(right: 15).onPress(() {
m.resumeRecoding();
}),
if (!m.isPause)
const Icon(
Icons.pause_circle_outline,
size: 26,
color: MyColors.lightGreenColor,
).paddingOnly(right: 15).onPress(() {
m.pauseRecoding();
}),
SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26)
.onPress(
() => m.sendChatMessage(context,
targetUserId: params!.chatUser!.id!,
userStatus: params!.chatUser!.userStatus ?? 0,
userEmail: params!.chatUser!.email!,
targetUserName: params!.chatUser!.userName!),
)
.paddingOnly(right: 21),
],
),
],
).objectContainerView(disablePadding: true, radius: 0),
if (!m.isRecoding)
Row(
children: [
TextField(
controller: m.message,
decoration: InputDecoration(
hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(),
hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14),
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
filled: true,
fillColor: MyColors.white,
contentPadding: const EdgeInsets.only(
left: 21,
top: 20,
bottom: 20,
),
prefixIconConstraints: const BoxConstraints(),
prefixIcon: m.sFileType.isNotEmpty
? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15)
: null,
),
onChanged: (val) {
m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!);
},
).expanded,
if (m.sFileType.isNotEmpty)
Row(
children: <Widget>[
const Icon(Icons.cancel, size: 15, color: MyColors.redA3Color).paddingOnly(right: 5),
("Clear").toText11(color: MyColors.redA3Color, isUnderLine: true).paddingOnly(left: 0),
],
).onPress(() => m.removeAttachment()).paddingOnly(right: 15),
if (m.sFileType.isEmpty)
RotationTransition(
turns: const AlwaysStoppedAnimation(45 / 360),
child: const Icon(Icons.attach_file_rounded, size: 26, color: MyColors.grey3AColor).onPress(
() => m.selectImageToUpload(context),
),
).paddingOnly(right: 15),
Icon(
Icons.mic,
color: MyColors.lightGreenColor,
).paddingOnly(right: 15).onPress(() {
m.startRecoding();
}),
SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26)
.onPress(
() => m.sendChatMessage(context,
targetUserId: params!.chatUser!.id!,
userStatus: params!.chatUser!.userStatus ?? 0,
userEmail: params!.chatUser!.email!,
targetUserName: params!.chatUser!.userName!),
),
],
),
).paddingOnly(right: 21),
),
onChanged: (val) {
m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!);
},
),
)
.paddingOnly(right: 21),
],
).objectContainerView(disablePadding: true, radius: 0),
],
));
},

@ -92,6 +92,10 @@ dependencies:
swipe_to: ^1.0.2
flutter_webrtc: ^0.9.16
camera: ^0.10.0+4
#Chat Voice Message Recoding & Play
record: ^4.4.3
audio_waveforms: ^0.1.5+1
# animated_text_kit: ^4.2.2
#Encryption

Loading…
Cancel
Save