Merge branch 'development_aamir' into 'master'

Chat Fixes & Scroll & IOS Fix. Voice Chat Scenarios --- In progress.

See merge request Cloud_Solution/mohemm-flutter-app!116
merge-requests/121/merge
Sikander Saleem 3 years ago
commit 37fe3f46b0

@ -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

@ -33,7 +33,6 @@ class ChatApiClient {
}, },
); );
if (!kReleaseMode) { if (!kReleaseMode) {
print("Status Code is ================" + response.statusCode.toString());
logger.i("res: " + response.body); logger.i("res: " + response.body);
} }
if (response.statusCode == 200) { if (response.statusCode == 200) {
@ -58,6 +57,7 @@ class ChatApiClient {
return List<ChatUser>.from(json.decode(response.body).map((x) => ChatUser.fromJson(x))); return List<ChatUser>.from(json.decode(response.body).map((x) => ChatUser.fromJson(x)));
} }
//Get User Recent Chats
Future<ChatUserModel> getRecentChats() async { Future<ChatUserModel> getRecentChats() async {
try { try {
Response response = await ApiClient().getJsonForResponse( Response response = await ApiClient().getJsonForResponse(
@ -75,6 +75,7 @@ class ChatApiClient {
} }
} }
// Get Favorite Users
Future<ChatUserModel> getFavUsers() async { Future<ChatUserModel> getFavUsers() async {
Response favRes = await ApiClient().getJsonForResponse( Response favRes = await ApiClient().getJsonForResponse(
"${ApiConsts.chatFavUser}getFavUserById/${AppState().chatDetails!.response!.id}", "${ApiConsts.chatFavUser}getFavUserById/${AppState().chatDetails!.response!.id}",
@ -86,6 +87,7 @@ class ChatApiClient {
return ChatUserModel.fromJson(json.decode(favRes.body)); return ChatUserModel.fromJson(json.decode(favRes.body));
} }
//Get User Chat History
Future<Response> getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false, required int paginationVal}) async { Future<Response> getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false, required int paginationVal}) async {
try { try {
Response response = await ApiClient().getJsonForResponse( Response response = await ApiClient().getJsonForResponse(
@ -97,10 +99,12 @@ class ChatApiClient {
} }
return response; return response;
} catch (e) { } catch (e) {
getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
throw e; throw e;
} }
} }
//Favorite Users
Future<fav.FavoriteChatUser> favUser({required int userID, required int targetUserID}) async { Future<fav.FavoriteChatUser> favUser({required int userID, required int targetUserID}) async {
Response response = await ApiClient().postJsonForResponse("${ApiConsts.chatFavUser}addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token); Response response = await ApiClient().postJsonForResponse("${ApiConsts.chatFavUser}addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token);
if (!kReleaseMode) { if (!kReleaseMode) {
@ -110,6 +114,7 @@ class ChatApiClient {
return favoriteChatUser; return favoriteChatUser;
} }
//UnFavorite Users
Future<fav.FavoriteChatUser> unFavUser({required int userID, required int targetUserID}) async { Future<fav.FavoriteChatUser> unFavUser({required int userID, required int targetUserID}) async {
try { try {
Response response = await ApiClient().postJsonForResponse( Response response = await ApiClient().postJsonForResponse(
@ -120,7 +125,6 @@ class ChatApiClient {
if (!kReleaseMode) { if (!kReleaseMode) {
logger.i("res: " + response.body); logger.i("res: " + response.body);
} }
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
return favoriteChatUser; return favoriteChatUser;
} catch (e) { } catch (e) {
@ -129,33 +133,37 @@ class ChatApiClient {
} }
} }
Future<StreamedResponse> uploadMedia(String userId, File file) async { // Upload Chat Media
Future<Object?> uploadMedia(String userId, File file) async {
if (kDebugMode) {
print("${ApiConsts.chatMediaImageUploadUrl}upload");
print(AppState().chatDetails!.response!.token);
}
dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatMediaImageUploadUrl}upload')); dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatMediaImageUploadUrl}upload'));
request.fields.addAll({'userId': userId, 'fileSource': '1'}); request.fields.addAll({'userId': userId, 'fileSource': '1'});
request.files.add(await MultipartFile.fromPath('files', file.path)); request.files.add(await MultipartFile.fromPath('files', file.path));
request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'}); request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'});
StreamedResponse response = await request.send(); StreamedResponse response = await request.send();
if (!kReleaseMode) {} String data = await response.stream.bytesToString();
return response; if (!kReleaseMode) {
logger.i("res: " + data);
}
return jsonDecode(data);
} }
// Download File For Chat // Download File For Chat
Future<Uint8List> downloadURL({required String fileName, required String fileTypeDescription}) async { 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( Response response = await ApiClient().postJsonForResponse(
"${ApiConsts.chatMediaImageUploadUrl}download", "${ApiConsts.chatMediaImageUploadUrl}download",
{"fileType": fileTypeDescription, "fileName": fileName, "fileSource": 1}, {"fileType": fileTypeDescription, "fileName": fileName, "fileSource": 1},
token: AppState().chatDetails!.response!.token, token: AppState().chatDetails!.response!.token,
); );
Uint8List data = Uint8List.fromList(response.bodyBytes); Uint8List data = Uint8List.fromList(response.bodyBytes);
return data; return data;
} }
//Get Chat Users & Favorite Images
Future<List<ChatUserImageModel>> getUsersImages({required List<String> encryptedEmails}) async { Future<List<ChatUserImageModel>> getUsersImages({required List<String> encryptedEmails}) async {
List<ChatUserImageModel> imagesData = []; List<ChatUserImageModel> imagesData = [];
Response response = await ApiClient().postJsonForResponse( Response response = await ApiClient().postJsonForResponse(

File diff suppressed because one or more lines are too long

@ -1,7 +1,8 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:just_audio/just_audio.dart';
List<SingleUserChatModel> singleUserChatModelFromJson(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x))); List<SingleUserChatModel> singleUserChatModelFromJson(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x)));
@ -32,7 +33,9 @@ class SingleUserChatModel {
this.userChatReplyResponse, this.userChatReplyResponse,
this.isReplied, this.isReplied,
this.isImageLoaded, this.isImageLoaded,
this.image}); this.image,
this.voice,
this.voiceController});
int? userChatHistoryId; int? userChatHistoryId;
int? userChatHistoryLineId; int? userChatHistoryLineId;
@ -58,6 +61,8 @@ class SingleUserChatModel {
bool? isReplied; bool? isReplied;
bool? isImageLoaded; bool? isImageLoaded;
Uint8List? image; Uint8List? image;
File? voice;
AudioPlayer? voiceController;
factory SingleUserChatModel.fromJson(Map<String, dynamic> json) => SingleUserChatModel( factory SingleUserChatModel.fromJson(Map<String, dynamic> json) => SingleUserChatModel(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
@ -83,7 +88,9 @@ class SingleUserChatModel {
userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]), userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]),
isReplied: false, isReplied: false,
isImageLoaded: false, isImageLoaded: false,
image: null); image: null,
voice: null,
voiceController: json["fileTypeId"] == 13 ? AudioPlayer() : null);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId, "userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,
@ -143,19 +150,19 @@ class FileTypeResponse {
} }
class UserChatReplyResponse { class UserChatReplyResponse {
UserChatReplyResponse({ UserChatReplyResponse(
this.userChatHistoryId, {this.userChatHistoryId,
this.chatEventId, this.chatEventId,
this.contant, this.contant,
this.contantNo, this.contantNo,
this.fileTypeId, this.fileTypeId,
this.createdDate, this.createdDate,
this.targetUserId, this.targetUserId,
this.targetUserName, this.targetUserName,
this.fileTypeResponse, this.fileTypeResponse,
this.isImageLoaded, this.isImageLoaded,
this.image, this.image,
}); this.voice});
int? userChatHistoryId; int? userChatHistoryId;
int? chatEventId; int? chatEventId;
@ -168,19 +175,22 @@ class UserChatReplyResponse {
FileTypeResponse? fileTypeResponse; FileTypeResponse? fileTypeResponse;
bool? isImageLoaded; bool? isImageLoaded;
Uint8List? image; Uint8List? image;
Uint8List? voice;
factory UserChatReplyResponse.fromJson(Map<String, dynamic> json) => UserChatReplyResponse( factory UserChatReplyResponse.fromJson(Map<String, dynamic> json) => UserChatReplyResponse(
userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"],
chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], chatEventId: json["chatEventId"] == null ? null : json["chatEventId"],
contant: json["contant"] == null ? null : json["contant"], contant: json["contant"] == null ? null : json["contant"],
contantNo: json["contantNo"] == null ? null : json["contantNo"], contantNo: json["contantNo"] == null ? null : json["contantNo"],
fileTypeId: json["fileTypeId"], fileTypeId: json["fileTypeId"],
createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]),
targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], targetUserId: json["targetUserId"] == null ? null : json["targetUserId"],
targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], targetUserName: json["targetUserName"] == null ? null : json["targetUserName"],
fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]),
isImageLoaded: false, isImageLoaded: false,
image: null); image: null,
voice: null,
);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId, "userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId,

