diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index 1f97b1f..bea5b7c 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -9,6 +9,7 @@ import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/exceptions/api_exception.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as user; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; @@ -152,16 +153,12 @@ class ChatApiClient { return data; } - Future getUsersImages({required List encryptedEmails}) async { + Future> getUsersImages({required List encryptedEmails}) async { Response response = await ApiClient().postJsonForResponse( "${ApiConsts.chatUserImages}images", - { - "encryptedEmails": ["/g8Rc+s6eEOdci41PwJuV5dX+gXe51G9OTHzb9ahcVlHCmVvNhxReirudF79+hdxVSkCnQ6wC5DBFV8xnJlC74X6157PxF7mNYrAYuHRgp4="], - "fromClient": true - }, + {"encryptedEmails": encryptedEmails, "fromClient": false}, token: AppState().chatDetails!.response!.token, ); - logger.d(response.body); - // Uint8List data = Uint8List.fromList(response.body); + return chatUserImageModelFromJson(response.body); } } diff --git a/lib/classes/encryption.dart b/lib/classes/encryption.dart index 67466fe..f26c92b 100644 --- a/lib/classes/encryption.dart +++ b/lib/classes/encryption.dart @@ -1,188 +1,28 @@ -// import 'dart:convert'; -// -// import 'package:encrypt/encrypt.dart'; -// import 'package:crypto/crypto.dart'; -// -// class Encryption { -// static final Encryption instance = Encryption._(); -// -// late IV _iv; -// late Encrypter _encrypter; -// -// Encryption._() { -// const mykey = 'PeShVmYp'; -// const myiv = 'j70IbWYn'; -// var keyUtf8 = utf8.encode(mykey); -// var ivUtf8 = utf8.encode(myiv); -// var key = sha256.convert(keyUtf8).toString().substring(0, 32); -// var iv = sha256.convert(ivUtf8).toString().substring(0, 16); -// _iv = IV.fromUtf8(iv); -// -// _encrypter = Encrypter(AES(Key.fromUtf8(key), mode: AESMode.cbc)); -// } -// -// String encrypt(String value) { -// return _encrypter.encrypt(value, iv: _iv).base64; -// } -// -// String decrypt(String base64value) { -// var encrypted = Encrypted.fromBase64(base64value); -// return _encrypter.decrypt(encrypted, iv: _iv); -// } -// -// // -// // void passEncrypt(String text, String pass) async { -// // var salt = randomUint8List(8); -// // var keyndIV = deriveKeyAndIV(pass, salt); -// // var key = encrypt.Key(keyndIV.item1); -// // var iv = encrypt.IV(keyndIV.item2); -// // var encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); -// // var encrypted = encrypter.encrypt(text, iv: iv); -// // Uint8List encryptedBytesWithSalt = Uint8List.fromList(createUint8ListFromString("Salted__") + salt + encrypted.bytes); -// // var resulttt = base64.encode(encryptedBytesWithSalt); -// // print("Enc : " + resulttt); -// // -// // decryptAESCryptoJS(resulttt, pass); -// // } -// // -// // Uint8List randomUint8List(int length) { -// // assert(length > 0); -// // var random = Random(); -// // var ret = Uint8List(length); -// // for (var i = 0; i < length; i++) { -// // ret[i] = random.nextInt(256); -// // } -// // return ret; -// // } -// // -// // void decryptAESCryptoJS(String encrypted, String passphrase) { -// // try { -// // Uint8List encryptedBytesWithSalt = base64.decode(encrypted); -// // Uint8List encryptedBytes = encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length); -// // var salt = encryptedBytesWithSalt.sublist(8, 16); -// // var keyndIV = deriveKeyAndIV(passphrase, salt); -// // var key = encrypt.Key(keyndIV.item1); -// // var iv = encrypt.IV(keyndIV.item2); -// // var encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7")); -// // var decrypted = encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv); -// // print("Dec : " + decrypted); -// // // return decrypted; -// // } catch (error) { -// // throw error; -// // } -// // } -// // -// // Future enc(String input) async { -// // var ekey = "PeShVmYpPeShVmYp"; -// // var eIV = "j70IbWYnj70IbWYn"; -// // -// // var abc = Encryption.instance.encrypt(input); -// // // -// // // var inputByt = utf8.encode(input); -// // // final encrypted = encrypter.encrypt(plainText); -// // // final decrypted = encrypter.decrypt(encrypted); -// // // -// // // print(decrypted); // Lorem ipsum dolor sit amet, consectetur adipiscing elit -// // // print(encrypted.base64); // R4PxiU3h8YoIRqVowBXm36ZcCeNeZ4s1OvVBTfFlZRdmohQqOpPQqD1YecJeZMAop/hZ4OxqgC1WtwvX/hP9mw== -// // // /// -// // -// // var key = encrypt.Key.fromUtf8(ekey.substring(0, 8)); -// // var iv = encrypt.IV.fromLength(8); -// // var encrypter = Encrypter(AES(key)); -// // var encrypted = encrypter.encrypt(input, iv: iv); -// // var decrypted = encrypter.decrypt(encrypted, iv: iv); -// // print("====== ORI =========="); -// // print("e4PTDencHsiLJv0XcbT2I4tafb7dqJP9c72PEnhp1Uv6U2GZ/gODtA=="); -// // print("====== ENC =========="); -// // print(encrypted.base64); -// // print("====== DEC =========="); -// // print(decrypted); -// // print("====== B64 =========="); -// // String bs64 = base64.encode(utf8.encode(input)); -// // print(bs64); -// // -// // // try { -// // // var cipher = _cipher; -// // // var secretBox = await cipher.encrypt( -// // // iByt, -// // // secretKey: SecretKeyData(eByt), -// // // nonce: eIvByt, -// // // ); -// // // print(utf8.decode(secretBox.cipherText)); -// // // } catch (error) { -// // // print(error); -// // // return; -// // // } -// // } -// // ///Accepts encrypted data and decrypt it. Returns plain text -// // String decryptWithAES(String key, Encrypted encryptedData) { -// // var cipherKey = encrypt.Key.fromUtf8(key); -// // var encryptService = Encrypter(AES(cipherKey, mode: AESMode.cbc,padding: null)); -// // var initVector = IV.fromUtf8(key.substring(0, 16)); -// // return encryptService.decrypt(encryptedData, iv: initVector); -// // } -// // -// // ///Encrypts the given plainText using the key. Returns encrypted data -// // Encrypted encryptWithAES(String key, String plainText) { -// // var cipherKey = encrypt.Key.fromUtf8(key); -// // var encryptService = Encrypter(AES(cipherKey, mode: AESMode.cbc,padding: null)); -// // var initVector = IV.fromUtf8("j70IbWYn"); -// // Encrypted encryptedData = encryptService.encrypt(plainText, iv: initVector); -// // print(encryptedData.base64); -// // return encryptedData; -// // } -// // -// // Tuple2 deriveKeyAndIV(String passphrase, Uint8List salt) { -// // var password = createUint8ListFromString(passphrase); -// // Uint8List concatenatedHashes = Uint8List(0); -// // Uint8List currentHash = Uint8List(0); -// // bool enoughBytesForKey = false; -// // Uint8List preHash = Uint8List(0); -// // -// // while (!enoughBytesForKey) { -// // int preHashLength = currentHash.length + password.length + salt.length; -// // if (currentHash.length > 0) -// // preHash = Uint8List.fromList(currentHash + password + salt); -// // else -// // preHash = Uint8List.fromList(password + salt); -// // -// // currentHash = preHash; -// // concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash); -// // if (concatenatedHashes.length >= 48) enoughBytesForKey = true; -// // } -// // -// // var keyBtyes = concatenatedHashes.sublist(0, 32); -// // var ivBtyes = concatenatedHashes.sublist(32, 48); -// // return new Tuple2(keyBtyes, ivBtyes); -// // } -// // -// // Uint8List createUint8ListFromString(String s) { -// // var ret = new Uint8List(s.length); -// // for (var i = 0; i < s.length; i++) { -// // ret[i] = s.codeUnitAt(i); -// // } -// // return ret; -// // } -// // -// // Uint8List genRandomWithNonZero(int seedLength) { -// // var random = Random.secure(); -// // const int randomMax = 245; -// // Uint8List uint8list = Uint8List(seedLength); -// // for (int i = 0; i < seedLength; i++) { -// // uint8list[i] = random.nextInt(randomMax) + 1; -// // } -// // return uint8list; -// // } -// // -// // -// // -// // void test(String text, String kk) { -// // Uint8List key = Uint8List.fromList(utf8.encode(kk)); -// // PaddedBlockCipher cipher = exp.PaddedBlockCipherImpl(exp.PKCS7Padding(), exp.ECBBlockCipher(exp.AESEngine())); -// // cipher.init(true, PaddedBlockCipherParameters(KeyParameter(key), null)); -// // var byte = Uint8List.fromList(utf8.encode(text)); -// // var data = cipher.process(byte); -// // print(data); -// // } -// -// } +import 'dart:convert'; + +import 'package:flutter/services.dart'; + +class Encryption { + static final Encryption _instance = Encryption._internal(); + static const MethodChannel _channel = MethodChannel('flutter_des'); + static const key = "PeShVmYp"; + static const iv = "j70IbWYn"; + + Encryption._internal(); + + factory Encryption() => _instance; + + Future encrypt({required String val}) async { + Uint8List? crypt = await _channel.invokeMethod('encrypt', [val, key, iv]); + String data = base64Encode(crypt!.toList()); + print("Base64Enc: " + data); + return data; + } + + Future decrypt({required String encodedVal}) async { + var deco = base64Decode(encodedVal); + String? decCrypt = await _channel.invokeMethod('decrypt', [deco, key, iv]); + print("Base64ToStringDec: " + decCrypt!); + return decCrypt!; + } +} diff --git a/lib/models/chat/chat_user_image_model.dart b/lib/models/chat/chat_user_image_model.dart new file mode 100644 index 0000000..cdee33d --- /dev/null +++ b/lib/models/chat/chat_user_image_model.dart @@ -0,0 +1,33 @@ +// To parse this JSON data, do +// +// final chatUserImageModel = chatUserImageModelFromJson(jsonString); + +import 'dart:convert'; + +List chatUserImageModelFromJson(String str) => List.from(json.decode(str).map((x) => ChatUserImageModel.fromJson(x))); + +String chatUserImageModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); + +class ChatUserImageModel { + ChatUserImageModel({ + this.email, + this.profilePicture, + this.mobileNumber, + }); + + String? email; + String? profilePicture; + String? mobileNumber; + + factory ChatUserImageModel.fromJson(Map json) => ChatUserImageModel( + email: json["email"] == null ? null : json["email"], + profilePicture: json["profilePicture"] == null ? null : json["profilePicture"], + mobileNumber: json["mobileNumber"] == null ? null : json["mobileNumber"], + ); + + Map toJson() => { + "email": email == null ? null : email, + "profilePicture": profilePicture == null ? null : profilePicture, + "mobileNumber": mobileNumber == null ? null : mobileNumber, + }; +} diff --git a/lib/models/chat/get_search_user_chat_model.dart b/lib/models/chat/get_search_user_chat_model.dart index 31d1085..fe87061 100644 --- a/lib/models/chat/get_search_user_chat_model.dart +++ b/lib/models/chat/get_search_user_chat_model.dart @@ -19,21 +19,23 @@ class ChatUserModel { } class ChatUser { - ChatUser( - {this.id, - this.userName, - this.email, - this.phone, - this.title, - this.userStatus, - this.image, - this.unreadMessageCount, - this.userAction, - this.isPin, - this.isFav, - this.isAdmin, - this.isTyping, - this.isLoadingCounter}); + ChatUser({ + this.id, + this.userName, + this.email, + this.phone, + this.title, + this.userStatus, + this.image, + this.unreadMessageCount, + this.userAction, + this.isPin, + this.isFav, + this.isAdmin, + this.isTyping, + this.isImageLoaded, + this.isImageLoading, + }); int? id; String? userName; @@ -48,7 +50,8 @@ class ChatUser { bool? isFav; bool? isAdmin; bool? isTyping; - bool? isLoadingCounter; + bool? isImageLoaded; + bool? isImageLoading; factory ChatUser.fromJson(Map json) => ChatUser( id: json["id"] == null ? null : json["id"], @@ -64,7 +67,8 @@ class ChatUser { isFav: json["isFav"] == null ? null : json["isFav"], isAdmin: json["isAdmin"] == null ? null : json["isAdmin"], isTyping: false, - isLoadingCounter: true, + isImageLoaded: false, + isImageLoading: true, ); Map toJson() => { diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index b48d845..c56241f 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -7,8 +7,10 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/encryption.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; @@ -75,6 +77,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { ), ); notifyListeners(); + getUserImages(); } Future invokeUserChatHistoryNotDeliveredAsync({required int userId}) async { @@ -105,7 +108,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } isLoading = false; notifyListeners(); - markRead(userChatHistory, receiverUID); + // markRead(userChatHistory, receiverUID); generateConvId(); } @@ -231,7 +234,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { int? val = element.unreadMessageCount ?? 0; element.unreadMessageCount = val! + 1; } - element.isLoadingCounter = false; }, ); } @@ -698,4 +700,33 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // } // } + void getUserImages() async { + List emails = []; + for (ChatUser element in searchedChats!) { + var encMail = await Encryption().encrypt(val: element.email!); + emails.add(encMail); + } + List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); + for (ChatUser user in searchedChats!) { + for (ChatUserImageModel uImage in chatImages) { + if (user.email == uImage.email) { + user.image = uImage.profilePicture ?? ""; + user.isImageLoading = false; + user.isImageLoaded = true; + notifyListeners(); + } + } + } + + for (ChatUser favUser in favUsersList) { + for (ChatUserImageModel uImage in chatImages) { + if (favUser.email == uImage.email) { + favUser.image = uImage.profilePicture ?? ""; + favUser.isImageLoading = false; + favUser.isImageLoaded = true; + notifyListeners(); + } + } + } + } } diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index 1d220bc..6f9e4cf 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -68,6 +68,7 @@ class _ChatDetailScreenState extends State { isNewChat: userDetails["isNewChat"], ); } + print("Img: "+ userDetails["targetUser"].image); return Scaffold( backgroundColor: MyColors.backgroundColor, @@ -75,7 +76,7 @@ class _ChatDetailScreenState extends State { context, title: userDetails["targetUser"].userName.toString().replaceAll(".", " ").capitalizeFirstofEach, showHomeButton: false, - image: userDetails["targetUser"].image, + image: userDetails["targetUser"].image.isEmpty ? null : userDetails["targetUser"].image, actions: [ SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() { // makeCall(callType: "AUDIO", con: hubConnection); diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index 3bf8cda..1ae3252 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -27,6 +27,8 @@ class _ChatHomeState extends State { void initState() { super.initState(); data = Provider.of(context, listen: false); + data.registerEvents(); + data.getUserRecentChats(); } @override diff --git a/lib/ui/chat/chat_home_screen.dart b/lib/ui/chat/chat_home_screen.dart index e2f2dc2..5ccc6ad 100644 --- a/lib/ui/chat/chat_home_screen.dart +++ b/lib/ui/chat/chat_home_screen.dart @@ -1,10 +1,14 @@ import 'dart:convert'; +import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_des/flutter_des.dart'; import 'package:flutter_svg/flutter_svg.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/encryption.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'; @@ -12,6 +16,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/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -26,11 +31,8 @@ class _ChatHomeScreenState extends State { @override void initState() { - // TODO: implement initState super.initState(); data = Provider.of(context, listen: false); - data.registerEvents(); - data.getUserRecentChats(); } @override @@ -89,11 +91,26 @@ class _ChatHomeScreenState extends State { children: [ Stack( children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 48, - width: 48, - ), + if (m.searchedChats![index].isImageLoading!) + const SizedBox( + height: 48, + width: 48, + child: Center(child: CircularProgressIndicator()), + ), + if (m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image != null && m.searchedChats![index].image.isNotEmpty) + CircularAvatar( + radius: 20, + height: 48, + width: 48, + url: m.searchedChats![index].image, + isImageBase64: true, + ), + if (!m.searchedChats![index].isImageLoading! && m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image.isEmpty) + SvgPicture.asset( + "assets/images/user.svg", + height: 48, + width: 48, + ), Positioned( right: 5, bottom: 1, diff --git a/lib/ui/chat/favorite_users_screen.dart b/lib/ui/chat/favorite_users_screen.dart index 6bc0040..8bfcc4a 100644 --- a/lib/ui/chat/favorite_users_screen.dart +++ b/lib/ui/chat/favorite_users_screen.dart @@ -9,6 +9,7 @@ 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/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -36,11 +37,26 @@ class ChatFavoriteUsersScreen extends StatelessWidget { children: [ Stack( children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 48, - width: 48, - ), + if (m.favUsersList![index].isImageLoading!) + const SizedBox( + height: 48, + width: 48, + child: Center(child: CircularProgressIndicator()), + ), + if (m.favUsersList![index].isImageLoaded! && m.favUsersList![index].image != null && m.favUsersList![index].image.isNotEmpty) + CircularAvatar( + radius: 20, + height: 48, + width: 48, + url: m.favUsersList![index].image, + isImageBase64: true, + ), + if (!m.favUsersList![index].isImageLoading! && m.favUsersList![index].isImageLoaded! && m.favUsersList![index].image.isEmpty) + SvgPicture.asset( + "assets/images/user.svg", + height: 48, + width: 48, + ), Positioned( right: 5, bottom: 1, @@ -49,11 +65,8 @@ class ChatFavoriteUsersScreen extends StatelessWidget { height: 10, decoration: BoxDecoration( color: m.favUsersList![index].userStatus == 1 ? MyColors.green2DColor : Colors.red, - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), ), - ), + ).circle(10), ) ], ), diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart index c2129c1..9cd7e27 100644 --- a/lib/widgets/app_bar_widget.dart +++ b/lib/widgets/app_bar_widget.dart @@ -5,6 +5,7 @@ import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; AppBar AppBarWidget(BuildContext context, {required String title, bool showHomeButton = true, bool showNotificationButton = false, bool showMemberButton = false, String? image, List? actions}) { @@ -25,12 +26,20 @@ AppBar AppBarWidget(BuildContext context, ), 4.width, if (image != null) + CircularAvatar( + url: image, + height: 40, + width: 40, + isImageBase64: true, + ), + if (image == null) SvgPicture.asset( - image, + "assets/images/user.svg", height: 40, width: 40, ), if (image != null) 14.width, + if (image == null) 14.width, title.toText24(color: MyColors.darkTextColor, isBold: true).expanded, ], ), @@ -59,7 +68,7 @@ AppBar AppBarWidget(BuildContext context, }, icon: const Icon(Icons.people, color: MyColors.textMixColor), ), - ...actions??[] + ...actions ?? [] ], ); } diff --git a/pubspec.yaml b/pubspec.yaml index f17ce5f..4fc00c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,6 +93,8 @@ dependencies: flutter_webrtc: ^0.9.16 camera: ^0.10.0+4 + #Encryption + flutter_des: ^2.1.0 video_player: ^2.4.7 just_audio: ^0.9.30