@ -10,7 +10,9 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:just_audio/just_audio.dart' as JustAudio; import 'package:just_audio/just_audio.dart' as JustAudio;
import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/api/my_team/my_team_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.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/consts.dart';
import 'package:mohem_flutter_app/classes/encryption.dart'; import 'package:mohem_flutter_app/classes/encryption.dart';
@ -21,6 +23,7 @@ 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_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/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/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
@ -32,32 +35,42 @@ import 'package:uuid/uuid.dart';
class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
ScrollController scrollController = ScrollController(); ScrollController scrollController = ScrollController();
TextEditingController message = TextEditingController(); TextEditingController message = TextEditingController();
TextEditingController search = TextEditingController(); TextEditingController search = TextEditingController();
List<SingleUserChatModel> userChatHistory = []; List<SingleUserChatModel> userChatHistory = [], repliedMsg = [];
List<ChatUser>? pChatHistory, searchedChats; List<ChatUser>? pChatHistory, searchedChats;
String chatCID = ''; String chatCID = '';
bool isLoading = true; bool isLoading = true;
bool isChatScreenActive = false; bool isChatScreenActive = false;
int receiverID = 0; int receiverID = 0;
late File selectedFile; late File selectedFile;
bool isFileSelected = false;
String sFileType = ""; String sFileType = "";
bool isMsgReply = false;
List<SingleUserChatModel> repliedMsg = [];
List<ChatUser> favUsersList = []; List<ChatUser> favUsersList = [];
int paginationVal = 0; int paginationVal = 0;
bool currentUserTyping = false;
int? cTypingUserId = 0; int? cTypingUserId = 0;
bool isTextMsg = false, isReplyMsg = false, isAttachmentMsg = false, isVoiceMsg = false;
// 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;
List<GetEmployeeSubordinatesList> getEmployeeSubordinatesList = [];
List<ChatUser> teamMembersList = [];
//Chat Home Page Counter //Chat Home Page Counter
int chatUConvCounter = 0; int chatUConvCounter = 0;
Future<void> getUserAutoLoginToken() async { Future<void> getUserAutoLoginToken() async {
userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
print("======================================= Chat Login Token Check =====================================");
logger.d(userLoginResponse.toJson());
print("======================================= Chat Login Token Check =====================================");
if (userLoginResponse.response != null) { if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse; AppState().setchatUserDetails = userLoginResponse;
} else { } else {
@ -69,9 +82,12 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
Future<void> buildHubConnection() async { Future<void> buildHubConnection() async {
chatHubConnection = await getHubConnection(); chatHubConnection = await getHubConnection();
await chatHubConnection.start(); await chatHubConnection.start();
print("Startedddddddd"); if (kDebugMode) {
logger.i("Hub Conn: Startedddddddd");
}
chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion); chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion);
} }
@ -88,12 +104,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void registerEvents() { void registerEvents() {
chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus); chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus);
// chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); // chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
// hubConnection.on("OnSeenChatUserAsync", onChatSeen); chatHubConnection.on("OnSubmitChatAsync", OnSubmitChatAsync);
chatHubConnection.on("OnUserTypingAsync", onUserTyping); chatHubConnection.on("OnUserTypingAsync", onUserTyping);
chatHubConnection.on("OnUserCountAsync", userCountAsync); chatHubConnection.on("OnUserCountAsync", userCountAsync);
// hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); // chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow);
chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered);
chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus);
if (kDebugMode) {
logger.i("All listeners registered");
}
} }
void getUserRecentChats() async { void getUserRecentChats() async {
@ -110,9 +129,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
if (favUList.response != null && recentChat.response != null) { if (favUList.response != null && recentChat.response != null) {
favUsersList = favUList.response!; favUsersList = favUList.response!;
favUsersList.sort( favUsersList.sort(
(ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo( (ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo(b.userName!.toLowerCase()),
b.userName!.toLowerCase(),
),
); );
for (dynamic user in recentChat.response!) { for (dynamic user in recentChat.response!) {
for (dynamic favUser in favUList.response!) { for (dynamic favUser in favUList.response!) {
@ -182,34 +199,32 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void markRead(List<SingleUserChatModel> data, int receiverID) { void markRead(List<SingleUserChatModel> data, int receiverID) {
if (data != null) { for (SingleUserChatModel element in data!) {
for (SingleUserChatModel element in data!) { if (AppState().chatDetails!.response!.id! == element.targetUserId) {
if (AppState().chatDetails!.response!.id! == element.targetUserId) { if (element.isSeen != null) {
if (element.isSeen != null) { if (!element.isSeen!) {
if (!element.isSeen!) { element.isSeen = true;
element.isSeen = true; dynamic data = [
dynamic data = [ {
{ "userChatHistoryId": element.userChatHistoryId,
"userChatHistoryId": element.userChatHistoryId, "TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId,
"TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId, "isDelivered": true,
"isDelivered": true, "isSeen": true,
"isSeen": true, }
} ];
]; updateUserChatHistoryStatusAsync(data);
updateUserChatHistoryStatusAsync(data); notifyListeners();
notifyListeners();
}
} }
for (ChatUser element in searchedChats!) { }
if (element.id == receiverID) { for (ChatUser element in searchedChats!) {
element.unreadMessageCount = 0; if (element.id == receiverID) {
chatUConvCounter = 0; element.unreadMessageCount = 0;
} chatUConvCounter = 0;
} }
} }
} }
notifyListeners();
} }
notifyListeners();
} }
void updateUserChatHistoryStatusAsync(List data) { void updateUserChatHistoryStatusAsync(List data) {
@ -233,16 +248,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<dynamic> uploadAttachments(String userId, File file) async { Future<dynamic> uploadAttachments(String userId, File file) async {
dynamic result; dynamic result;
try { try {
StreamedResponse response = await ChatApiClient().uploadMedia(userId, file); Object? response = await ChatApiClient().uploadMedia(userId, file);
if (response.statusCode == 200) { if (response != null) {
result = jsonDecode(await response.stream.bytesToString()); result = response;
} else { } else {
result = []; result = [];
} }
} catch (e) { } catch (e) {
throw e; throw e;
} }
return result; return result;
} }
@ -284,7 +298,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void updateChatHistoryWindow(List<Object?>? args) { void updateChatHistoryWindow(List<Object?>? args) {
dynamic items = args!.toList(); dynamic items = args!.toList();
print("---------------------------------Update Chat History Windows Async -------------------------------------"); if (kDebugMode) {
logger.i("---------------------------------Update Chat History Windows Async -------------------------------------");
}
logger.d(items); logger.d(items);
// for (var user in searchedChats!) { // for (var user in searchedChats!) {
// if (user.id == items.first["id"]) { // if (user.id == items.first["id"]) {
@ -368,6 +384,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
ChatUser( ChatUser(
id: data.first.currentUserId, id: data.first.currentUserId,
userName: data.first.currentUserName, userName: data.first.currentUserName,
email: data.first.currentUserEmail,
unreadMessageCount: 0, unreadMessageCount: 0,
isImageLoading: false, isImageLoading: false,
image: chatImages!.first.profilePicture ?? "", image: chatImages!.first.profilePicture ?? "",
@ -407,6 +424,31 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners(); notifyListeners();
} }
void OnSubmitChatAsync(List<Object?>? parameters) {
print(isChatScreenActive);
print(receiverID);
print(isChatScreenActive);
logger.i(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() { void sort() {
searchedChats!.sort( searchedChats!.sort(
(ChatUser a, ChatUser b) => b.unreadMessageCount!.compareTo(a.unreadMessageCount!), (ChatUser a, ChatUser b) => b.unreadMessageCount!.compareTo(a.unreadMessageCount!),
@ -457,6 +499,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return 2; return 2;
case ".rar": case ".rar":
return 2; return 2;
case ".aac":
return 13;
case ".mp3":
return 14;
default: default:
return 0; return 0;
} }
@ -490,6 +536,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return "application/octet-stream"; return "application/octet-stream";
case ".rar": case ".rar":
return "application/octet-stream"; return "application/octet-stream";
case ".aac":
return "audio/aac";
case ".mp3":
return "audio/mp3";
default: default:
return ""; return "";
} }
@ -504,39 +554,56 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
required bool isAttachment, required bool isAttachment,
required bool isReply, required bool isReply,
Uint8List? image, Uint8List? image,
required bool isImageLoaded}) async { required bool isImageLoaded,
String? userEmail,
int? userStatus,
File? voiceFile,
required bool isVoiceAttached}) async {
Uuid uuid = const Uuid(); Uuid uuid = const Uuid();
String contentNo = uuid.v4(); String contentNo = uuid.v4();
String msg = message.text; String msg;
if (isVoiceAttached) {
msg = voiceFile!.path.split("/").last;
} else {
msg = message.text;
logger.w(msg);
}
SingleUserChatModel data = SingleUserChatModel( SingleUserChatModel data = SingleUserChatModel(
chatEventId: chatEventId, userChatHistoryId: 0,
chatSource: 1, chatEventId: chatEventId,
contant: msg, chatSource: 1,
contantNo: contentNo, contant: msg,
conversationId: chatCID, contantNo: contentNo,
createdDate: DateTime.now(), conversationId: chatCID,
currentUserId: AppState().chatDetails!.response!.id, createdDate: DateTime.now(),
currentUserName: AppState().chatDetails!.response!.userName, currentUserId: AppState().chatDetails!.response!.id,
targetUserId: targetUserId, currentUserName: AppState().chatDetails!.response!.userName,
targetUserName: targetUserName, targetUserId: targetUserId,
isReplied: false, targetUserName: targetUserName,
fileTypeId: fileTypeId, isReplied: false,
userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null, fileTypeId: fileTypeId,
fileTypeResponse: isAttachment userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null,
? FileTypeResponse( fileTypeResponse: isAttachment
fileTypeId: fileTypeId, ? FileTypeResponse(
fileTypeName: getFileExtension(selectedFile.path).toString(), fileTypeId: fileTypeId,
fileKind: "file", fileTypeName: isVoiceMsg ? getFileExtension(voiceFile!.path).toString() : getFileExtension(selectedFile.path).toString(),
fileName: selectedFile.path.split("/").last, fileKind: "file",
fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()), fileName: isVoiceMsg ? msg : selectedFile.path.split("/").last,
) fileTypeDescription: isVoiceMsg ? getFileTypeDescription(getFileExtension(voiceFile!.path).toString()) : getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
: null, )
image: image, : null,
isImageLoaded: isImageLoaded, image: image,
); isImageLoaded: isImageLoaded,
voice: isVoiceMsg ? voiceFile! : null,
voiceController: isVoiceMsg ? AudioPlayer() : null);
if (kDebugMode) {
logger.i("model data: " + jsonEncode(data));
}
userChatHistory.insert(0, data); userChatHistory.insert(0, data);
isFileSelected = false; isTextMsg = false;
isMsgReply = false; isReplyMsg = false;
isAttachmentMsg = false;
isVoiceMsg = false;
sFileType = ""; sFileType = "";
message.clear(); message.clear();
notifyListeners(); notifyListeners();
@ -547,35 +614,35 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void sendChatMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async { 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"); print("====================== Values ============================");
if (message.text == null || message.text.isEmpty) { print("Is Text " + isTextMsg.toString());
print("isReply " + isReplyMsg.toString());
print("isAttachment " + isAttachmentMsg.toString());
print("isVoice " + isVoiceMsg.toString());
}
//Text
if (isTextMsg && !isAttachmentMsg && !isVoiceMsg && !isReplyMsg) {
logger.d("// Normal Text Message");
if (message.text.isEmpty) {
return; return;
} }
sendChatToServer( sendChatToServer(
chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false, isImageLoaded: false, image: null); chatEventId: 1,
} // normal Text msg fileTypeId: null,
if (isFileSelected && !isMsgReply) {
bool isImage = false;
print("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, targetUserId: targetUserId,
targetUserName: targetUserName, targetUserName: targetUserName,
isAttachment: true, isAttachment: false,
chatReplyId: null, chatReplyId: null,
isReply: false, isReply: false,
isImageLoaded: true, isImageLoaded: false,
image: selectedFile.readAsBytesSync()); image: null,
} // normal attachemnt msg isVoiceAttached: false,
if (!isFileSelected && isMsgReply) { userEmail: userEmail,
print("Normal Text To Text Reply"); userStatus: userStatus);
if (message.text == null || message.text.isEmpty) { } else if (isTextMsg && !isAttachmentMsg && !isVoiceMsg && isReplyMsg) {
logger.d("// Text Message as Reply");
if (message.text.isEmpty) {
return; return;
} }
sendChatToServer( sendChatToServer(
@ -587,10 +654,34 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
isAttachment: false, isAttachment: false,
isReply: true, isReply: true,
isImageLoaded: repliedMsg.first.isImageLoaded!, isImageLoaded: repliedMsg.first.isImageLoaded!,
image: repliedMsg.first.image); image: repliedMsg.first.image,
} // reply msg over image && normal isVoiceAttached: false,
if (isFileSelected && isMsgReply) { voiceFile: null,
print("Reply With File"); userEmail: userEmail,
userStatus: userStatus);
}
// Attachment
else if (!isTextMsg && isAttachmentMsg && !isVoiceMsg && !isReplyMsg) {
logger.d("// Normal Image Message");
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(),
isVoiceAttached: false,
userEmail: userEmail,
userStatus: userStatus);
} else if (!isTextMsg && isAttachmentMsg && !isVoiceMsg && isReplyMsg) {
logger.d("// Image as Reply Msg");
Utils.showLoading(context); Utils.showLoading(context);
dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile);
String? ext = getFileExtension(selectedFile.path); String? ext = getFileExtension(selectedFile.path);
@ -604,9 +695,81 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
chatReplyId: repliedMsg.first.userChatHistoryId, chatReplyId: repliedMsg.first.userChatHistoryId,
isReply: true, isReply: true,
isImageLoaded: true, isImageLoaded: true,
image: selectedFile.readAsBytesSync()); image: selectedFile.readAsBytesSync(),
isVoiceAttached: false,
userEmail: userEmail,
userStatus: userStatus);
} }
//Voice
else if (!isTextMsg && !isAttachmentMsg && isVoiceMsg && !isReplyMsg) {
logger.d("// Normal Voice Message");
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);
sendChatToServer(
chatEventId: 2,
fileTypeId: getFileType(ext.toString()),
targetUserId: targetUserId,
targetUserName: targetUserName,
chatReplyId: null,
isAttachment: true,
isReply: isReplyMsg,
isImageLoaded: false,
voiceFile: voiceFile,
isVoiceAttached: true,
userEmail: userEmail,
userStatus: userStatus);
notifyListeners();
} else if (!isTextMsg && !isAttachmentMsg && isVoiceMsg && isReplyMsg) {
logger.d("// Voice as Reply Msg");
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);
sendChatToServer(
chatEventId: 2,
fileTypeId: getFileType(ext.toString()),
targetUserId: targetUserId,
targetUserName: targetUserName,
chatReplyId: null,
isAttachment: true,
isReply: isReplyMsg,
isImageLoaded: false,
voiceFile: voiceFile,
isVoiceAttached: true,
userEmail: userEmail,
userStatus: userStatus);
notifyListeners();
}
if (searchedChats != null) { if (searchedChats != null) {
dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) { if (contain.isEmpty) {
@ -630,34 +793,36 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
); );
notifyListeners(); 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();
} }
// 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 selectImageToUpload(BuildContext context) { void selectImageToUpload(BuildContext context) {
ImageOptions.showImageOptionsNew(context, true, (String image, File file) async { ImageOptions.showImageOptionsNew(context, true, (String image, File file) async {
if (checkFileSize(file.path)) { if (checkFileSize(file.path)) {
selectedFile = file; selectedFile = file;
isFileSelected = true; isAttachmentMsg = true;
isTextMsg = false;
sFileType = getFileExtension(file.path)!; sFileType = getFileExtension(file.path)!;
message.text = file.path.split("/").last; message.text = file.path.split("/").last;
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -669,7 +834,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void removeAttachment() { void removeAttachment() {
isFileSelected = false; isAttachmentMsg = false;
sFileType = ""; sFileType = "";
message.text = ''; message.text = '';
notifyListeners(); notifyListeners();
@ -677,7 +842,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
String? getFileExtension(String fileName) { String? getFileExtension(String fileName) {
try { try {
print("Ext: " + "." + fileName.split('.').last); if (kDebugMode) {
logger.i("ext: " + "." + fileName.split('.').last);
}
return "." + fileName.split('.').last; return "." + fileName.split('.').last;
} catch (e) { } catch (e) {
return null; return null;
@ -724,6 +891,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return "assets/icons/chat/zip.svg"; return "assets/icons/chat/zip.svg";
case ".rar": case ".rar":
return "assets/icons/chat/zip.svg"; return "assets/icons/chat/zip.svg";
case ".aac":
return "assets/icons/chat/aac.svg";
case ".mp3":
return "assets/icons/chat/zip.mp3";
default: default:
return "assets/images/thumb.svg"; return "assets/images/thumb.svg";
} }
@ -732,14 +903,14 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void chatReply(SingleUserChatModel data) { void chatReply(SingleUserChatModel data) {
repliedMsg = []; repliedMsg = [];
data.isReplied = true; data.isReplied = true;
isMsgReply = true; isReplyMsg = true;
repliedMsg.add(data); repliedMsg.add(data);
notifyListeners(); notifyListeners();
} }
void closeMe() { void closeMe() {
repliedMsg = []; repliedMsg = [];
isMsgReply = false; isReplyMsg = false;
notifyListeners(); notifyListeners();
} }
@ -789,22 +960,26 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
receiverID = 0; receiverID = 0;
paginationVal = 0; paginationVal = 0;
message.text = ''; message.text = '';
isFileSelected = false; isAttachmentMsg = false;
repliedMsg = []; repliedMsg = [];
sFileType = ""; sFileType = "";
isMsgReply = false; isReplyMsg = false;
isTextMsg = false;
isVoiceMsg = false;
notifyListeners(); notifyListeners();
} }
void clearAll() { void clearAll() {
print("----------------- Disposed ---------------------------");
searchedChats = pChatHistory; searchedChats = pChatHistory;
search.clear(); search.clear();
isChatScreenActive = false; isChatScreenActive = false;
receiverID = 0; receiverID = 0;
paginationVal = 0; paginationVal = 0;
message.text = ''; message.text = '';
isFileSelected = false; isTextMsg = false;
isAttachmentMsg = false;
isVoiceMsg = false;
isReplyMsg = false;
repliedMsg = []; repliedMsg = [];
sFileType = ""; sFileType = "";
} }
@ -815,7 +990,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
receiverID = 0; receiverID = 0;
paginationVal = 0; paginationVal = 0;
message.text = ''; message.text = '';
isFileSelected = false; isTextMsg = false;
isAttachmentMsg = false;
isVoiceMsg = false;
isReplyMsg = false;
repliedMsg = []; repliedMsg = [];
sFileType = ""; sFileType = "";
deleteData(); deleteData();
@ -886,8 +1064,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} else { } else {
await deleteFile(userID); await deleteFile(userID);
Uint8List decodedBytes = base64Decode(encodedBytes); Uint8List decodedBytes = base64Decode(encodedBytes);
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
late File imageFile = File("${appDocumentsDirectory.path}/$userID.jpg"); 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); imageFile.writeAsBytesSync(decodedBytes);
return imageFile; return imageFile;
} }
@ -895,7 +1078,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future deleteFile(String userID) async { Future deleteFile(String userID) async {
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
late File imageFile = File('${appDocumentsDirectory.path}/$userID.jpg'); String dirPath = '${appDocumentsDirectory.path}/chat_images';
late File imageFile = File('$dirPath/$userID.jpg');
if (await imageFile.exists()) { if (await imageFile.exists()) {
await imageFile.delete(); await imageFile.delete();
} }
@ -952,33 +1136,30 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void userTypingInvoke({required int currentUser, required int reciptUser}) async { void userTypingInvoke({required int currentUser, required int reciptUser}) async {
logger.d([reciptUser, currentUser]);
await chatHubConnection.invoke("UserTypingAsync", args: [reciptUser, currentUser]); await chatHubConnection.invoke("UserTypingAsync", args: [reciptUser, currentUser]);
} }
// Audio Recoding Work //////// 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 { Future<void> initAudio({required int receiverId}) async {
// final dir = Directory((Platform.isAndroid
// ? await getExternalStorageDirectory() //FOR ANDROID
// : await getApplicationSupportDirectory() //FOR IOS
// )!
appDirectory = await getApplicationDocumentsDirectory(); appDirectory = await getApplicationDocumentsDirectory();
path = "${appDirectory.path}/${AppState().chatDetails!.response!.id}-${DateTime.now().microsecondsSinceEpoch}.aac"; 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() recorderController = RecorderController()
..androidEncoder = AndroidEncoder.aac ..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4 ..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 8000 ..sampleRate = 6000
..bitRate = 32000; ..updateFrequency = const Duration(milliseconds: 100)
..bitRate = 18000;
playerController = PlayerController(); playerController = PlayerController();
} }
@ -986,16 +1167,17 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
isRecoding = false; isRecoding = false;
isPlaying = false; isPlaying = false;
isPause = false; isPause = false;
isVoiceMsg = false;
recorderController.dispose(); recorderController.dispose();
playerController.dispose(); playerController.dispose();
} }
void startRecoding() async { void startRecoding() async {
PermissionStatus status = await Permission.microphone.request(); PermissionStatus status = await Permission.microphone.request();
print(status);
if (status.isDenied == true) { if (status.isDenied == true) {
startRecoding(); startRecoding();
} else { } else {
isVoiceMsg = true;
recorderController.reset(); recorderController.reset();
await recorderController.record(path); await recorderController.record(path);
_recodeDuration = 0; _recodeDuration = 0;
@ -1005,58 +1187,60 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
} }
void _startTimer() { Future<void> _startTimer() async {
_timer?.cancel(); _timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) { _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) async {
_recodeDuration++; _recodeDuration++;
buildTimer(); if (_recodeDuration <= 59) {
notifyListeners(); applyCounter();
} else {
pauseRecoding();
}
}); });
} }
void applyCounter() {
buildTimer();
notifyListeners();
}
Future<void> pauseRecoding() async { Future<void> pauseRecoding() async {
isPause = true; isPause = true;
isPlaying = true; isPlaying = true;
recorderController.pause(); recorderController.pause();
path = await recorderController.stop(false); path = await recorderController.stop(false);
print(path);
File file = File(path!); File file = File(path!);
file.readAsBytesSync(); file.readAsBytesSync();
path = file.path;
await playerController.preparePlayer(file.path, 1.0); await playerController.preparePlayer(file.path, 1.0);
var tempDuration = _recodeDuration;
_recodeDuration = tempDuration;
_timer?.cancel(); _timer?.cancel();
notifyListeners(); notifyListeners();
} }
void resumeRecoding() {
isPause = false;
isPlaying = false;
isRecoding = true;
recorderController.record(path);
_startTimer();
}
Future<void> deleteRecoding() async { Future<void> deleteRecoding() async {
print(path);
_recodeDuration = 0; _recodeDuration = 0;
_timer?.cancel(); _timer?.cancel();
// path = await recorderController.stop(false); if (path == null) {
recorderController.reset(); path = await recorderController.stop(true);
print(path); } else {
await recorderController.stop(true);
}
if (path != null && path!.isNotEmpty) { if (path != null && path!.isNotEmpty) {
File delFile = File(path!); File delFile = File(path!);
double fileSizeInKB = delFile.lengthSync() / 1024; double fileSizeInKB = delFile.lengthSync() / 1024;
double fileSizeInMB = fileSizeInKB / 1024; double fileSizeInMB = fileSizeInKB / 1024;
debugPrint("Deleted file size: ${delFile.lengthSync()}"); if (kDebugMode) {
debugPrint("Deleted file size in KB: " + fileSizeInKB.toString()); debugPrint("Deleted file size: ${delFile.lengthSync()}");
debugPrint("Deleted file size in MB: " + fileSizeInMB.toString()); debugPrint("Deleted file size in KB: " + fileSizeInKB.toString());
debugPrint("Deleted file size in MB: " + fileSizeInMB.toString());
}
if (await delFile.exists()) { if (await delFile.exists()) {
delFile.delete(); delFile.delete();
} }
isPause = false; isPause = false;
isRecoding = false; isRecoding = false;
isPlaying = false; isPlaying = false;
isVoiceMsg = false;
notifyListeners(); notifyListeners();
} }
} }
@ -1075,13 +1259,81 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return numberStr; return numberStr;
} }
void playRecoding() async { Future<String> downChatVoice(Uint8List bytes, String ext, SingleUserChatModel data) async {
isPlaying = true; String dirPath = '${(await getApplicationDocumentsDirectory()).path}/chat_audios';
await playerController.startPlayer(finishMode: FinishMode.stop); 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;
}
void scrollToMsg(SingleUserChatModel data) {
if (data.userChatReplyResponse != null && data.userChatReplyResponse!.userChatHistoryId != null) {
int index = userChatHistory.indexWhere((SingleUserChatModel element) => element.userChatHistoryId == data.userChatReplyResponse!.userChatHistoryId);
if (index >= 1) {
double contentSize = scrollController.position.viewportDimension + scrollController.position.maxScrollExtent;
double target = contentSize * index / userChatHistory.length;
scrollController.position.animateTo(
target,
duration: const Duration(seconds: 1),
curve: Curves.easeInOut,
);
}
}
} }
void playOrPause() async { Future<void> getTeamMembers() async {
playerController.playerState == PlayerState.playing ? await playerController.pausePlayer() : playRecoding(); teamMembersList = [];
isLoading = true;
if (AppState().getemployeeSubordinatesList.isNotEmpty) {
print("=============== In App State =====================");
getEmployeeSubordinatesList = AppState().getemployeeSubordinatesList;
for (GetEmployeeSubordinatesList element in getEmployeeSubordinatesList) {
print(element.eMPLOYEEEMAILADDRESS);
teamMembersList.add(
ChatUser(
id: int.parse(element.eMPLOYEENUMBER!),
email: element.eMPLOYEEEMAILADDRESS,
userName: element.eMPLOYEEDISPLAYNAME,
phone: element.eMPLOYEEMOBILENUMBER,
userStatus: 0,
unreadMessageCount: 0,
isFav: false,
isTyping: false,
isImageLoading: false,
image: element.eMPLOYEEIMAGE ?? "",
isImageLoaded: true,
userLocalDownlaodedImage: await downloadImageLocal(element.eMPLOYEEIMAGE ?? "", element.eMPLOYEENUMBER!),
),
);
}
} else {
getEmployeeSubordinatesList = await MyTeamApiClient().getEmployeeSubordinates("", "", "");
AppState().setemployeeSubordinatesList = getEmployeeSubordinatesList;
for (GetEmployeeSubordinatesList element in getEmployeeSubordinatesList) {
print(element.eMPLOYEEEMAILADDRESS);
teamMembersList.add(
ChatUser(
id: int.parse(element.eMPLOYEENUMBER!),
email: element.eMPLOYEEEMAILADDRESS,
userName: element.eMPLOYEEDISPLAYNAME,
phone: element.eMPLOYEEMOBILENUMBER,
userStatus: 0,
unreadMessageCount: 0,
isFav: false,
isTyping: false,
isImageLoading: false,
image: element.eMPLOYEEIMAGE ?? "",
isImageLoaded: true,
userLocalDownlaodedImage: await downloadImageLocal(element.eMPLOYEEIMAGE ?? "", element.eMPLOYEENUMBER!),
),
);
}
}
isLoading = false;
notifyListeners(); notifyListeners();
} }
} }

@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart'; import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
@ -35,8 +36,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
bool isWorkListLoading = true; bool isWorkListLoading = true;
int workListCounter = 0; int workListCounter = 0;
//Misssing Swipe //Misssing Swipe
bool isMissingSwipeLoading = true; bool isMissingSwipeLoading = true;
int missingSwipeCounter = 0; int missingSwipeCounter = 0;
@ -94,7 +93,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
accrualList = null; accrualList = null;
leaveBalanceAccrual = null; leaveBalanceAccrual = null;
ticketBalance = 0; ticketBalance = 0;
isServicesMenusLoading = true; isServicesMenusLoading = true;
homeMenus = null; homeMenus = null;
@ -215,6 +213,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
List findMyTeam = menuList.where((element) => element.menuType == "M").toList(); List findMyTeam = menuList.where((element) => element.menuType == "M").toList();
if (findMyTeam.isNotEmpty) { if (findMyTeam.isNotEmpty) {
AppState().setempStatusIsManager = true;
drawerMenuItemList.insert(2, DrawerMenuItem("assets/images/drawer/my_team.svg", LocaleKeys.myTeamMembers.tr(), AppRoutes.myTeam)); drawerMenuItemList.insert(2, DrawerMenuItem("assets/images/drawer/my_team.svg", LocaleKeys.myTeamMembers.tr(), AppRoutes.myTeam));
} }
} catch (ex) { } catch (ex) {

@ -1,10 +1,10 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:audio_waveforms/audio_waveforms.dart' as awf;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.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/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
@ -12,30 +12,37 @@ import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.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/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_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/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart'; import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/ui/chat/common.dart';
import 'package:open_file/open_file.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
// todo: @aamir use extension methods, and use correct widgets. import 'package:just_audio/just_audio.dart';
class ChatBubble extends StatelessWidget { class ChatBubble extends StatelessWidget {
ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key); ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key);
final String dateTime; final String dateTime;
final SingleUserChatModel cItem; final SingleUserChatModel cItem;
bool isCurrentUser = false; bool isCurrentUser = false;
bool isSeen = false; bool isSeen = false;
bool isReplied = false; bool isReplied = false;
int? fileTypeID; int? fileTypeID;
String? fileTypeName; String? fileTypeName;
late ChatProviderModel data;
late ChatProviderModel provider;
String? fileTypeDescription; String? fileTypeDescription;
bool isDelivered = false; bool isDelivered = false;
String userName = ''; String userName = '';
late Offset screenOffset; late Offset screenOffset;
void makeAssign() { void makeAssign() {
@ -49,12 +56,57 @@ class ChatBubble extends StatelessWidget {
userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString(); userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString();
} }
void playVoice(
BuildContext context, {
required SingleUserChatModel data,
}) async {
if (data.voice != null && data.voice!.existsSync()) {
await data.voiceController!.setFilePath(data!.voice!.path);
await data.voiceController!.setLoopMode(LoopMode.off);
Duration? duration = await data.voiceController!.load();
await data.voiceController!.seek(duration);
await data.voiceController!.play();
} else {
Utils.showLoading(context);
Uint8List encodedString = await ChatApiClient().downloadURL(fileName: data.contant!, fileTypeDescription: provider.getFileTypeDescription(data.fileTypeResponse!.fileTypeName ?? ""));
try {
String path = await provider.downChatVoice(encodedString, data.fileTypeResponse!.fileTypeName ?? "", data);
File file = File(path!);
await file.readAsBytes();
data.voice = file;
Duration? duration = await data.voiceController!.setFilePath(file.path);
await data.voiceController!.setLoopMode(LoopMode.off);
await data.voiceController!.seek(duration);
await data.voiceController!.setVolume(1.0);
await data.voiceController!.load();
Utils.hideLoading(context);
await data.voiceController!.play();
} catch (e) {
Utils.showToast("Cannot open file.");
}
}
}
void pausePlaying(BuildContext context, {required SingleUserChatModel data}) async {
await data.voiceController!.pause();
}
void rePlay(BuildContext context, {required SingleUserChatModel data}) async {
if (data.voice != null && data.voice!.existsSync()) {
await data.voiceController!.seek(Duration.zero);
await data.voiceController!.play();
}
}
Stream<PositionData> get _positionDataStream => Rx.combineLatest3<Duration, Duration, Duration?, PositionData>(cItem.voiceController!.positionStream, cItem.voiceController!.bufferedPositionStream,
cItem.voiceController!.durationStream, (Duration position, Duration bufferedPosition, Duration? duration) => PositionData(position, bufferedPosition, duration ?? Duration.zero));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size windowSize = MediaQuery.of(context).size; Size windowSize = MediaQuery.of(context).size;
screenOffset = Offset(windowSize.width / 2, windowSize.height / 2); screenOffset = Offset(windowSize.width / 2, windowSize.height / 2);
makeAssign(); makeAssign();
data = Provider.of<ChatProviderModel>(context, listen: false); provider = Provider.of<ChatProviderModel>(context, listen: false);
return isCurrentUser ? currentUser(context) : receiptUser(context); return isCurrentUser ? currentUser(context) : receiptUser(context);
} }
@ -84,25 +136,24 @@ class ChatBubble extends StatelessWidget {
.paddingOnly(right: 5, top: 5, bottom: 8, left: 5), .paddingOnly(right: 5, top: 5, bottom: 8, left: 5),
], ],
).expanded, ).expanded,
if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || if (cItem.userChatReplyResponse != null)
cItem.userChatReplyResponse!.fileTypeId == 3 || if (cItem.userChatReplyResponse!.fileTypeId == 12 || cItem.userChatReplyResponse!.fileTypeId == 3 || cItem.userChatReplyResponse!.fileTypeId == 4)
cItem.userChatReplyResponse!.fileTypeId == 4) ClipRRect(
ClipRRect( borderRadius: BorderRadius.circular(8.0),
borderRadius: BorderRadius.circular(8.0), child: SizedBox(
child: SizedBox( height: 32,
height: 32, width: 32,
width: 32, child: showImage(
child: showImage( isReplyPreview: false,
isReplyPreview: true,
fileName: cItem.userChatReplyResponse!.contant!, fileName: cItem.userChatReplyResponse!.contant!,
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg") fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg")),
.paddingOnly(left: 10, right: 10, bottom: 16, top: 16), ).paddingOnly(left: 10, right: 10, bottom: 16, top: 16),
),
),
], ],
), ),
), ),
).paddingOnly(bottom: 7), ).paddingOnly(bottom: 7).onPress(() {
provider.scrollToMsg(cItem);
}),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(5.0), borderRadius: BorderRadius.circular(5.0),
@ -117,18 +168,21 @@ class ChatBubble extends StatelessWidget {
); );
}), }),
), ),
).paddingOnly(bottom: 4) ).paddingOnly(bottom: 4),
if (fileTypeID == 13 && cItem.voiceController != null)
currentWaveBubble(context, cItem)
else else
Row( Row(
children: [ children: [
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 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), SvgPicture.asset(provider.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 0, right: 10),
(cItem.contant ?? "").toText12().expanded, (cItem.contant ?? "").toText12().expanded,
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
//|| fileTypeID == 2 //|| fileTypeID == 2
) const Icon(Icons.remove_red_eye, size: 16) )
const Icon(Icons.remove_red_eye, size: 16)
], ],
), ),
Align( Align(
@ -157,10 +211,7 @@ class ChatBubble extends StatelessWidget {
transform: GradientRotation(.83), transform: GradientRotation(.83),
begin: Alignment.topRight, begin: Alignment.topRight,
end: Alignment.bottomLeft, end: Alignment.bottomLeft,
colors: <Color>[ colors: <Color>[MyColors.gradiantEndColor, MyColors.gradiantStartColor],
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
],
), ),
), ),
child: Column( child: Column(
@ -186,24 +237,25 @@ class ChatBubble extends StatelessWidget {
.paddingOnly(right: 5, top: 5, bottom: 8, left: 5), .paddingOnly(right: 5, top: 5, bottom: 8, left: 5),
], ],
).expanded, ).expanded,
if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || if (cItem.userChatReplyResponse != null)
cItem.userChatReplyResponse!.fileTypeId == 3 || if (cItem.userChatReplyResponse!.fileTypeId == 12 || cItem.userChatReplyResponse!.fileTypeId == 3 || cItem.userChatReplyResponse!.fileTypeId == 4)
cItem.userChatReplyResponse!.fileTypeId == 4) ClipRRect(
ClipRRect( borderRadius: BorderRadius.circular(8.0),
borderRadius: BorderRadius.circular(8.0), child: SizedBox(
child: SizedBox( height: 32,
height: 32, width: 32,
width: 32, child: showImage(
child: showImage( isReplyPreview: true,
isReplyPreview: true, fileName: cItem.userChatReplyResponse!.contant!,
fileName: cItem.userChatReplyResponse!.contant!, fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg"),
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg"), ),
), ).paddingOnly(left: 10, right: 10, bottom: 16, top: 16)
).paddingOnly(left: 10, right: 10, bottom: 16, top: 16)
], ],
), ),
), ),
).paddingOnly(bottom: 7), ).paddingOnly(bottom: 7).onPress(() {
provider.scrollToMsg(cItem);
}),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(5.0), borderRadius: BorderRadius.circular(5.0),
@ -218,14 +270,16 @@ class ChatBubble extends StatelessWidget {
); );
}), }),
), ),
).paddingOnly(bottom: 4) ).paddingOnly(bottom: 4),
if (fileTypeID == 13 && cItem.voiceController != null)
recipetWaveBubble(context, cItem)
else else
Row( Row(
children: [ children: [
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 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), SvgPicture.asset(provider.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 0, right: 10),
(cItem.contant ?? "").toText12(color: Colors.white).expanded, (cItem.contant ?? "").toText12(color: Colors.white).expanded,
if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8
//|| fileTypeID == 2 //|| fileTypeID == 2
@ -245,8 +299,6 @@ class ChatBubble extends StatelessWidget {
} }
Widget showImage({required bool isReplyPreview, required String fileName, required String fileTypeDescription}) { Widget showImage({required bool isReplyPreview, required String fileName, required String fileTypeDescription}) {
if (isReplyPreview) {}
if (cItem.isImageLoaded! && cItem.image != null) { if (cItem.isImageLoaded! && cItem.image != null) {
return Image.memory( return Image.memory(
cItem.image!, cItem.image!,
@ -283,64 +335,108 @@ 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 currentWaveBubble(BuildContext context, SingleUserChatModel data) {
Widget build(BuildContext context) {
return Container( return Container(
margin: const EdgeInsets.all(10), margin: const EdgeInsets.all(0),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30), border: Border(
gradient: const LinearGradient( left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white),
transform: GradientRotation(.83),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
],
), ),
color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
), ),
child: Row( child: Row(
mainAxisSize: MainAxisSize.min,
children: [ children: [
IconButton( getPlayer(player: data.voiceController!, modelData: data),
onPressed: onTap, StreamBuilder<PositionData>(
icon: Icon(isPlaying ? Icons.stop : Icons.play_arrow), stream: _positionDataStream,
color: Colors.white, builder: (BuildContext context, AsyncSnapshot<PositionData> snapshot) {
splashColor: Colors.transparent, PositionData? positionData = snapshot.data;
highlightColor: Colors.transparent, return SeekBar(
duration: positionData?.duration ?? Duration.zero,
position: positionData?.position ?? Duration.zero,
bufferedPosition: positionData?.bufferedPosition ?? Duration.zero,
onChangeEnd: data.voiceController!.seek,
).expanded;
},
), ),
AudioFileWaveforms( ],
size: Size(MediaQuery.of(context).size.width / 2, 10), ),
playerController: playerController, ).circle(5);
padding: EdgeInsets.zero, }
margin: EdgeInsets.zero,
playerWaveStyle: const PlayerWaveStyle( Widget recipetWaveBubble(BuildContext context, SingleUserChatModel data) {
fixedWaveColor: Colors.white, return Container(
liveWaveColor:MyColors.lightGreenColor, margin: const EdgeInsets.all(0),
showTop: true, decoration: BoxDecoration(
showBottom: true, border: Border(
waveCap: StrokeCap.round, left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white),
seekLineThickness: 3, ),
visualizerHeight: 6, color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
backgroundColor: Colors.transparent ),
), child: Row(
mainAxisSize: MainAxisSize.max,
children: [
getPlayer(player: data.voiceController!, modelData: data),
StreamBuilder<PositionData>(
stream: _positionDataStream,
builder: (BuildContext context, AsyncSnapshot<PositionData> snapshot) {
PositionData? positionData = snapshot.data;
return SeekBar(
duration: positionData?.duration ?? Duration.zero,
position: positionData?.position ?? Duration.zero,
bufferedPosition: positionData?.bufferedPosition ?? Duration.zero,
onChangeEnd: data.voiceController!.seek,
).expanded;
},
), ),
], ],
), ),
).circle(5);
}
Widget getPlayer({required AudioPlayer player, required SingleUserChatModel modelData}) {
return StreamBuilder<PlayerState>(
stream: player!.playerStateStream,
builder: (BuildContext context, AsyncSnapshot<PlayerState> snapshot) {
PlayerState? playerState = snapshot.data;
ProcessingState? processingState = playerState?.processingState;
bool? playing = playerState?.playing;
if (processingState == ProcessingState.loading || processingState == ProcessingState.buffering) {
return Container(
margin: const EdgeInsets.all(8.0),
width: 30.0,
height: 30.0,
child: const CircularProgressIndicator(),
);
} else if (playing != true) {
return Icon(
Icons.play_arrow,
size: 30,
color: MyColors.lightGreenColor,
).onPress(() {
playVoice(context, data: modelData);
});
} else if (processingState != ProcessingState.completed) {
return Icon(
Icons.pause,
size: 30,
color: MyColors.lightGreenColor,
).onPress(() {
pausePlaying(context, data: modelData);
});
} else {
return Icon(
Icons.replay,
size: 30,
color: MyColors.lightGreenColor,
).onPress(() {
rePlay(context, data: modelData);
});
}
},
); );
} }
} }

@ -1,6 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -18,7 +16,7 @@ import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.da
import 'package:mohem_flutter_app/provider/chat_provider_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/call/chat_outgoing_call_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart'; import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/ui/chat/common.dart';
import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart'; import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -80,7 +78,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
loadMore: false, loadMore: false,
isNewChat: params!.isNewChat!, isNewChat: params!.isNewChat!,
); );
data.initAudio(); data.initAudio(receiverId: params!.chatUser!.id!);
} }
return Scaffold( return Scaffold(
@ -130,7 +128,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
reverse: true, reverse: true,
itemCount: m.userChatHistory.length, itemCount: m.userChatHistory.length,
padding: const EdgeInsets.all(21), padding: const EdgeInsets.all(21),
separatorBuilder: (cxt, index) => 8.height, separatorBuilder: (BuildContext cxt, int index) => 8.height,
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
return SwipeTo( return SwipeTo(
iconColor: MyColors.lightGreenColor, iconColor: MyColors.lightGreenColor,
@ -144,15 +142,24 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
); );
}, },
).onPress(() async { ).onPress(() async {
if (m.userChatHistory[i].fileTypeResponse != null) { logger.w(m.userChatHistory[i].toJson());
m.getChatMedia(context, if (m.userChatHistory[i].fileTypeResponse != null && m.userChatHistory[i].fileTypeId != null) {
fileTypeName: m.userChatHistory[i].fileTypeResponse!.fileTypeName ?? "", fileTypeID: m.userChatHistory[i].fileTypeId!, fileName: m.userChatHistory[i].contant!); if (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!);
}
} }
}); });
}, },
), ),
).expanded, ).expanded,
if (m.isMsgReply) if (m.isReplyMsg)
SizedBox( SizedBox(
height: 82, height: 82,
child: Row( child: Row(
@ -174,7 +181,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
], ],
).expanded, ).expanded,
12.width, 12.width,
if (m.isMsgReply && m.repliedMsg.isNotEmpty) showReplyImage(m.repliedMsg, m), if (m.isReplyMsg && m.repliedMsg.isNotEmpty) showReplyImage(m.repliedMsg, m),
12.width, 12.width,
const Icon(Icons.cancel, size: 23, color: MyColors.grey7BColor).onPress(m.closeMe), const Icon(Icons.cancel, size: 23, color: MyColors.grey7BColor).onPress(m.closeMe),
], ],
@ -183,12 +190,9 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
], ],
), ),
), ),
if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg") if (m.isAttachmentMsg && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg")
SizedBox(height: 200, width: double.infinity, child: Image.file(m.selectedFile, fit: BoxFit.cover)).paddingOnly(left: 21, right: 21, top: 21), SizedBox(height: 200, width: double.infinity, child: Image.file(m.selectedFile, fit: BoxFit.cover)).paddingOnly(left: 21, right: 21, top: 21),
const Divider( const Divider(height: 1, color: MyColors.lightGreyEFColor),
height: 1,
color: MyColors.lightGreyEFColor,
),
if (m.isRecoding) if (m.isRecoding)
Column( Column(
children: <Widget>[ children: <Widget>[
@ -197,12 +201,11 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
Text(m.buildTimer()).paddingAll(10), Text(m.buildTimer()).paddingAll(10),
if (m.isRecoding && m.isPlaying) if (m.isRecoding && m.isPlaying)
WaveBubble( WaveBubble(
playerController: m.playerController, playerController: m.playerController,
onTap: () { isPlaying: m.playerController.playerState == PlayerState.playing,
m.playOrPause(); onTap: () {
}, },
isPlaying: m.playerController.playerState == PlayerState.playing) ).expanded
.expanded
else else
AudioWaveforms( AudioWaveforms(
waveStyle: const WaveStyle( waveStyle: const WaveStyle(
@ -234,22 +237,6 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
).paddingAll(10).onPress(() { ).paddingAll(10).onPress(() {
m.deleteRecoding(); 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) SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26)
.onPress( .onPress(
() => m.sendChatMessage(context, () => m.sendChatMessage(context,
@ -269,8 +256,8 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
TextField( TextField(
controller: m.message, controller: m.message,
decoration: InputDecoration( decoration: InputDecoration(
hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), hintText: m.isAttachmentMsg ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(),
hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), hintStyle: TextStyle(color: m.isAttachmentMsg ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14),
border: InputBorder.none, border: InputBorder.none,
focusedBorder: InputBorder.none, focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none, enabledBorder: InputBorder.none,
@ -288,7 +275,13 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15)
: null, : null,
), ),
onChanged: (val) { onChanged: (String val) {
print(val.length);
if (val.isNotEmpty) {
m.isTextMsg = true;
} else {
m.isTextMsg = false;
}
m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!); m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!);
}, },
).expanded, ).expanded,
@ -306,7 +299,7 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
() => m.selectImageToUpload(context), () => m.selectImageToUpload(context),
), ),
).paddingOnly(right: 15), ).paddingOnly(right: 15),
Icon( const Icon(
Icons.mic, Icons.mic,
color: MyColors.lightGreenColor, color: MyColors.lightGreenColor,
).paddingOnly(right: 15).onPress(() { ).paddingOnly(right: 15).onPress(() {

@ -1,5 +1,6 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.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/string_extensions.dart';
@ -8,6 +9,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart';
import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
import 'package:mohem_flutter_app/ui/chat/my_team_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -81,6 +83,7 @@ class _ChatHomeState extends State<ChatHome> {
children: <Widget>[ children: <Widget>[
myTab(LocaleKeys.mychats.tr(), 0), myTab(LocaleKeys.mychats.tr(), 0),
myTab(LocaleKeys.favorite.tr(), 1), myTab(LocaleKeys.favorite.tr(), 1),
AppState().getempStatusIsManager ? myTab("My Team", 2) : const SizedBox(),
], ],
), ),
), ),
@ -95,6 +98,7 @@ class _ChatHomeState extends State<ChatHome> {
children: <Widget>[ children: <Widget>[
ChatHomeScreen(), ChatHomeScreen(),
ChatFavoriteUsersScreen(), ChatFavoriteUsersScreen(),
AppState().getempStatusIsManager ? const MyTeamScreen() : const SizedBox(),
], ],
).expanded, ).expanded,
], ],

@ -0,0 +1,189 @@
import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
class SeekBar extends StatefulWidget {
final Duration duration;
final Duration position;
final Duration bufferedPosition;
final ValueChanged<Duration>? onChanged;
final ValueChanged<Duration>? onChangeEnd;
const SeekBar({
Key? key,
required this.duration,
required this.position,
required this.bufferedPosition,
this.onChanged,
this.onChangeEnd,
}) : super(key: key);
@override
SeekBarState createState() => SeekBarState();
}
class SeekBarState extends State<SeekBar> {
double? _dragValue;
late SliderThemeData _sliderThemeData;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_sliderThemeData = SliderTheme.of(context).copyWith(
// trackHeight: 2.0,
thumbColor: MyColors.lightGreenColor,
activeTrackColor: MyColors.lightGreenColor,
inactiveTrackColor: MyColors.grey57Color.withOpacity(0.4),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SliderTheme(
data: _sliderThemeData.copyWith(
thumbShape: HiddenThumbComponentShape(),
),
child: ExcludeSemantics(
child: Slider(
min: 0.0,
max: widget.duration.inMilliseconds.toDouble(),
value: min(widget.bufferedPosition.inMilliseconds.toDouble(), widget.duration.inMilliseconds.toDouble()),
onChanged: (value) {
setState(() {
_dragValue = value;
});
if (widget.onChanged != null) {
widget.onChanged!(Duration(milliseconds: value.round()));
}
},
onChangeEnd: (value) {
if (widget.onChangeEnd != null) {
widget.onChangeEnd!(Duration(milliseconds: value.round()));
}
_dragValue = null;
},
),
),
),
SliderTheme(
data: _sliderThemeData.copyWith(
inactiveTrackColor: Colors.transparent,
),
child: Slider(
min: 0.0,
max: widget.duration.inMilliseconds.toDouble(),
value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(), widget.duration.inMilliseconds.toDouble()),
onChanged: (value) {
setState(() {
_dragValue = value;
});
if (widget.onChanged != null) {
widget.onChanged!(Duration(milliseconds: value.round()));
}
},
onChangeEnd: (value) {
if (widget.onChangeEnd != null) {
widget.onChangeEnd!(Duration(milliseconds: value.round()));
}
_dragValue = null;
},
),
),
],
);
}
}
class PositionData {
final Duration position;
final Duration bufferedPosition;
final Duration duration;
PositionData(this.position, this.bufferedPosition, this.duration);
}
class HiddenThumbComponentShape extends SliderComponentShape {
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) => Size.zero;
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
required bool isDiscrete,
required TextPainter labelPainter,
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required TextDirection textDirection,
required double value,
required double textScaleFactor,
required Size sizeWithOverflow,
}) {}
}
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,
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,
),
),
],
),
);
}
}

@ -0,0 +1,155 @@
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
class MyTeamScreen extends StatefulWidget {
const MyTeamScreen({Key? key}) : super(key: key);
@override
State<MyTeamScreen> createState() => _MyTeamScreenState();
}
class _MyTeamScreenState extends State<MyTeamScreen> {
late ChatProviderModel provider;
@override
void initState() {
super.initState();
provider = Provider.of<ChatProviderModel>(context, listen: false);
loadMembers();
}
void loadMembers(){
provider.getTeamMembers();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyColors.white,
body: Consumer<ChatProviderModel>(
builder: (BuildContext context, ChatProviderModel m, Widget? child) {
if (m.isLoading) {
return ChatHomeShimmer(
isDetailedScreen: false,
);
} else {
return m.teamMembersList != null && m.teamMembersList.isNotEmpty
? ListView.separated(
itemCount: m.teamMembersList!.length,
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(bottom: 80.0),
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 55,
child: Row(
children: [
Stack(
children: <Widget>[
if (m.teamMembersList![index].isImageLoading!)
const SizedBox(
height: 48,
width: 48,
).toShimmer().circle(30),
if (!m.teamMembersList![index].isImageLoading! && m.teamMembersList![index].userLocalDownlaodedImage == null)
SvgPicture.asset(
"assets/images/user.svg",
height: 48,
width: 48,
),
if (!m.teamMembersList![index].isImageLoading! && m.teamMembersList![index].userLocalDownlaodedImage != null)
Container(
width: 48.0,
height: 48.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.cover,
image: FileImage(m.teamMembersList![index].userLocalDownlaodedImage!),
),
),
),
Positioned(
right: 5,
bottom: 1,
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: m.teamMembersList![index].userStatus == 1 ? MyColors.green2DColor : Colors.red,
),
).circle(10),
)
],
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(m.teamMembersList![index].userName!.replaceFirst(".", " ").capitalizeFirstofEach ?? "").toText14(color: MyColors.darkTextColor).paddingOnly(left: 11, top: 13),
],
).expanded,
// SizedBox(
// width: 60,
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.end,
// mainAxisSize: MainAxisSize.max,
// children: <Widget>[
// Icon(
// m.teamMembersList![index].isFav! ? Icons.star : Icons.star_border,
// color: m.teamMembersList![index].isFav! ? MyColors.yellowColor : MyColors.grey35Color,
// ).onPress(() {
// if (m.teamMembersList![index].isFav!) {
// m.unFavoriteUser(
// userID: AppState().chatDetails!.response!.id!,
// targetUserID: m.teamMembersList![index].id!,
// );
// }
// }).center,
// ],
// ),
// ),
],
),
).onPress(() {
print(jsonEncode(m.teamMembersList[index]));
// Navigator.pushNamed(
// context,
// AppRoutes.chatDetailed,
// arguments: ChatDetailedScreenParams(m.teamMembersList![index], true),
// ).then(
// (Object? value) {
// m.clearSelections();
// },
// );
});
},
separatorBuilder: (BuildContext context, int index) => const Divider(color: MyColors.lightGreyE5Color).paddingOnly(left: 70),
).paddingAll(21)
: Column(
children: <Widget>[
Utils.getNoDataWidget(context).expanded,
],
);
}
},
),
);
}
}

@ -94,7 +94,7 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
searchText, searchText,
int.parse(AppState().chatDetails!.response!.id.toString()), int.parse(AppState().chatDetails!.response!.id.toString()),
); );
chatUsersList!.removeWhere((element) => element.id == AppState().chatDetails!.response!.id); chatUsersList!.removeWhere((ChatUser element) => element.id == AppState().chatDetails!.response!.id);
Utils.hideLoading(context); Utils.hideLoading(context);
setState(() {}); setState(() {});
} catch (e) { } catch (e) {

@ -94,9 +94,8 @@ dependencies:
camera: ^0.10.0+4 camera: ^0.10.0+4
#Chat Voice Message Recoding & Play #Chat Voice Message Recoding & Play
# record: ^4.4.3
audio_waveforms: ^0.1.5+1 audio_waveforms: ^0.1.5+1
# animated_text_kit: ^4.2.2 rxdart: ^0.27.7
#Encryption #Encryption
flutter_des: ^2.1.0 flutter_des: ^2.1.0
@ -106,6 +105,7 @@ dependencies:
safe_device: ^1.1.2 safe_device: ^1.1.2
flutter_layout_grid: ^2.0.1 flutter_layout_grid: ^2.0.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

Loading…
Cancel
Save