diff --git a/android/app/agconnect-services.json b/android/app/agconnect-services.json new file mode 100644 index 0000000..20a7546 --- /dev/null +++ b/android/app/agconnect-services.json @@ -0,0 +1,57 @@ +{ + "agcgw_all":{ + "CN":"connect-drcn.dbankcloud.cn", + "CN_back":"connect-drcn.hispace.hicloud.com", + "DE":"connect-dre.dbankcloud.cn", + "DE_back":"connect-dre.hispace.hicloud.com", + "RU":"connect-drru.hispace.dbankcloud.ru", + "RU_back":"connect-drru.hispace.dbankcloud.cn", + "SG":"connect-dra.dbankcloud.cn", + "SG_back":"connect-dra.hispace.hicloud.com" + }, + "websocketgw_all":{ + "CN":"connect-ws-drcn.hispace.dbankcloud.cn", + "CN_back":"connect-ws-drcn.hispace.dbankcloud.com", + "DE":"connect-ws-dre.hispace.dbankcloud.cn", + "DE_back":"connect-ws-dre.hispace.dbankcloud.com", + "RU":"connect-ws-drru.hispace.dbankcloud.ru", + "RU_back":"connect-ws-drru.hispace.dbankcloud.cn", + "SG":"connect-ws-dra.hispace.dbankcloud.cn", + "SG_back":"connect-ws-dra.hispace.dbankcloud.com" + }, + "client":{ + "cp_id":"2640966000002322881", + "product_id":"737518067793559971", + "client_id":"715996003571874624", + "client_secret":"B5B89A56A53847C6BB9D216A8747E75952760DF9A8232239D8744CD847A8FFDA", + "project_id":"737518067793559971", + "app_id":"104737117", + "api_key":"DAEDACKDrYgyco9mjPV9ZUjCSh1kCr/GBV0nseHH0z2mnxlZ41RksOKmyTi+PUTwmGEPK+VxCup4F9oUf4VbDnCsjB7aNBShYcjR+g==", + "package_name":"hmg.cloudSolutions.mohem" + }, + "oauth_client":{ + "client_id":"104737117", + "client_type":1 + }, + "app_info":{ + "app_id":"104737117", + "package_name":"hmg.cloudSolutions.mohem" + }, + "configuration_version":"3.0", + "appInfos":[ + { + "package_name":"hmg.cloudSolutions.mohem", + "client":{ + "app_id":"104737117" + }, + "app_info":{ + "package_name":"hmg.cloudSolutions.mohem", + "app_id":"104737117" + }, + "oauth_client":{ + "client_type":1, + "client_id":"104737117" + } + } + ] +} \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 198bc87..5590740 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -31,6 +31,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'com.google.gms.google-services' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +apply plugin: 'com.huawei.agconnect' android { compileSdkVersion 33 @@ -51,13 +52,19 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "hmg.cloudSolutions.mohem" - minSdkVersion 21 + minSdkVersion 28 targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } signingConfigs { + debug { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] @@ -68,6 +75,9 @@ android { buildTypes { release { signingConfig signingConfigs.release + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f187ebd..3f5e424 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -7,12 +7,21 @@ - + + + + + + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSAllowsArbitraryLoadsForMedia + + NSAllowsArbitraryLoadsInWebContent + + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -33,7 +42,7 @@ NFCReaderUsageDescription This App requires access to NFC to mark your attendance. NSCameraUsageDescription - This app requires camera access to capture & upload pictures. + This app requires camera access to capture & upload picture as profile image. NSFaceIDUsageDescription This app requires Face ID to allow biometric authentication for app login. NSLocationAlwaysAndWhenInUseUsageDescription @@ -46,15 +55,13 @@ This app requires photo library access to select image as document & upload it. NSMicrophoneUsageDescription This app requires microphone access to for call. - NSPhotoLibraryUsageDescription - This app requires photo library access to select image as document & upload it. UIBackgroundModes - fetch + fetch remote-notification FirebaseAppDelegateProxyEnabled - + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -80,14 +87,9 @@ ITSAppUsesNonExemptEncryption - com.apple.developer.nfc.readersession.formats TAG - com.apple.developer.nfc.readersession.felica.systemcodes - - 0000 - diff --git a/lib/api/chat/chat_api_client.dart b/lib/api/chat/chat_api_client.dart index b4c0a2c..f727c7d 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -31,7 +31,8 @@ class ChatApiClient { "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(), "password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG", "isMobile": true, - "deviceToken": AppState().getDeviceToken, + "deviceToken":AppState().getIsHuawei ? AppState().getHuaweiPushToken : AppState().getDeviceToken, + "isHuaweiDevice": AppState().getIsHuawei, }, ); diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 95631a9..76b10ce 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -111,6 +111,7 @@ class DashboardApiClient { // Generate a v4 (random) id Map postParams = { + // "UID": await PlatformDeviceId.getDeviceId, //uuid.v4(), //Mobile Id "UID": uuid.v4(), //Mobile Id "Latitude": lat, "Longitude": long, @@ -199,17 +200,18 @@ class DashboardApiClient { }, url, postParams); } -// Future setAdvertisementViewed(String masterID, int advertisementId) async { -// String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed"; -// -// Map postParams = { -// "ItgNotificationMasterId": masterID, -// "ItgAdvertisement": {"advertisementId": advertisementId, "acknowledgment": true} //Mobile Id -// }; -// postParams.addAll(AppState().postParamsJson); -// return await ApiClient().postJsonForObject((json) { -// // ItgMainRes responseData = ItgMainRes.fromJson(json); -// return json; -// }, url, postParams); -// } + Future setAdvertisementViewed(String masterID, int advertisementId) async { + String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed"; + + Map postParams = { + "ItgNotificationMasterId": masterID, + "EmployeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(), + "ItgAdvertisement": {"advertisementId": advertisementId, "acknowledgment": true} //Mobile Id + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + // ItgMainRes responseData = ItgMainRes.fromJson(json); + return json; + }, url, postParams); + } } diff --git a/lib/api/marathon/marathon_api_client.dart b/lib/api/marathon/marathon_api_client.dart index ee3810f..9497b48 100644 --- a/lib/api/marathon/marathon_api_client.dart +++ b/lib/api/marathon/marathon_api_client.dart @@ -34,6 +34,20 @@ class MarathonApiClient { ); } + Future getMarathonersCount({required String marathonId}) async { + Response response = await ApiClient().getJsonForResponse( + ApiConsts.marathonGetMarathonersCount + '?marathonId=$marathonId', + token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken, + ); + + var json = jsonDecode(response.body); + logger.i("json in getMarathonersCount: $json"); + + MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json); + + return marathonGenericModel.data as int; + } + Future getProjectId() async { return await ApiClient().postJsonForObject( (json) { @@ -89,6 +103,7 @@ class MarathonApiClient { ); } + Future getNextQuestion({required String? questionId, required String marathonId}) async { Map jsonObject = { "previousQuestionId": questionId, diff --git a/lib/api/worklist/worklist_api_client.dart b/lib/api/worklist/worklist_api_client.dart index 82b865c..d55dc8d 100644 --- a/lib/api/worklist/worklist_api_client.dart +++ b/lib/api/worklist/worklist_api_client.dart @@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/models/get_mo_notification_body_list_model.dar import 'package:mohem_flutter_app/models/get_notification_buttons_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_Item_history_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_notification_body_list_model.dart'; +import 'package:mohem_flutter_app/models/get_pr_information_list.dart'; import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_quotation_analysis_list_model.dart'; import 'package:mohem_flutter_app/models/get_stamp_ms_notification_body_list_model.dart'; @@ -626,4 +627,19 @@ class WorkListApiClient { return responseData.updateUserItemTypesList; }, url, postParams); } + + Future getPRDetailsForPO(String poLineID) async { + String url = "${ApiConsts.erpRest}GET_PR_INFORMATION"; + Map postParams = { + "P_PO_LINE_ID": poLineID, + "P_PAGE_LIMIT": 100, + "P_PAGE_NUM": 1, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.getPRInformationList; + }, url, postParams); + } + } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index cf7b96c..23be328 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; -import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/request_detail_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; @@ -79,7 +78,19 @@ class AppState { bool get getIsDemoMarathon => _isDemoMarathon; - final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 4.0, mobileType: Platform.isAndroid ? "android" : "ios"); + bool _isHuawei = false; + + set setIsHuawei(bool value) => _isHuawei = value; + + bool get getIsHuawei => _isHuawei; + + String _huaweiPushToken = ""; + + set setHuaweiPushToken(String value) => _huaweiPushToken = value; + + String get getHuaweiPushToken => _huaweiPushToken; + + final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 4.4, mobileType: Platform.isAndroid ? "android" : "ios"); void setPostParamsInitConfig() { isAuthenticated = false; diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 96fd4b0..688fd70 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -37,6 +37,7 @@ class ApiConsts { static String marathonSubmitAnswerUrl = marathonBaseUrl + "question/submit"; static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/"; static String marathonSelectedWinner = marathonBaseUrl + "winner/getSelectedWinner/"; + static String marathonGetMarathonersCount = marathonBaseUrl + "Participant/GetRemainingParticipants"; //DummyCards for the UI static CardContent dummyQuestion = const CardContent(); diff --git a/lib/classes/date_uitl.dart b/lib/classes/date_uitl.dart index 3cae1cd..89e237d 100644 --- a/lib/classes/date_uitl.dart +++ b/lib/classes/date_uitl.dart @@ -60,13 +60,13 @@ class DateUtil { } } date = date + " $hours:$mins:$secs"; - DateTime returnDate = DateFormat("MM/dd/yyyy HH:mm:ss").parse(date); + DateTime returnDate = DateFormat("MM/dd/yyyy HH:mm:ss", "en_US").parse(date); return returnDate; } static DateTime convertSimpleStringDateToDateddMMyyyy(String date) { - return DateFormat("MM/dd/yyyy hh:mm:ss").parse(date); + return DateFormat("MM/dd/yyyy hh:mm:ss", "en_US").parse(date); } static DateTime convertStringToDateNoTimeZone(String date) { @@ -123,7 +123,7 @@ class DateUtil { } static String formatDateToTime(DateTime date) { - return DateFormat('hh:mm a').format(date); + return DateFormat('hh:mm a', "en_US").format(date); } static String yearMonthDay(DateTime dateTime) { @@ -419,7 +419,7 @@ class DateUtil { /// [dateTime] convert DateTime to data formatted static String getDayMonthDateFormatted(DateTime dateTime) { if (dateTime != null) - return DateFormat('dd/MM').format(dateTime); + return DateFormat('dd/MM', "en_US").format(dateTime); else return ""; } @@ -466,7 +466,7 @@ class DateUtil { /// [dateTime] convert DateTime to data formatted static String getDayMonthYearHourMinuteDateFormatted(DateTime dateTime) { if (dateTime != null) - return dateTime.day.toString() + "/" + dateTime.month.toString() + "/" + dateTime.year.toString() + " " + DateFormat('HH:mm').format(dateTime); + return dateTime.day.toString() + "/" + dateTime.month.toString() + "/" + dateTime.year.toString() + " " + DateFormat('HH:mm', "en_US").format(dateTime); else return ""; } @@ -491,11 +491,11 @@ class DateUtil { } static String getFormattedDate(DateTime dateTime, String formattedString) { - return DateFormat(formattedString).format(dateTime); + return DateFormat(formattedString, "en_US").format(dateTime); } static String convertISODateToJsonDate(String isoDate) { - return "/Date(" + DateFormat('mm-dd-yyy').parse(isoDate).millisecondsSinceEpoch.toString() + ")/"; + return "/Date(" + DateFormat('mm-dd-yyy', "en_US").parse(isoDate).millisecondsSinceEpoch.toString() + ")/"; } // static String getDay(DayOfWeek dayOfWeek) { diff --git a/lib/classes/lottie_consts.dart b/lib/classes/lottie_consts.dart index bcc7149..e112243 100644 --- a/lib/classes/lottie_consts.dart +++ b/lib/classes/lottie_consts.dart @@ -9,4 +9,5 @@ class MyLottieConsts { static const String congratsGif = "assets/images/congrats.gif"; static const String loadingLottie = "assets/lottie/loading_lottie.json"; static const String noWinnerLottie = "assets/lottie/no_winner.json"; + static const String audioPlaybackLottie = "assets/lottie/audio_playback.json"; } diff --git a/lib/classes/my_custom_stream.dart b/lib/classes/my_custom_stream.dart new file mode 100644 index 0000000..2a0927f --- /dev/null +++ b/lib/classes/my_custom_stream.dart @@ -0,0 +1,21 @@ +import 'package:flutter/foundation.dart'; +import 'package:just_audio/just_audio.dart'; + +class MyCustomStream extends StreamAudioSource { + final Uint8List bytes; + + MyCustomStream(this.bytes); + + @override + Future request([int? start, int? end]) async { + start ??= 0; + end ??= bytes.length; + return StreamAudioResponse( + sourceLength: bytes.length, + contentLength: end - start, + offset: start, + stream: Stream.value(bytes.sublist(start, end)), + contentType: 'audio/aac', + ); + } +} \ No newline at end of file diff --git a/lib/classes/notifications.dart b/lib/classes/notifications.dart index cebf76d..e3d7997 100644 --- a/lib/classes/notifications.dart +++ b/lib/classes/notifications.dart @@ -1,13 +1,14 @@ -import 'dart:convert'; import 'dart:io'; + +import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart'; +import 'package:huawei_push/huawei_push.dart' as huawei_push; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; -import 'package:mohem_flutter_app/main.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:firebase_core/firebase_core.dart'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); @@ -18,6 +19,10 @@ class AppNotifications { factory AppNotifications() => _instance; + // late HmsApiAvailability hmsApiAvailability; + + String _huaweiToken = ''; + Future requestPermissions() async { if (Platform.isIOS) { await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation()?.requestPermissions(alert: true, badge: true, sound: true); @@ -35,6 +40,10 @@ class AppNotifications { } void init(String? firebaseToken) async { + // if (Platform.isAndroid) { + // hmsApiAvailability = HmsApiAvailability(); + // } + await requestPermissions(); AppState().setDeviceToken = firebaseToken; await Permission.notification.isDenied.then((bool value) { @@ -57,10 +66,53 @@ class AppNotifications { FirebaseMessaging.instance.onTokenRefresh.listen((String token) { AppState().setDeviceToken = token; }); + + if (Platform.isAndroid) { + // await hmsApiAvailability.isHMSAvailable().then((value) async { + if (!(await Utils.isGoogleServicesAvailable())) { + + huawei_push.Push.enableLogger(); + var result = await huawei_push.Push.setAutoInitEnabled(true); + + huawei_push.Push.onNotificationOpenedApp.listen((message) { + // newMessage(toFirebaseRemoteMessage(message)); + }, onError: (e) => print(e.toString())); + + huawei_push.Push.onMessageReceivedStream.listen((message) { + // newMessage(toFirebaseRemoteMessage(message)); + }, onError: (e) => print(e.toString())); + } + // }).catchError((err) { + // print(err); + // }); + } + } + + void initHuaweiPush(Function loginCallback) { + AppState().setIsHuawei = true; + initTokenStream(loginCallback); + huawei_push.Push.getToken(""); + } + + // HUAWEI PUSH TOKEN IMPLEMENTATION + void _onTokenEvent(String event) { + _huaweiToken = event; + AppState().setHuaweiPushToken = _huaweiToken; + debugPrint("HUAWEI PUSH TOKEN: $_huaweiToken"); + } + + void _onTokenError(Object error) {} + + Future initTokenStream(Function loginCallback) async { + huawei_push.Push.getTokenStream.listen(_onTokenEvent, onError: _onTokenError).onData((data) { + AppState().setHuaweiPushToken = data; + debugPrint("HUAWEI PUSH TOKEN: $data"); + loginCallback(); + }); } void _handleMessage(RemoteMessage message) { - Utils.saveStringFromPrefs("isAppOpendByChat", "true"); + Utils.saveStringFromPrefs("isAppOpendByChat", "false"); } void _handleOpenApp(RemoteMessage message) { @@ -77,7 +129,6 @@ AndroidNotificationChannel channel = const AndroidNotificationChannel( Future backgroundMessageHandler(RemoteMessage message) async { await Firebase.initializeApp(); - Utils.saveStringFromPrefs("isAppOpendByChat", "true"); - logger.w(message.data["user_chat_history_response"]); + Utils.saveStringFromPrefs("isAppOpendByChat", "false"); Utils.saveStringFromPrefs("notificationData", message.data["user_chat_history_response"].toString()); } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 5990edf..34831c2 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -7,6 +7,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_api_availability/google_api_availability.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/config/routes.dart'; @@ -115,12 +116,14 @@ class Utils { } else { if (!AppState().isAuthenticated) { showDialog( + barrierDismissible: false, context: cxt, builder: (cxt) => ConfirmDialog( message: errorMessage, onTap: () { Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route route) => false); }, + onCloseTap: () {}, ), ); } else { @@ -264,7 +267,7 @@ class Utils { static String getMonthNamedFormat(DateTime date) { /// it will return like "29-Sep-2022" - return DateFormat('dd-MMM-yyyy').format(date); + return DateFormat('dd-MMM-yyyy', "en_US").format(date); } static String reverseFormatDate(String date) { @@ -334,9 +337,9 @@ class Utils { return date; } else { if (date.toLowerCase().split("-")[1].length == 3) { - return DateFormat('dd-MM-yyyy').format(DateFormat('dd-MMM-yyyy').parseLoose(date)); + return DateFormat('dd-MM-yyyy', "en_US").format(DateFormat('dd-MMM-yyyy', "en_US").parseLoose(date)); } else { - return DateFormat('dd-MM-yyyy').format(DateFormat('yyyy-MM-dd').parseLoose(date)); + return DateFormat('dd-MM-yyyy', "en_US").format(DateFormat('yyyy-MM-dd', "en_US").parseLoose(date)); } // return DateFormat('yyyy-MM-dd').format(DateFormat('dd-MM-yyyy').parseLoose(date)); } @@ -388,4 +391,14 @@ class Utils { print(err); }); } + + //HUAWEI DECISION MAKING + static Future isGoogleServicesAvailable() async { + GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); + String status = availability.toString().split('.').last; + if (status == "success") { + return true; + } + return false; + } } diff --git a/lib/dialogs/otp_dialog.dart b/lib/dialogs/otp_dialog.dart index 9158b94..9f7c82e 100644 --- a/lib/dialogs/otp_dialog.dart +++ b/lib/dialogs/otp_dialog.dart @@ -9,7 +9,6 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/otp_widget.dart'; -import 'package:sizer/sizer.dart'; final ValueNotifier otpFieldClear = ValueNotifier(""); diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index b916f23..45cfc05 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -22,6 +22,17 @@ extension TrimString on String { } } +String displayLocalizedContent({required bool isPhoneLangArabic, required int selectedLanguage, required String englishContent, required String arabicContent}) { + if (selectedLanguage == 1) { + return englishContent; + } else if (selectedLanguage == 2) { + return arabicContent; + } else if (selectedLanguage == 3) { + return isPhoneLangArabic ? arabicContent : englishContent; + } + return englishContent; +} + extension EmailValidator on String { Widget get toWidget => Text(this); @@ -111,9 +122,10 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText16({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height}) => Text( + Widget toText16({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text( this, maxLines: maxlines, + textAlign: isCentered ? TextAlign.center : null, style: TextStyle( color: color ?? MyColors.darkTextColor, fontSize: 16, @@ -158,13 +170,15 @@ extension EmailValidator on String { style: TextStyle(height: 1, color: color ?? MyColors.darkTextColor, fontSize: 22, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); - Widget toText24({Color? color, bool isBold = false}) => Text( + Widget toText24({Color? color, bool isBold = false, bool isCentered = false}) => Text( this, + textAlign: isCentered ? TextAlign.center : null, style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 24, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); - Widget toText30({Color? color, bool isBold = false}) => Text( + Widget toText30({Color? color, bool isBold = false, bool isCentered = false}) => Text( this, + textAlign: isCentered ? TextAlign.center : null, style: TextStyle(height: 20 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.2, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); @@ -219,7 +233,7 @@ extension EmailValidator on String { String date = this.split("T")[0]; String time = this.split("T")[1]; var dates = date.split("-"); - return "${dates[2]} ${getMonth(int.parse(dates[1]))} ${dates[0]} ${DateFormat('hh:mm a').format(DateFormat('hh:mm:ss').parse(time))}"; + return "${dates[2]} ${getMonth(int.parse(dates[1]))} ${dates[0]} ${DateFormat('hh:mm a', "en_US").format(DateFormat('hh:mm:ss', "en_US").parse(time))}"; } String getMonth(int month) { diff --git a/lib/main.dart b/lib/main.dart index ae2d367..07590f3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,10 +9,10 @@ import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/generated/codegen_loader.g.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart'; +import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/eit_provider_model.dart'; -import 'package:mohem_flutter_app/provider/hmg_connection_provider.dart'; import 'package:mohem_flutter_app/theme/app_theme.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:month_year_picker/month_year_picker.dart'; @@ -28,7 +28,6 @@ Logger logger = Logger( // output: null, // U ); - class MyHttpOverrides extends HttpOverrides { @override HttpClient createHttpClient(SecurityContext? context) { @@ -71,9 +70,9 @@ Future main() async { ChangeNotifierProvider( create: (_) => MarathonProvider(), ), - ChangeNotifierProvider( - create: (_) => HmgConnectionProvider(), - ) + // ChangeNotifierProvider( + // create: (_) => ChatCallProvider(), + // ), ], child: const MyApp(), ), @@ -265,4 +264,3 @@ class MyApp extends StatelessWidget { // }); // } // } - diff --git a/lib/models/chat/call.dart b/lib/models/chat/call.dart index 7f8f6eb..ce58ae3 100644 --- a/lib/models/chat/call.dart +++ b/lib/models/chat/call.dart @@ -7,127 +7,191 @@ import 'dart:convert'; class CallDataModel { CallDataModel({ this.callerId, - this.callReceiverID, - this.notificationForeground, - this.message, + this.callerDetails, + this.receiverId, + this.receiverDetails, this.title, - this.type, - this.identity, - this.name, - this.isCall, - this.isWebrtc, - this.contant, - this.contantNo, - this.chatEventId, - this.fileTypeId, - this.currentUserId, - this.chatSource, - this.userChatHistoryLineRequestList, - this.server, + this.calltype, }); String? callerId; - String? callReceiverID; - String? notificationForeground; - String? message; - String? title; - String? type; - String? identity; - String? name; - String? isCall; - String? isWebrtc; - String? contant; - String? contantNo; - String? chatEventId; - dynamic? fileTypeId; - String? currentUserId; - String? chatSource; - List? userChatHistoryLineRequestList; - String? server; + CallerDetails? callerDetails; + String? receiverId; + ReceiverDetails? receiverDetails; + dynamic title; + String? calltype; factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str)); String toRawJson() => json.encode(toJson()); factory CallDataModel.fromJson(Map json) => CallDataModel( - callerId: json["callerID"] == null ? null : json["callerID"], - callReceiverID: json["callReceiverID"] == null ? null : json["callReceiverID"], - notificationForeground: json["notification_foreground"] == null ? null : json["notification_foreground"], - message: json["message"] == null ? null : json["message"], - title: json["title"] == null ? null : json["title"], - type: json["type"] == null ? null : json["type"], - identity: json["identity"] == null ? null : json["identity"], - name: json["name"] == null ? null : json["name"], - isCall: json["is_call"] == null ? null : json["is_call"], - isWebrtc: json["is_webrtc"] == null ? null : json["is_webrtc"], - contant: json["contant"] == null ? null : json["contant"], - contantNo: json["contantNo"] == null ? null : json["contantNo"], - chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], - fileTypeId: json["fileTypeId"], - currentUserId: json["currentUserId"] == null ? null : json["currentUserId"], - chatSource: json["chatSource"] == null ? null : json["chatSource"], - userChatHistoryLineRequestList: json["userChatHistoryLineRequestList"] == null - ? null - : List.from( - json["userChatHistoryLineRequestList"].map( - (x) => UserChatHistoryLineRequestList.fromJson(x), - ), - ), - server: json["server"] == null ? null : json["server"], - ); + callerId: json["callerID"], + callerDetails: json["callerDetails"] == null ? null : CallerDetails.fromJson(json["callerDetails"]), + receiverId: json["receiverID"], + receiverDetails: json["receiverDetails"] == null ? null : ReceiverDetails.fromJson(json["receiverDetails"]), + title: json["title"], + calltype: json["calltype"], + ); + + Map toJson() => { + "callerID": callerId, + "callerDetails": callerDetails?.toJson(), + "receiverID": receiverId, + "receiverDetails": receiverDetails?.toJson(), + "title": title, + "calltype": calltype, + }; +} + +class CallerDetails { + CallerDetails({ + this.response, + this.errorResponses, + }); + + Response? response; + dynamic errorResponses; + + factory CallerDetails.fromRawJson(String str) => CallerDetails.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory CallerDetails.fromJson(Map json) => CallerDetails( + response: json["response"] == null ? null : Response.fromJson(json["response"]), + errorResponses: json["errorResponses"], + ); Map toJson() => { - "callerID": callerId == null ? null : callerId, - "callReceiverID": callReceiverID == null ? null : callReceiverID, - "notification_foreground": notificationForeground == null ? null : notificationForeground, - "message": message == null ? null : message, - "title": title == null ? null : title, - "type": type == null ? null : type, - "identity": identity == null ? null : identity, - "name": name == null ? null : name, - "is_call": isCall == null ? null : isCall, - "is_webrtc": isWebrtc == null ? null : isWebrtc, - "contant": contant == null ? null : contant, - "contantNo": contantNo == null ? null : contantNo, - "chatEventId": chatEventId == null ? null : chatEventId, - "fileTypeId": fileTypeId, - "currentUserId": currentUserId == null ? null : currentUserId, - "chatSource": chatSource == null ? null : chatSource, - "userChatHistoryLineRequestList": userChatHistoryLineRequestList == null - ? null - : List.from( - userChatHistoryLineRequestList!.map( - (x) => x.toJson(), - ), - ), - "server": server == null ? null : server, - }; + "response": response?.toJson(), + "errorResponses": errorResponses, + }; } -class UserChatHistoryLineRequestList { - UserChatHistoryLineRequestList({ - this.isSeen, - this.isDelivered, - this.targetUserId, - this.targetUserStatus, +class Response { + Response({ + this.id, + this.userName, + this.email, + this.phone, + this.title, + this.token, + this.isDomainUser, + this.isActiveCode, + this.encryptedUserId, + this.encryptedUserName, }); - bool? isSeen; - bool? isDelivered; - int? targetUserId; - int? targetUserStatus; + int? id; + String? userName; + String? email; + dynamic phone; + String? title; + String? token; + bool? isDomainUser; + bool? isActiveCode; + String? encryptedUserId; + String? encryptedUserName; + + factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory Response.fromJson(Map json) => Response( + id: json["id"], + userName: json["userName"], + email: json["email"], + phone: json["phone"], + title: json["title"], + token: json["token"], + isDomainUser: json["isDomainUser"], + isActiveCode: json["isActiveCode"], + encryptedUserId: json["encryptedUserId"], + encryptedUserName: json["encryptedUserName"], + ); + + Map toJson() => { + "id": id, + "userName": userName, + "email": email, + "phone": phone, + "title": title, + "token": token, + "isDomainUser": isDomainUser, + "isActiveCode": isActiveCode, + "encryptedUserId": encryptedUserId, + "encryptedUserName": encryptedUserName, + }; +} + +class ReceiverDetails { + ReceiverDetails({ + 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.rKey, + this.totalCount, + }); + + int? id; + String? userName; + String? email; + dynamic phone; + dynamic title; + int? userStatus; + String? image; + int? unreadMessageCount; + dynamic userAction; + bool? isPin; + bool? isFav; + bool? isAdmin; + String? rKey; + int? totalCount; + + factory ReceiverDetails.fromRawJson(String str) => ReceiverDetails.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); - factory UserChatHistoryLineRequestList.fromJson(Map json) => UserChatHistoryLineRequestList( - isSeen: json["isSeen"] == null ? null : json["isSeen"], - isDelivered: json["isDelivered"] == null ? null : json["isDelivered"], - targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], - targetUserStatus: json["targetUserStatus"] == null ? null : json["targetUserStatus"], - ); + factory ReceiverDetails.fromJson(Map json) => ReceiverDetails( + id: json["id"], + userName: json["userName"], + email: json["email"], + phone: json["phone"], + title: json["title"], + userStatus: json["userStatus"], + image: json["image"], + unreadMessageCount: json["unreadMessageCount"], + userAction: json["userAction"], + isPin: json["isPin"], + isFav: json["isFav"], + isAdmin: json["isAdmin"], + rKey: json["rKey"], + totalCount: json["totalCount"], + ); Map toJson() => { - "isSeen": isSeen == null ? null : isSeen, - "isDelivered": isDelivered == null ? null : isDelivered, - "targetUserId": targetUserId == null ? null : targetUserId, - "targetUserStatus": targetUserStatus == null ? null : targetUserStatus, - }; + "id": id, + "userName": userName, + "email": email, + "phone": phone, + "title": title, + "userStatus": userStatus, + "image": image, + "unreadMessageCount": unreadMessageCount, + "userAction": userAction, + "isPin": isPin, + "isFav": isFav, + "isAdmin": isAdmin, + "rKey": rKey, + "totalCount": totalCount, + }; } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index ed00de4..a5677af 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -28,6 +28,7 @@ import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/models/get_notification_buttons_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_Item_history_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_notification_body_list_model.dart'; +import 'package:mohem_flutter_app/models/get_pr_information_list.dart'; import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_quotation_analysis_list_model.dart'; import 'package:mohem_flutter_app/models/get_schedule_shifts_details_list_model.dart'; @@ -226,6 +227,7 @@ class GenericResponseModel { List? getPoItemHistoryList; GetPoNotificationBodyList? getPoNotificationBodyList; GetPrNotificationBodyList? getPrNotificationBodyList; + GetPRInformationList? getPRInformationList; List? getQuotationAnalysisList; List? getRFCEmployeeListList; List? getRespondAttributeValueList; @@ -490,6 +492,7 @@ class GenericResponseModel { this.getPoItemHistoryList, this.getPoNotificationBodyList, this.getPrNotificationBodyList, + this.getPRInformationList, this.getQuotationAnalysisList, this.getRFCEmployeeListList, this.getRespondAttributeValueList, @@ -979,6 +982,7 @@ class GenericResponseModel { } getPoNotificationBodyList = json['GetPoNotificationBodyList'] != null ? GetPoNotificationBodyList.fromJson(json['GetPoNotificationBodyList']) : null; getPrNotificationBodyList = json['GetPrNotificationBodyList'] != null ? GetPrNotificationBodyList.fromJson(json['GetPrNotificationBodyList']) : null; + getPRInformationList = json['PR_Information_List'] != null ? GetPRInformationList.fromJson(json['PR_Information_List']) : null; if (json['GetQuotationAnalysisList'] != null) { getQuotationAnalysisList = []; json['GetQuotationAnalysisList'].forEach((v) { @@ -1602,6 +1606,10 @@ class GenericResponseModel { data['GetPrNotificationBodyList'] = this.getPrNotificationBodyList!.toJson(); } + if (this.getPRInformationList != null) { + data['PR_Information_List'] = this.getPRInformationList!.toJson(); + } + if (this.getQuotationAnalysisList != null) { data['GetQuotationAnalysisList'] = this.getQuotationAnalysisList!.map((v) => v.toJson()).toList(); } diff --git a/lib/models/get_po_notification_body_list_model.dart b/lib/models/get_po_notification_body_list_model.dart index d0473dc..a1a56e6 100644 --- a/lib/models/get_po_notification_body_list_model.dart +++ b/lib/models/get_po_notification_body_list_model.dart @@ -158,6 +158,7 @@ class POLines { String? nEEDBYDATE; int? nOOFROWS; int? pOHEADERID; + int? pOLINEID; String? pROMISEDDATE; String? pRNUM; num? qUANTITY; @@ -181,6 +182,7 @@ class POLines { this.nEEDBYDATE, this.nOOFROWS, this.pOHEADERID, + this.pOLINEID, this.pROMISEDDATE, this.pRNUM, this.qUANTITY, @@ -204,6 +206,7 @@ class POLines { nEEDBYDATE = json['NEED_BY_DATE']; nOOFROWS = json['NO_OF_ROWS']; pOHEADERID = json['PO_HEADER_ID']; + pOLINEID = json['PO_LINE_ID']; pROMISEDDATE = json['PROMISED_DATE']; pRNUM = json['PR_NUM']; qUANTITY = json['QUANTITY']; @@ -229,6 +232,7 @@ class POLines { data['NEED_BY_DATE'] = this.nEEDBYDATE; data['NO_OF_ROWS'] = this.nOOFROWS; data['PO_HEADER_ID'] = this.pOHEADERID; + data['PO_LINE_ID'] = this.pOLINEID; data['PROMISED_DATE'] = this.pROMISEDDATE; data['PR_NUM'] = this.pRNUM; data['QUANTITY'] = this.qUANTITY; diff --git a/lib/models/get_pr_information_list.dart b/lib/models/get_pr_information_list.dart new file mode 100644 index 0000000..33ede12 --- /dev/null +++ b/lib/models/get_pr_information_list.dart @@ -0,0 +1,125 @@ +class GetPRInformationList { + List? pRHeader; + List? pRLines; + String? pCURRENCYCODE; + + GetPRInformationList({this.pRHeader, this.pRLines, this.pCURRENCYCODE}); + + GetPRInformationList.fromJson(Map json) { + if (json['PRHeader'] != null) { + pRHeader = []; + json['PRHeader'].forEach((v) { + pRHeader!.add(new PRHeader.fromJson(v)); + }); + } + if (json['PRLines'] != null) { + pRLines = []; + json['PRLines'].forEach((v) { + pRLines!.add(new PRLines.fromJson(v)); + }); + } + pCURRENCYCODE = json['P_CURRENCY_CODE']; + } + + Map toJson() { + Map data = new Map(); + if (this.pRHeader != null) { + data['PRHeader'] = this.pRHeader!.map((v) => v.toJson()).toList(); + } + if (this.pRLines != null) { + data['PRLines'] = this.pRLines!.map((v) => v.toJson()).toList(); + } + data['P_CURRENCY_CODE'] = this.pCURRENCYCODE; + return data; + } +} + +class PRHeader { + String? dESCRIPTION; + String? pRNUMBER; + String? rEQUISITIONTOTAL; + String? tAXTOTAL; + + PRHeader({this.dESCRIPTION, this.pRNUMBER, this.rEQUISITIONTOTAL, this.tAXTOTAL}); + + PRHeader.fromJson(Map json) { + dESCRIPTION = json['DESCRIPTION']; + pRNUMBER = json['PR_NUMBER']; + rEQUISITIONTOTAL = json['REQUISITION_TOTAL']; + tAXTOTAL = json['TAX_TOTAL']; + } + + Map toJson() { + Map data = new Map(); + data['DESCRIPTION'] = this.dESCRIPTION; + data['PR_NUMBER'] = this.pRNUMBER; + data['REQUISITION_TOTAL'] = this.rEQUISITIONTOTAL; + data['TAX_TOTAL'] = this.tAXTOTAL; + return data; + } +} + +class PRLines { + String? cOSTCENTER; + String? dESCRIPTION; + int? fROMROWNUM; + int? iTEMAMU; + String? iTEMCODE; + int? lINEAMOUNT; + int? lINENUM; + int? nOOFROWS; + int? qUANTITY; + int? rOWNUM; + int? tOROWNUM; + int? uNITPRICE; + String? uOM; + + PRLines( + {this.cOSTCENTER, + this.dESCRIPTION, + this.fROMROWNUM, + this.iTEMAMU, + this.iTEMCODE, + this.lINEAMOUNT, + this.lINENUM, + this.nOOFROWS, + this.qUANTITY, + this.rOWNUM, + this.tOROWNUM, + this.uNITPRICE, + this.uOM}); + + PRLines.fromJson(Map json) { + cOSTCENTER = json['COST_CENTER']; + dESCRIPTION = json['DESCRIPTION']; + fROMROWNUM = json['FROM_ROW_NUM']; + iTEMAMU = json['ITEM_AMU']; + iTEMCODE = json['ITEM_CODE']; + lINEAMOUNT = json['LINE_AMOUNT']; + lINENUM = json['LINE_NUM']; + nOOFROWS = json['NO_OF_ROWS']; + qUANTITY = json['QUANTITY']; + rOWNUM = json['ROW_NUM']; + tOROWNUM = json['TO_ROW_NUM']; + uNITPRICE = json['UNIT_PRICE']; + uOM = json['UOM']; + } + + Map toJson() { + Map data = new Map(); + data['COST_CENTER'] = this.cOSTCENTER; + data['DESCRIPTION'] = this.dESCRIPTION; + data['FROM_ROW_NUM'] = this.fROMROWNUM; + data['ITEM_AMU'] = this.iTEMAMU; + data['ITEM_CODE'] = this.iTEMCODE; + data['LINE_AMOUNT'] = this.lINEAMOUNT; + data['LINE_NUM'] = this.lINENUM; + data['NO_OF_ROWS'] = this.nOOFROWS; + data['QUANTITY'] = this.qUANTITY; + data['ROW_NUM'] = this.rOWNUM; + data['TO_ROW_NUM'] = this.tOROWNUM; + data['UNIT_PRICE'] = this.uNITPRICE; + data['UOM'] = this.uOM; + return data; + } +} diff --git a/lib/provider/chat_call_provider.dart b/lib/provider/chat_call_provider.dart new file mode 100644 index 0000000..45205df --- /dev/null +++ b/lib/provider/chat_call_provider.dart @@ -0,0 +1,187 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; + +class ChatCallProvider with ChangeNotifier, DiagnosticableTreeMixin { + ///////////////////// Web RTC Video Calling ////////////////////// + // Video Call + late RTCPeerConnection _peerConnection; + RTCVideoRenderer _localVideoRenderer = RTCVideoRenderer(); + final RTCVideoRenderer _remoteRenderer = RTCVideoRenderer(); + + MediaStream? _localStream; + MediaStream? _remoteStream; + + void initCallListeners() { + chatHubConnection.on("OnCallAcceptedAsync", onCallAcceptedAsync); + chatHubConnection.on("OnIceCandidateAsync", onIceCandidateAsync); + chatHubConnection.on("OnOfferAsync", onOfferAsync); + chatHubConnection.on("OnAnswerOffer", onAnswerOffer); + chatHubConnection.on("OnHangUpAsync", onHangUpAsync); + chatHubConnection.on("OnCallDeclinedAsync", onCallDeclinedAsync); + } + + //Video Constraints + var videoConstraints = { + "video": { + "mandatory": { + "width": {"min": 320}, + "height": {"min": 180} + }, + "optional": [ + { + "width": {"max": 1280} + }, + {"frameRate": 25}, + {"facingMode": "user"} + ] + }, + "frameRate": 25, + "width": 420, //420,//640,//1280, + "height": 240 //240//480//720 + }; + + // Audio Constraints + var audioConstraints = { + "sampleRate": 8000, + "sampleSize": 16, + "channelCount": 2, + "echoCancellation": true, + "audio": true, + }; + + Future _createPeerConnection() async { + // {"url": "stun:stun.l.google.com:19302"}, + Map configuration = { + "iceServers": [ + {"urls": 'stun:15.185.116.59:3478'}, + {"urls": "turn:15.185.116.59:3479", "username": "admin", "credential": "admin"} + ] + }; + + Map offerSdpConstraints = { + "mandatory": { + "OfferToReceiveAudio": true, + "OfferToReceiveVideo": true, + }, + "optional": [], + }; + + RTCPeerConnection pc = await createPeerConnection(configuration, offerSdpConstraints); + // if (pc != null) print(pc); + //pc.addStream(widget.localStream); + + pc.onIceCandidate = (e) { + if (e.candidate != null) { + print(json.encode({ + 'candidate': e.candidate.toString(), + 'sdpMid': e.sdpMid.toString(), + 'sdpMlineIndex': e.sdpMLineIndex, + })); + } + }; + pc.onIceConnectionState = (e) { + print(e); + }; + pc.onAddStream = (stream) { + print('addStream: ' + stream.id); + _remoteRenderer.srcObject = stream; + }; + return pc; + } + + void init() { + initRenderers(); + _createPeerConnection().then((pc) { + _peerConnection = pc; + // _setRemoteDescription(widget.info); + }); + } + + void initRenderers() { + _localVideoRenderer.initialize(); + _remoteRenderer.initialize(); + initLocalCamera(); + } + + void initLocalCamera() async { + _localStream = await navigator.mediaDevices.getUserMedia({'video': true, 'audio': true}); + _localVideoRenderer.srcObject = _localStream; + // _localVideoRenderer.srcObject = await navigator.mediaDevices + // .getUserMedia({'video': true, 'audio': true}); + print('this source Object'); + print('this suarce ${_localVideoRenderer.srcObject != null}'); + notifyListeners(); + } + + void startCall({required String callType}) {} + + void endCall() {} + + void checkCall(Map message) { + switch (message["callStatus"]) { + case 'connected': + {} + break; + case 'offer': + {} + break; + case 'accept': + {} + break; + case 'candidate': + {} + break; + case 'bye': + {} + break; + case 'leave': + {} + break; + } + } + + //// Listeners Methods //// + + void onCallAcceptedAsync(List? params) {} + + void onIceCandidateAsync(List? params) {} + + void onOfferAsync(List? params) {} + + void onAnswerOffer(List? params) {} + + void onHangUpAsync(List? params) {} + + void onCallDeclinedAsync(List? params) {} + + //// Invoke Methods + + Future invoke({required String invokeMethod, required String currentUserID, required String targetUserID, bool isVideoCall = false, var data}) async { + List args = []; + if (invokeMethod == "answerCallAsync") { + args = [currentUserID, targetUserID]; + } else if (invokeMethod == "CallUserAsync") { + args = [currentUserID, targetUserID, isVideoCall]; + } else if (invokeMethod == "IceCandidateAsync") { + args = [targetUserID, data]; + } else if (invokeMethod == "OfferAsync") { + args = [targetUserID, data]; + } else if (invokeMethod == "AnswerOfferAsync") { + args = [targetUserID, data]; + //json In Data + } + await chatHubConnection.invoke(invokeMethod, args: args); + } + + void stopListeners() async { + chatHubConnection.off('OnCallDeclinedAsync'); + chatHubConnection.off('OnCallAcceptedAsync'); + chatHubConnection.off('OnIceCandidateAsync'); + chatHubConnection.off('OnAnswerOffer'); + } +} diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 08b148d..d374114 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -69,6 +69,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List teamMembersList = []; Material.TextDirection textDirection = Material.TextDirection.ltr; + bool isRTL = false; + String msgText = ""; //Chat Home Page Counter int chatUConvCounter = 0; @@ -77,6 +79,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? chatUsersList = []; int pageNo = 1; + bool disbaleChatForThisUser = false; + Future getUserAutoLoginToken() async { userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); if (userLoginResponse.response != null) { @@ -86,6 +90,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.showToast( userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", ); + disbaleChatForThisUser = true; + notifyListeners(); } } @@ -117,6 +123,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // chatHubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); chatHubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); + if (kDebugMode) { logger.i("All listeners registered"); } @@ -912,7 +919,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } String dateFormte(DateTime data) { - DateFormat f = DateFormat('hh:mm a dd MMM yyyy'); + DateFormat f = DateFormat('hh:mm a dd MMM yyyy', "en_US"); f.format(data); return f.format(data); } @@ -1007,23 +1014,25 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } void disposeData() { - search.clear(); - isChatScreenActive = false; - receiverID = 0; - paginationVal = 0; - message.text = ''; - isTextMsg = false; - isAttachmentMsg = false; - isVoiceMsg = false; - isReplyMsg = false; - repliedMsg = []; - sFileType = ""; - deleteData(); - favUsersList.clear(); - searchedChats?.clear(); - pChatHistory?.clear(); - chatHubConnection.stop(); - AppState().chatDetails = null; + if (!disbaleChatForThisUser) { + search.clear(); + isChatScreenActive = false; + receiverID = 0; + paginationVal = 0; + message.text = ''; + isTextMsg = false; + isAttachmentMsg = false; + isVoiceMsg = false; + isReplyMsg = false; + repliedMsg = []; + sFileType = ""; + deleteData(); + favUsersList.clear(); + searchedChats?.clear(); + pChatHistory?.clear(); + chatHubConnection.stop(); + AppState().chatDetails = null; + } } void deleteData() { @@ -1407,17 +1416,16 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void inputBoxDirection(String val) { if (val.isNotEmpty) { isTextMsg = true; - RegExp exp = RegExp("[a-zA-Z]"); - if (exp.hasMatch(val.substring(val.length - 1)) && val.substring(val.length - 1) != " ") { - textDirection = Material.TextDirection.ltr; - notifyListeners(); - } else if (val.substring(val.length - 1) != " " && !exp.hasMatch(val.substring(val.length - 1))) { - textDirection = Material.TextDirection.rtl; - notifyListeners(); - } } else { isTextMsg = false; } + msgText = val; + notifyListeners(); + } + + void onDirectionChange(bool val) { + isRTL = val; + notifyListeners(); } Material.TextDirection getTextDirection(String v) { @@ -1451,19 +1459,19 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } void openChatByNoti(BuildContext context) async { - SingleUserChatModel nUser = SingleUserChatModel.fromJson( - jsonDecode(await Utils.getStringFromPrefs("notificationData")), - ); + SingleUserChatModel nUser = SingleUserChatModel(); Utils.saveStringFromPrefs("isAppOpendByChat", "false"); - Utils.saveStringFromPrefs("notificationData", "null"); - Future.delayed(const Duration(seconds: 1)); - for (ChatUser user in searchedChats!) { - if (user.id == nUser.targetUserId) { - Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false)); - return; - } else { - openChatByNoti(context); + if (await Utils.getStringFromPrefs("notificationData") != "null") { + nUser = SingleUserChatModel.fromJson(jsonDecode(await Utils.getStringFromPrefs("notificationData"))); + Utils.saveStringFromPrefs("notificationData", "null"); + Future.delayed(const Duration(seconds: 2)); + for (ChatUser user in searchedChats!) { + if (user.id == nUser.targetUserId) { + Navigator.pushNamed(context, AppRoutes.chatDetailed, arguments: ChatDetailedScreenParams(user, false)); + return; + } } } + Utils.saveStringFromPrefs("notificationData", "null"); } } diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 5899699..d0f33df 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -66,7 +66,11 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { if (attendanceTracking?.pSwipeIn != null) { isTimeRemainingInSeconds = calculateSeconds(attendanceTracking!.pRemainingHours ?? "00:00:00"); int totalShiftTimeInSeconds = calculateSeconds(attendanceTracking!.pScheduledHours ?? "00:00:00"); - progress = (isTimeRemainingInSeconds / totalShiftTimeInSeconds); + if(isTimeRemainingInSeconds == 0 || totalShiftTimeInSeconds == 0) { + progress = 0; + } else { + progress = (isTimeRemainingInSeconds / totalShiftTimeInSeconds); + } endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; } notifyListeners(); @@ -187,7 +191,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { //Leave and Ticket Balance API's & Methods Future fetchLeaveTicketBalance(context, DateTime date) async { try { - accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(date)); + accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy", "en_US").format(date)); isLeaveTicketBalanceLoading = false; leaveBalanceAccrual = accrualList![0]; ticketBalance = (accrualList![1].accrualNetEntitlement ?? 0.0) + (accrualList![2].accrualNetEntitlement ?? 0.0) + (accrualList![3].accrualNetEntitlement ?? 0.0); @@ -241,7 +245,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } - void getCategoryOffersListAPI(BuildContext context) async { + void getCategoryOffersListAPI(BuildContext context) async { try { // Utils.showLoading(context); getOffersList = await OffersAndDiscountsApiClient().getOffersList(0, 10); diff --git a/lib/ui/attendance/monthly_attendance_screen.dart b/lib/ui/attendance/monthly_attendance_screen.dart index 46d4d40..1753c29 100644 --- a/lib/ui/attendance/monthly_attendance_screen.dart +++ b/lib/ui/attendance/monthly_attendance_screen.dart @@ -432,7 +432,7 @@ class _MonthlyAttendanceScreenState extends State { expand: false, builder: (_, controller) { dynamic dmyString = getScheduleShiftsDetailsList!.sCHEDULEDATE; - DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss").parse(dmyString); + DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss", "en_US").parse(dmyString); return Column( children: [ Container( diff --git a/lib/ui/attendance/vacation_rule_screen.dart b/lib/ui/attendance/vacation_rule_screen.dart index 5598e05..af5b4cf 100644 --- a/lib/ui/attendance/vacation_rule_screen.dart +++ b/lib/ui/attendance/vacation_rule_screen.dart @@ -140,7 +140,7 @@ class _VacationRuleScreenState extends State { } String getParsedTime(String time) { - DateTime date = DateFormat("MM/dd/yyyy").parse(time); - return DateFormat("d MMM yyyy").format(date); + DateTime date = DateFormat("MM/dd/yyyy", "en_US").parse(time); + return DateFormat("d MMM yyyy", "en_US").format(date); } } diff --git a/lib/ui/chat/call/chat_outgoing_call_screen.dart b/lib/ui/chat/call/chat_outgoing_call_screen.dart index 627c24a..2356e10 100644 --- a/lib/ui/chat/call/chat_outgoing_call_screen.dart +++ b/lib/ui/chat/call/chat_outgoing_call_screen.dart @@ -10,12 +10,14 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/call.dart'; +import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; +import 'package:provider/provider.dart'; class OutGoingCall extends StatefulWidget { - CallDataModel OutGoingCallData; - bool? isVideoCall; + CallDataModel outGoingCallData; + bool isVideoCall; - OutGoingCall({Key? key, required this.OutGoingCallData, this.isVideoCall}) : super(key: key); + OutGoingCall({Key? key, required this.outGoingCallData, required this.isVideoCall}) : super(key: key); @override _OutGoingCallState createState() => _OutGoingCallState(); @@ -23,23 +25,25 @@ class OutGoingCall extends StatefulWidget { class _OutGoingCallState extends State with SingleTickerProviderStateMixin { AnimationController? _animationController; - CameraController? _controller; + late CameraController controller; + late List _cameras; Future? _initializeControllerFuture; bool isCameraReady = false; bool isMicOff = false; bool isLoudSpeaker = false; bool isCamOff = false; + late ChatCallProvider callProviderd; @override void initState() { + callProviderd = Provider.of(context, listen: false); _animationController = AnimationController( vsync: this, duration: const Duration( milliseconds: 500, ), ); - logger.d(jsonEncode(widget.OutGoingCallData)); - //_runAnimation(); + // _runAnimation(); // connectSignaling(); WidgetsBinding.instance.addPostFrameCallback( (_) => _runAnimation(), @@ -58,13 +62,10 @@ class _OutGoingCallState extends State with SingleTickerProviderSt return Stack( alignment: FractionalOffset.center, children: [ - if (widget.isVideoCall!) + if (widget.isVideoCall) Positioned.fill( - child: AspectRatio( - aspectRatio: _controller!.value.aspectRatio, - child: CameraPreview( - _controller!, - ), + child: CameraPreview( + controller, ), ), Positioned.fill( @@ -74,7 +75,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt child: Container( decoration: BoxDecoration( color: MyColors.grey57Color.withOpacity( - 0.7, + 0.3, ), ), child: Column( @@ -105,9 +106,9 @@ class _OutGoingCallState extends State with SingleTickerProviderSt fit: BoxFit.cover, ), 10.height, - const Text( - "Aamir Saleem Ahmad", - style: TextStyle( + Text( + widget.outGoingCallData.title, + style: const TextStyle( fontSize: 21, fontWeight: FontWeight.bold, color: MyColors.white, @@ -179,7 +180,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (widget.isVideoCall!) + if (widget.isVideoCall) RawMaterialButton( onPressed: () { _camOff(); @@ -267,13 +268,10 @@ class _OutGoingCallState extends State with SingleTickerProviderSt } void _runAnimation() async { - List cameras = await availableCameras(); - CameraDescription firstCamera = cameras[1]; - _controller = CameraController( - firstCamera, - ResolutionPreset.medium, - ); - _initializeControllerFuture = _controller!.initialize(); + _cameras = await availableCameras(); + CameraDescription firstCamera = _cameras[1]; + controller = CameraController(firstCamera, ResolutionPreset.medium); + _initializeControllerFuture = controller.initialize(); setState(() {}); // setAudioFile(); for (int i = 0; i < 100; i++) { @@ -304,7 +302,7 @@ class _OutGoingCallState extends State with SingleTickerProviderSt try { // backToHome(); // final roomModel = RoomModel(name: widget.OutGoingCallData.name, token: widget.OutGoingCallData.sessionId, identity: widget.OutGoingCallData.identity); - await _controller?.dispose(); + await controller?.dispose(); // changeCallStatusAPI(4); diff --git a/lib/ui/chat/chat_bubble.dart b/lib/ui/chat/chat_bubble.dart index 1e91a79..d6483dc 100644 --- a/lib/ui/chat/chat_bubble.dart +++ b/lib/ui/chat/chat_bubble.dart @@ -8,6 +8,7 @@ import 'package:just_audio/just_audio.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/my_custom_stream.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; @@ -429,7 +430,7 @@ class ChatBubble extends StatelessWidget { Widget getPlayer({required AudioPlayer player, required SingleUserChatModel modelData}) { return StreamBuilder( - stream: player!.playerStateStream, + stream: player.playerStateStream, builder: (BuildContext context, AsyncSnapshot snapshot) { PlayerState? playerState = snapshot.data; ProcessingState? processingState = playerState?.processingState; @@ -442,7 +443,7 @@ class ChatBubble extends StatelessWidget { child: const CircularProgressIndicator(), ); } else if (playing != true) { - return Icon( + return const Icon( Icons.play_arrow, size: 30, color: MyColors.lightGreenColor, @@ -470,22 +471,3 @@ class ChatBubble extends StatelessWidget { ); } } - -class MyCustomStream extends StreamAudioSource { - final Uint8List bytes; - - MyCustomStream(this.bytes); - - @override - Future request([int? start, int? end]) async { - start ??= 0; - end ??= bytes.length; - return StreamAudioResponse( - sourceLength: bytes.length, - contentLength: end - start, - offset: start, - stream: Stream.value(bytes.sublist(start, end)), - contentType: 'audio/aac', - ); - } -} diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index a72e12b..421eb91 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:audio_waveforms/audio_waveforms.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -13,7 +14,10 @@ import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/call.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/get_user_login_token_model.dart'; +import 'package:mohem_flutter_app/provider/chat_call_provider.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; +import 'package:mohem_flutter_app/ui/chat/custom_auto_direction.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/common.dart'; @@ -41,8 +45,10 @@ class ChatDetailScreen extends StatefulWidget { class _ChatDetailScreenState extends State { final RefreshController _rc = RefreshController(initialRefresh: false); late ChatProviderModel data; + late ChatCallProvider callPro; ChatDetailedScreenParams? params; - var textDirection = TextDirection.RTL; + + // var textDirection = TextDirection.RTL; void getMoreChat() async { if (params != null) { @@ -72,6 +78,7 @@ class _ChatDetailScreenState extends State { Widget build(BuildContext context) { params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams; data = Provider.of(context, listen: false); + // callPro = Provider.of(context, listen: false); if (params != null) { data.getSingleUserChatHistory( senderUID: AppState().chatDetails!.response!.id!.toInt(), @@ -92,11 +99,11 @@ class _ChatDetailScreenState extends State { chatUser: params!.chatUser, actions: [ // SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() { - // // makeCall(callType: "AUDIO", con: hubConnection); + // makeCall(callType: "AUDIO"); // }), // 24.width, // SvgPicture.asset("assets/icons/chat/video_call.svg", width: 21, height: 18).onPress(() { - // // makeCall(callType: "VIDEO", con: hubConnection); + // makeCall(callType: "VIDEO"); // }), // 21.width, ], @@ -252,37 +259,39 @@ class _ChatDetailScreenState extends State { if (!m.isRecoding) Row( children: [ - TextField( - textDirection: m.textDirection, - controller: m.message, - decoration: InputDecoration( - hintTextDirection: m.textDirection, - hintText: m.isAttachmentMsg - ? m.selectedFile.path.split("/").last - : m.textDirection.name == "rtl" ? "اكتب هنا للرد" :LocaleKeys.typeheretoreply.tr(), - hintStyle: TextStyle(color: m.isAttachmentMsg ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - filled: true, - fillColor: MyColors.white, - contentPadding: const EdgeInsets.only( - left: 21, - top: 20, - bottom: 20, + CustomAutoDirection( + onDirectionChange: (bool isRTL) => m.onDirectionChange(isRTL), + text: m.msgText, + child: TextField( + // textDirection: m.textDirection, + controller: m.message, + decoration: InputDecoration( + hintTextDirection: m.textDirection, + hintText: m.isAttachmentMsg ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), + hintStyle: TextStyle(color: m.isAttachmentMsg ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + filled: true, + fillColor: MyColors.white, + contentPadding: const EdgeInsets.only( + left: 21, + top: 20, + bottom: 20, + ), + prefixIconConstraints: const BoxConstraints(), + prefixIcon: m.sFileType.isNotEmpty + ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) + : null, ), - prefixIconConstraints: const BoxConstraints(), - prefixIcon: m.sFileType.isNotEmpty - ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) - : null, - ), - onChanged: (String val) { - m.inputBoxDirection(val); - m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!); - }, - ).expanded, + onChanged: (String val) { + m.inputBoxDirection(val); + m.userTypingInvoke(currentUser: AppState().chatDetails!.response!.id!, reciptUser: params!.chatUser!.id!); + }, + ).expanded, + ), if (m.sFileType.isNotEmpty) Row( children: [ @@ -342,45 +351,30 @@ class _ChatDetailScreenState extends State { } } - void makeCall({required String callType, required HubConnection con}) async { + void makeCall({required String callType}) async { + callPro.initCallListeners(); print("================== Make call Triggered ============================"); Map json = { "callerID": AppState().chatDetails!.response!.id!.toString(), - "callReceiverID": params!.chatUser!.id.toString(), - "notification_foreground": "true", - "message": "Aamir is calling", - "title": "Video Call", - "type": callType == "VIDEO" ? "Video" : "Audio", - "identity": AppState().chatDetails!.response!.userName, - "name": AppState().chatDetails!.response!.title, - "is_call": "true", - "is_webrtc": "true", - "contant": "Start video Call ${AppState().chatDetails!.response!.userName}", - "contantNo": "775d1f11-62d9-6fcc-91f6-21f8c14559fb", - "chatEventId": "3", - "fileTypeId": null, - "currentUserId": AppState().chatDetails!.response!.id!.toString(), - "chatSource": "1", - "userChatHistoryLineRequestList": [ - { - "isSeen": false, - "isDelivered": false, - "targetUserId": params!.chatUser!.id!, - "targetUserStatus": 4, - } - ], - // "server": "https://192.168.8.163:8086", - "server": "https://livecareturn.hmg.com:8086", + "callerDetails": AppState().chatDetails!.toJson(), + "receiverID": params!.chatUser!.id.toString(), + "receiverDetails": params!.chatUser!.toJson(), + "title": params!.chatUser!.userName!.replaceAll(".", " "), + "calltype": callType == "VIDEO" ? "Video" : "Audio", }; + logger.w(json); CallDataModel callData = CallDataModel.fromJson(json); await Navigator.push( context, MaterialPageRoute( builder: (BuildContext context) => OutGoingCall( isVideoCall: callType == "VIDEO" ? true : false, - OutGoingCallData: callData, + outGoingCallData: callData, ), ), - ); + ).then((value) { + print("then"); + callPro.stopListeners(); + }); } } diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index 2e23254..9c5f216 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -52,11 +52,11 @@ class _ChatHomeState extends State { if (data.searchedChats == null || data.searchedChats!.isEmpty) { data.isLoading = true; data.getUserRecentChats().whenComplete(() async { - String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); - String notificationData = await Utils.getStringFromPrefs("notificationData"); - if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") { - data.openChatByNoti(context); - } + // String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); + // String notificationData = await Utils.getStringFromPrefs("notificationData"); + // if (isAppOpendByChat != "null" || isAppOpendByChat == "true" && notificationData != "null") { + // data.openChatByNoti(context); + // } }); } } diff --git a/lib/ui/chat/custom_auto_direction.dart b/lib/ui/chat/custom_auto_direction.dart new file mode 100644 index 0000000..7e40644 --- /dev/null +++ b/lib/ui/chat/custom_auto_direction.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart' as intl; + +class CustomAutoDirection extends StatefulWidget { + final String text; + final Widget child; + final void Function(bool isRTL)? onDirectionChange; + + const CustomAutoDirection({Key? key, required this.text, required this.child, this.onDirectionChange}) : super(key: key); + + @override + _CustomAutoDirectionState createState() => _CustomAutoDirectionState(); +} + +class _CustomAutoDirectionState extends State { + late String text; + late Widget childWidget; + + @override + Widget build(BuildContext context) { + text = widget.text; + childWidget = widget.child; + return Directionality(textDirection: isRTL(text) ? TextDirection.rtl : TextDirection.ltr, child: childWidget); + } + + @override + void didUpdateWidget(CustomAutoDirection oldWidget) { + if (isRTL(oldWidget.text) != isRTL(widget.text)) { + WidgetsBinding.instance.addPostFrameCallback((_) => widget.onDirectionChange?.call(isRTL(widget.text))); + } + super.didUpdateWidget(oldWidget); + } + + bool isRTL(String text) { + if (text.isEmpty) return Directionality.of(context) == TextDirection.rtl; + return intl.Bidi.detectRtlDirectionality(text); + } +} diff --git a/lib/ui/dialogs/id/employee_digital_id_dialog.dart b/lib/ui/dialogs/id/employee_digital_id_dialog.dart index 0e672f8..63a5e29 100644 --- a/lib/ui/dialogs/id/employee_digital_id_dialog.dart +++ b/lib/ui/dialogs/id/employee_digital_id_dialog.dart @@ -6,10 +6,8 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; -import 'package:qr_flutter/qr_flutter.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; -import 'package:mohem_flutter_app/main.dart'; class EmployeeDigitialIdDialog extends StatelessWidget { @override diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index db1f1fc..8b78974 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -16,8 +16,8 @@ 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/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; +import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart'; @@ -26,6 +26,7 @@ import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_banner.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/dialogs.dart'; import 'package:mohem_flutter_app/widgets/mark_attendance_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart'; @@ -62,7 +63,9 @@ class _DashboardScreenState extends State with WidgetsBindingOb data = Provider.of(context, listen: false); marathonProvider = Provider.of(context, listen: false); cProvider = Provider.of(context, listen: false); - _bHubCon(); + if (checkIfPrivilegedForChat()) { + _bHubCon(); + } _onRefresh(true); }); } @@ -86,24 +89,28 @@ class _DashboardScreenState extends State with WidgetsBindingOb void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); - chatHubConnection.stop(); + if (!cProvider.disbaleChatForThisUser) { + chatHubConnection.stop(); + } } void _bHubCon() { cProvider.getUserAutoLoginToken().whenComplete(() async { - String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); - if (isAppOpendByChat != null && isAppOpendByChat == "true") { - Utils.showLoading(context); - cProvider.buildHubConnection(); - Future.delayed(const Duration(seconds: 2), () async { - cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); - gotoChat(context); - }); - } else { - cProvider.buildHubConnection(); - Future.delayed(const Duration(seconds: 2), () { - cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); - }); + if (!cProvider.disbaleChatForThisUser) { + String isAppOpendByChat = await Utils.getStringFromPrefs("isAppOpendByChat"); + if (isAppOpendByChat != null && isAppOpendByChat == "true") { + Utils.showLoading(context); + cProvider.buildHubConnection(); + Future.delayed(const Duration(seconds: 2), () async { + cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); + gotoChat(context); + }); + } else { + cProvider.buildHubConnection(); + Future.delayed(const Duration(seconds: 2), () { + cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); + }); + } } }); } @@ -139,54 +146,42 @@ class _DashboardScreenState extends State with WidgetsBindingOb data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); - if (!isFromInit) checkHubCon(); + if (isFromInit) { + checkERMChannel(); + } + if (!cProvider.disbaleChatForThisUser && !isFromInit) checkHubCon(); _refreshController.refreshCompleted(); } + void checkERMChannel() { + data.getITGNotification().then((val) { + if (val!.result!.data != null) { + print("-------------------- Survey ----------------------------"); + if (val.result!.data!.notificationType == "Survey") { + Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data); + } else { + print("------------------------------------------- Ads --------------------"); + DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then( + (value) { + if (value!.mohemmItgResponseItem!.statusCode == 200) { + if (value.mohemmItgResponseItem!.result!.data != null) { + Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { + "masterId": val.result!.data!.notificationMasterId, + "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement, + }); + } + } + }, + ); + } + } + }); + } + @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldState, - // appBar: AppBar( - // actions: [ - // IconButton( - // onPressed: () { - // data.getITGNotification().then((val) { - // if (val!.result!.data != null) { - // print("-------------------- Survey ----------------------------"); - // if (val.result!.data!.notificationType == "Survey") { - // Navigator.pushNamed(context, AppRoutes.survey, arguments: val.result!.data); - // } else { - // print("------------------------------------------- Ads --------------------"); - // DashboardApiClient().getAdvertisementDetail(val.result!.data!.notificationMasterId ?? "").then( - // (value) { - // if (value!.mohemmItgResponseItem!.statusCode == 200) { - // if (value.mohemmItgResponseItem!.result!.data != null) { - // Navigator.pushNamed(context, AppRoutes.advertisement, arguments: { - // "masterId": val.result!.data!.notificationMasterId, - // "advertisement": value.mohemmItgResponseItem!.result!.data!.advertisement, - // }); - // - // // Navigator.push( - // // context, - // // MaterialPageRoute( - // // builder: (BuildContext context) => ITGAdsScreen( - // // addMasterId: val.result!.data!.notificationMasterId!, - // // advertisement: value.mohemmItgResponseItem!.result!.data!.advertisement!, - // // ), - // // ), - // // ); - // } - // } - // }, - // ); - // } - // } - // }); - // }, - // icon: Icon(Icons.add)) - // ], - // ), body: Column( children: [ Row( @@ -247,7 +242,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ LocaleKeys.welcomeBack.tr().toText14(color: MyColors.grey77Color), (AppState().memberInformationList!.eMPLOYEENAME ?? "").toText24(isBold: true), 16.height, @@ -340,7 +335,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb bottomLeft: AppState().isArabic(context) ? Radius.circular(15) : Radius.circular(0), ), ), - child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/attendance.svg" : "assets/images/attendance.svg"), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"), ).onPress(() { showMyBottomSheet( context, @@ -445,7 +440,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, transitionOnUserGestures: true, child: Image.network( - data.getOffersList[index].bannerImage!, + data.getOffersList[index].logo!, fit: BoxFit.contain, ), ), @@ -555,20 +550,28 @@ class _DashboardScreenState extends State with WidgetsBindingOb children: [ SvgPicture.asset( "assets/icons/chat/chat.svg", - color: currentIndex == 4 ? MyColors.grey3AColor : MyColors.grey98Color, + color: !checkIfPrivilegedForChat() + ? MyColors.lightGreyE3Color + : currentIndex == 4 + ? MyColors.grey3AColor + : cProvider.disbaleChatForThisUser + ? MyColors.lightGreyE3Color + : MyColors.grey98Color, ).paddingAll(4), Consumer( builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { - return Positioned( - right: 0, - top: 0, - child: Container( - padding: const EdgeInsets.only(left: 4, right: 4), - alignment: Alignment.center, - decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), - child: data.chatUConvCounter.toString().toText10(color: Colors.white), - ), - ); + return !checkIfPrivilegedForChat() + ? const SizedBox() + : Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.chatUConvCounter.toString().toText10(color: Colors.white), + ), + ); }, ), ], @@ -592,7 +595,9 @@ class _DashboardScreenState extends State with WidgetsBindingOb } else if (index == 3) { Navigator.pushNamed(context, AppRoutes.itemsForSale); } else if (index == 4) { - Navigator.pushNamed(context, AppRoutes.chat); + if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) { + Navigator.pushNamed(context, AppRoutes.chat); + } } }, ), @@ -615,7 +620,17 @@ class _DashboardScreenState extends State with WidgetsBindingOb } } }); - Navigator.pushNamed(context, AppRoutes.offersAndDiscountsDetails, arguments: getOffersDetailList); } + + bool checkIfPrivilegedForChat() { + for (PrivilegeListModel element in AppState().privilegeListModel!) { + if (element.serviceName?.toLowerCase() == "chat") { + if (element.previlege != null) { + return element.previlege!; + } + } + } + return false; + } } diff --git a/lib/ui/landing/itg/its_add_screen_video_image.dart b/lib/ui/landing/itg/its_add_screen_video_image.dart index bcb9ed4..be16a89 100644 --- a/lib/ui/landing/itg/its_add_screen_video_image.dart +++ b/lib/ui/landing/itg/its_add_screen_video_image.dart @@ -2,10 +2,17 @@ import 'dart:convert'; import 'dart:io' as Io; import 'dart:io'; import 'dart:typed_data'; + import 'package:flutter/material.dart'; -import 'package:just_audio/just_audio.dart'; +import 'package:flutter_countdown_timer/index.dart'; +import 'package:lottie/lottie.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/lottie_consts.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads; import 'package:path_provider/path_provider.dart'; @@ -24,11 +31,13 @@ class _ITGAdsScreenState extends State { bool skip = false; bool isVideo = false; bool isImage = false; + bool isAudio = false; String ext = ''; late File imageFile; ads.Advertisement? advertisementData; dynamic data; String? masterID; + int videoDuration = 0; void checkFileType() async { String? rFile = advertisementData!.viewAttachFileColl!.first.base64String; @@ -38,11 +47,13 @@ class _ITGAdsScreenState extends State { await processImage(rFile!); isImage = true; } else { + if (ext == ".aac") { + isAudio = true; + } isVideo = true; _futureController = createVideoPlayer(rFile!); } setState(() {}); - initTimer(); } Future processImage(String encodedBytes) async { @@ -76,26 +87,29 @@ class _ITGAdsScreenState extends State { void initTimer() { Future.delayed(const Duration(seconds: 5), () { skip = true; - setState(() {}); + // setState(() {}); }); } @override void dispose() { _controller.dispose(); + // player.stop(); + // player.dispose(); super.dispose(); } @override Widget build(BuildContext context) { data = ModalRoute.of(context)!.settings.arguments; - if (advertisementData == null) advertisementData = data["advertisement"] as ads.Advertisement; - if (masterID == null) masterID = data["masterId"]; + advertisementData ??= data["advertisement"] as ads.Advertisement; + masterID ??= data["masterId"]; if (advertisementData != null) { checkFileType(); + videoDuration = advertisementData?.durationInSeconds ?? 0; } - // double height = MediaQuery.of(context).size.height * .25; return Scaffold( + backgroundColor: Colors.black, body: Stack( children: [ if (isVideo) @@ -104,11 +118,52 @@ class _ITGAdsScreenState extends State { builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) { _controller = snapshot.data as VideoPlayerController; - return Positioned.fill( - child: AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), - ), + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Center( + child: isAudio + ? Lottie.asset(MyLottieConsts.audioPlaybackLottie) + : AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ), + ), + 30.height, + CountdownTimer( + endTime: DateTime.now().millisecondsSinceEpoch + 1000 * videoDuration, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: const TextStyle(color: Colors.white, fontSize: 16, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + 50.height, + Container(padding: const EdgeInsets.all(16), decoration: Utils.containerRadius(MyColors.white, 10), child: const Icon(Icons.thumb_up, color: MyColors.gradiantEndColor)) + .onPress(() { + try { + DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!).then((value) { + logger.d(value); + Navigator.pop(context); + }); + } catch (ex) { + logger.wtf(ex); + Utils.handleException(ex, context, null); + } + }), + // DefaultButton(LocaleKeys.home.tr(), () async { + // DashboardApiClient().setAdvertisementViewed(masterID!, advertisementData!.advertisementId!).then((value) { + // logger.d(value); + // }); + // }).paddingOnly(left: 50, right: 50) + + // ElevatedButton( + // onPressed: () async { + // // DashboardApiClient().setAdvertisementViewed(widget.addMasterId, widget.advertisement!.advertisementId!).then((value) { + // // logger.d(value); + // // }); + // }, + // child: const Text("Go To Dashboard"), + // ) + ], ); } else { return const Center( @@ -118,15 +173,6 @@ class _ITGAdsScreenState extends State { }, ), if (isImage) Image.file(imageFile), - if (skip) - ElevatedButton( - onPressed: () async { - // DashboardApiClient().setAdvertisementViewed(widget.addMasterId, widget.advertisement!.advertisementId!).then((value) { - // logger.d(value); - // }); - }, - child: const Text("Go To Dashboard"), - ) ], ), ); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 784b337..219d235 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -3,11 +3,12 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/src/public_ext.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +// import 'package:huawei_hmsavailability/huawei_hmsavailability.dart'; import 'package:mohem_flutter_app/api/login_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -19,7 +20,6 @@ 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/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; @@ -28,8 +28,8 @@ import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/button/hmg_connectivity_button.dart'; import 'package:mohem_flutter_app/widgets/input_widget.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:safe_device/safe_device.dart'; + +// import 'package:safe_device/safe_device.dart'; import 'package:wifi_iot/wifi_iot.dart'; class LoginScreen extends StatefulWidget { @@ -59,67 +59,92 @@ class _LoginScreenState extends State { bool isOnExternalStorage = false; bool isDevelopmentModeEnable = false; + // late HmsApiAvailability hmsApiAvailability; + @override void initState() { super.initState(); + // hmsApiAvailability = HmsApiAvailability(); // checkFirebaseToken(); // if (kReleaseMode) { // checkDeviceSafety(); // } } - void checkDeviceSafety() async { - try { - isJailBroken = await SafeDevice.isJailBroken; - isRealDevice = await SafeDevice.isRealDevice; - if (Platform.isAndroid) { - isOnExternalStorage = await SafeDevice.isOnExternalStorage; - isDevelopmentModeEnable = await SafeDevice.isDevelopmentModeEnable; - } - if (isJailBroken || !isRealDevice || isOnExternalStorage || isDevelopmentModeEnable) { - Navigator.pushNamedAndRemoveUntil(context, AppRoutes.unsafeDeviceScreen, (_) => false); - } - } catch (error) { - print(error); - } - } + // void checkDeviceSafety() async { + // try { + // isJailBroken = await SafeDevice.isJailBroken; + // isRealDevice = await SafeDevice.isRealDevice; + // if (Platform.isAndroid) { + // isOnExternalStorage = await SafeDevice.isOnExternalStorage; + // isDevelopmentModeEnable = await SafeDevice.isDevelopmentModeEnable; + // } + // if (isJailBroken || !isRealDevice || isOnExternalStorage || isDevelopmentModeEnable) { + // Navigator.pushNamedAndRemoveUntil(context, AppRoutes.unsafeDeviceScreen, (_) => false); + // } + // } catch (error) { + // print(error); + // } + // } @override void dispose() { super.dispose(); } + + + String? firebaseToken; GetMobileLoginInfoListModel? loginInfo; Future checkFirebaseToken() async { try { Utils.showLoading(context); - await Firebase.initializeApp(); - // await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( - // alert: true, - // badge: true, - // sound: true, - // ); - // await FirebaseMessaging.instance.requestPermission(); - _firebaseMessaging = FirebaseMessaging.instance; - firebaseToken = await _firebaseMessaging.getToken(); - AppNotifications().init(firebaseToken); - loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); - if (loginInfo == null) { - await checkPrefs(); - _autoLogin = false; - Utils.hideLoading(context); - return; + if (Platform.isAndroid) { + try { + if (!(await Utils.isGoogleServicesAvailable())) { + print("HUAWEI APPPP GALLERYYYY!!!!"); + AppState().setIsHuawei = true; + AppNotifications().initHuaweiPush(checkLoginInfo); + } else { + print("GOOGLE PLAY STOREEEE!!!!"); + await Firebase.initializeApp(); + _firebaseMessaging = FirebaseMessaging.instance; + firebaseToken = await _firebaseMessaging.getToken(); + AppNotifications().init(firebaseToken); + checkLoginInfo(); + await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); + } + // }); + } catch (ex) {} } else { - loginInfo!.deviceToken = firebaseToken; - await checkPrefs(); - Utils.hideLoading(context); - performLogin(); + await Firebase.initializeApp(); + _firebaseMessaging = FirebaseMessaging.instance; + firebaseToken = await _firebaseMessaging.getToken(); + AppNotifications().init(firebaseToken); + checkLoginInfo(); + await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); } } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); + await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); + } + } + + void checkLoginInfo() async { + loginInfo = await LoginApiClient().getMobileLoginInfoNEW(AppState().getIsHuawei ? AppState().getHuaweiPushToken : firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); + if (loginInfo == null) { + await checkPrefs(); + _autoLogin = false; + Utils.hideLoading(context); + return; + } else { + loginInfo!.deviceToken = firebaseToken; + await checkPrefs(); + Utils.hideLoading(context); + performLogin(); } } diff --git a/lib/ui/login/verify_last_login_screen.dart b/lib/ui/login/verify_last_login_screen.dart index 30feb71..c48d1f3 100644 --- a/lib/ui/login/verify_last_login_screen.dart +++ b/lib/ui/login/verify_last_login_screen.dart @@ -364,7 +364,7 @@ class _VerifyLastLoginScreenState extends State { Future performDirectApiCall(String _title, String _icon, int _flag, String value, TextEditingController? _pinPutController, {bool isDirectLogin = false}) async { try { - GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); + GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(true, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); GenericResponseModel? genericResponseModel1 = await LoginApiClient().insertMobileLoginInfoNEW( AppState().memberLoginList?.pEMAILADDRESS ?? "", genericResponseModel?.pSESSIONID ?? 0, @@ -372,7 +372,7 @@ class _VerifyLastLoginScreenState extends State { _flag, AppState().memberLoginList?.pMOBILENUMBER ?? "", AppState().getUserName!, - mobileLoginInfoListModel!.deviceToken!, + AppState().getIsHuawei ? AppState().getHuaweiPushToken : mobileLoginInfoListModel!.deviceToken!, Platform.isAndroid ? "android" : "ios"); AppState().setMemberInformationListModel = genericResponseModel!.memberInformationList?.first; AppState().setPrivilegeListModel = genericResponseModel!.privilegeList ?? []; diff --git a/lib/ui/login/verify_login_screen.dart b/lib/ui/login/verify_login_screen.dart index 0ba486b..0147472 100644 --- a/lib/ui/login/verify_login_screen.dart +++ b/lib/ui/login/verify_login_screen.dart @@ -628,7 +628,7 @@ class _VerifyLoginScreenState extends State { _flag, AppState().memberLoginList?.pMOBILENUMBER ?? "", AppState().getUserName!, - firebaseToken!, + AppState().getIsHuawei ? AppState().getHuaweiPushToken : firebaseToken!, Platform.isAndroid ? "android" : "ios"); if (genericResponseModel?.errorMessage != null) { Utils.showToast(genericResponseModel?.errorMessage ?? ""); diff --git a/lib/ui/marathon/marathon_intro_screen.dart b/lib/ui/marathon/marathon_intro_screen.dart index 9d4f81d..de1c356 100644 --- a/lib/ui/marathon/marathon_intro_screen.dart +++ b/lib/ui/marathon/marathon_intro_screen.dart @@ -17,7 +17,18 @@ class MarathonIntroScreen extends StatelessWidget { Widget build(BuildContext context) { MarathonProvider provider = context.watch(); return Scaffold( - appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()), + appBar: AppBarWidget( + context, + title: LocaleKeys.brainMarathon.tr(), + onHomeTapped: () { + Navigator.pop(context); + context.setLocale(provider.savedLocale); + }, + onBackTapped: () { + Navigator.pop(context); + context.setLocale(provider.savedLocale); + }, + ), body: Column( children: [ ListView( diff --git a/lib/ui/marathon/marathon_provider.dart b/lib/ui/marathon/marathon_provider.dart index 9185a19..b88f54b 100644 --- a/lib/ui/marathon/marathon_provider.dart +++ b/lib/ui/marathon/marathon_provider.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:appinio_swiper/appinio_swiper.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/marathon/demo_marathon_repo.dart'; @@ -31,7 +32,7 @@ class MarathonProvider extends ChangeNotifier { int? selectedOptionIndex; String? selectedOptionId; int? totalQualifiers; - + Locale savedLocale = const Locale("en", "US"); String? gapTimeImage; String? gapTimeText; int? gapTimeType; @@ -190,6 +191,7 @@ class MarathonProvider extends ChangeNotifier { oneSec, (Timer timer) async { if (totalSecondsToWaitForMarathon == 0) { + timer.cancel(); if (isUserWaiting) { MarathonApiClient().joinMarathonAsParticipant().whenComplete(() async { await callNextQuestionApi(); @@ -197,7 +199,6 @@ class MarathonProvider extends ChangeNotifier { } else { isButtonEnabled = false; } - timer.cancel(); return; } else { totalSecondsToWaitForMarathon--; @@ -210,20 +211,24 @@ class MarathonProvider extends ChangeNotifier { int totalCurrentQuestionTime = 0; int currentGapTime = 0; Timer timerForQuestion = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); + int callCountThreshold = 0; void startTimerForQuestion() { const Duration oneSec = Duration(seconds: 1); timerForQuestion = Timer.periodic( oneSec, (Timer timer) async { - // This 2 is just to show the color of answer tile for 1 and then update card status - if (totalCurrentQuestionTime - currentGapTime == 1) { - getCorrectAnswerAndUpdateAnswerColor(); + // This 1 is just to show the color of answer tile for 1 and then update card status + if (totalCurrentQuestionTime - currentGapTime == 0) { + if (callCountThreshold == 0) { + getCorrectAnswerAndUpdateAnswerColor(); + } } if (totalCurrentQuestionTime - currentGapTime == -2) { - updateCardStatusToAnswer(); - + if (callCountThreshold == 0) { + updateCardStatusToAnswer(); + } // scheduleMicrotask(() async { // if (AppState().getIsDemoMarathon || isUserOutOfGame) { // await callNextQuestionApi(); @@ -243,6 +248,7 @@ class MarathonProvider extends ChangeNotifier { notifyListeners(); } totalCurrentQuestionTime--; + callCountThreshold = 0; } if (totalCurrentQuestionTime == 0) { @@ -255,6 +261,7 @@ class MarathonProvider extends ChangeNotifier { } else { if (totalCurrentQuestionTime - currentGapTime != -2) { totalCurrentQuestionTime--; + callCountThreshold = 0; } } @@ -272,8 +279,8 @@ class MarathonProvider extends ChangeNotifier { oneSec, (Timer timer) async { if (totalSecondsToWaitForWinner == 1) { - await callGetSelectedWinnersApi().whenComplete(() => updateQuestionCardStatus(QuestionCardStatus.winnerFound)); timer.cancel(); + await callGetSelectedWinnersApi().whenComplete(() => updateQuestionCardStatus(QuestionCardStatus.winnerFound)); return; } else if (totalSecondsToWaitForWinner == 15) { totalSecondsToWaitForWinner--; @@ -289,6 +296,18 @@ class MarathonProvider extends ChangeNotifier { //************************************************ FUNCTIONS ********************************************************** + void updateLanguageAsPerMarathon(BuildContext context, MarathonDetailModel detailModel) { + savedLocale = context.locale; + if (detailModel.selectedLanguage == 1) { + context.setLocale(const Locale("en", "US")); + } else if (detailModel.selectedLanguage == 2) { + context.setLocale(const Locale("ar", "SA")); + } else if (detailModel.selectedLanguage == 3) { + } else { + context.setLocale(const Locale("en", "US")); + } + } + Future callSubmitOptionApi() async { return await MarathonApiClient().submitSelectedOption(marathonId: marathonDetailModel.id!, questionId: currentQuestion.id, selectedAnswerId: selectedOptionId); } @@ -334,11 +353,15 @@ class MarathonProvider extends ChangeNotifier { gapTimeImage = currentQuestion.gapImage; gapTimeText = currentQuestion.gapText; gapTimeType = currentQuestion.gapType; + + startTimerForQuestion(); + updateCardData(); if (Utils.isLoading) { Utils.hideLoading(AppRoutes.navigatorKey.currentContext!); } - startTimerForQuestion(); - updateCardData(); + if (!AppState().getIsDemoMarathon) { + totalMarathoners = await MarathonApiClient().getMarathonersCount(marathonId: marathonDetailModel.id!); + } Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen); } else { currentQuestion = AppState().getIsDemoMarathon @@ -413,6 +436,7 @@ class MarathonProvider extends ChangeNotifier { } void getCorrectAnswerAndUpdateAnswerColor() { + callCountThreshold = 1; if (selectedOptionIndex != null) { scheduleMicrotask(() async { if (AppState().getIsDemoMarathon) { @@ -447,7 +471,7 @@ class MarathonProvider extends ChangeNotifier { if (currentQuestionNumber == 0) { return; } - + callCountThreshold = 1; scheduleMicrotask(() async { await callNextQuestionApi(); }); @@ -478,6 +502,7 @@ class MarathonProvider extends ChangeNotifier { void resetValues() async { _currentQuestionNumber = 0; + iAmWinner = false; cardContentList.clear(); itsMarathonTime = false; timerForWinnerSelection.cancel(); @@ -502,6 +527,7 @@ class MarathonProvider extends ChangeNotifier { answerStatusesList[i] = QuestionCardStatus.question; } } + AppRoutes.navigatorKey.currentContext!.setLocale(savedLocale); notifyListeners(); } diff --git a/lib/ui/marathon/marathon_screen.dart b/lib/ui/marathon/marathon_screen.dart index 9d76cf7..6dbbd18 100644 --- a/lib/ui/marathon/marathon_screen.dart +++ b/lib/ui/marathon/marathon_screen.dart @@ -78,7 +78,12 @@ class MarathonScreen extends StatelessWidget { 16.height, Column( children: [ - (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn)!.toText22( + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.demoMarathonDetailModel.selectedLanguage!, + arabicContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr!, + englishContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!, + ).toText22( color: MyColors.grey3AColor, isCentered: true, ), @@ -92,7 +97,12 @@ class MarathonScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ "${LocaleKeys.sponsoredBy.tr()} ".toText14(color: MyColors.grey77Color), - (AppState().isArabic(context) ? provider.demoMarathonDetailModel.sponsors!.first.nameAr ?? "" : provider.demoMarathonDetailModel.sponsors!.first.nameEn ?? "").toText14( + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.demoMarathonDetailModel.selectedLanguage!, + englishContent: provider.demoMarathonDetailModel.sponsors!.first.nameEn!, + arabicContent: provider.demoMarathonDetailModel.sponsors!.first.nameAr!, + ).toText14( color: MyColors.darkTextColor, isBold: true, ), @@ -160,7 +170,12 @@ class MarathonScreen extends StatelessWidget { provider.iAmWinner ? Column( children: [ - (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn)!.toText22( + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.marathonDetailModel.selectedLanguage ?? 0, + arabicContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr ?? "", + englishContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn ?? "", + ).toText24( color: MyColors.grey3AColor, isCentered: true, ), @@ -169,26 +184,46 @@ class MarathonScreen extends StatelessWidget { ], ) : const SizedBox(), - 36.height, if (provider.selectedWinners != null) ...[ - ListView.separated( - shrinkWrap: true, - itemCount: provider.selectedWinners!.length, - separatorBuilder: (BuildContext context, int index) { - return const Divider(); - }, - itemBuilder: (BuildContext context, int index) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (AppState().isArabic(context) ? provider.selectedWinners![index].nameEn : provider.selectedWinners![index].nameEn)!.toText16( - color: MyColors.grey3AColor, - ), - provider.selectedWinners!.first.employeeId!.toText16(color: MyColors.grey57Color), - ], - ); - }, - ), + provider.selectedWinners!.length == 1 && !provider.iAmWinner + ? Column( + children: [ + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.marathonDetailModel.selectedLanguage ?? 0, + arabicContent: provider.selectedWinners![0].nameAr ?? "", + englishContent: provider.selectedWinners![0].nameEn ?? "", + ).toText24( + color: MyColors.grey3AColor, + isCentered: true, + ), + 8.height, + provider.selectedWinners![0].employeeId!.toText22(color: MyColors.grey57Color), + ], + ) + : ListView.separated( + shrinkWrap: true, + itemCount: provider.selectedWinners!.length, + separatorBuilder: (BuildContext context, int index) { + return const Divider(); + }, + itemBuilder: (BuildContext context, int index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.marathonDetailModel.selectedLanguage ?? 0, + arabicContent: provider.selectedWinners![index].nameAr ?? "", + englishContent: provider.selectedWinners![index].nameEn ?? "", + ).toText16( + color: MyColors.grey3AColor, + ), + provider.selectedWinners![index].employeeId!.toText16(color: MyColors.grey57Color), + ], + ); + }, + ), ], 60.height, if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...[ @@ -196,7 +231,12 @@ class MarathonScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ "${LocaleKeys.sponsoredBy.tr()} ".toText14(color: MyColors.grey77Color), - (AppState().isArabic(context) ? provider.marathonDetailModel.sponsors!.first.nameAr ?? "" : provider.marathonDetailModel.sponsors!.first.nameEn ?? "").toText14( + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.marathonDetailModel.selectedLanguage!, + arabicContent: provider.marathonDetailModel.sponsors!.first.nameAr ?? "", + englishContent: provider.marathonDetailModel.sponsors!.first.nameEn ?? "", + ).toText14( color: MyColors.darkTextColor, isBold: true, ), @@ -221,7 +261,7 @@ class MarathonScreen extends StatelessWidget { ); } - Widget getNameContainer(BuildContext context) { + Widget getNameContainer(BuildContext context, MarathonProvider provider) { return Container( height: 50, padding: const EdgeInsets.symmetric(horizontal: 20), @@ -233,8 +273,12 @@ class MarathonScreen extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!) - .toText17(isBold: true, color: MyColors.white), + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: (!AppState().getIsDemoMarathon ? provider.marathonDetailModel.selectedLanguage : provider.demoMarathonDetailModel.selectedLanguage) ?? 0, + arabicContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr ?? "", + englishContent: AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn ?? "", + ).toText17(isBold: true, color: MyColors.white), AppState().memberInformationList!.eMPLOYEENUMBER!.toText17(isBold: true, color: MyColors.white), ], ), @@ -299,8 +343,8 @@ class MarathonScreen extends StatelessWidget { else ...[ MarathonProgressContainer(provider: provider).paddingOnly(left: 21, right: 21), ], - if (provider.questionCardStatus == QuestionCardStatus.findingWinner) ...[ - getNameContainer(context), + if (provider.questionCardStatus == QuestionCardStatus.findingWinner && !provider.isUserOutOfGame) ...[ + getNameContainer(context, provider), ], QuestionCardBuilder( onQuestion: (BuildContext context) => const QuestionCard(), @@ -326,7 +370,7 @@ class MarathonScreen extends StatelessWidget { subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor, isCentered: true), ), onFindingWinner: (BuildContext context) => CustomStatusWidget( - asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168, reverse: false), + asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168, reverse: false, repeat: true), title: LocaleKeys.fingersCrossed.tr().toText22(color: MyColors.greenColor), subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor, isCentered: true), ), diff --git a/lib/ui/marathon/widgets/countdown_timer_detail_screen.dart b/lib/ui/marathon/widgets/countdown_timer_detail_screen.dart index c12a452..5c34b6a 100644 --- a/lib/ui/marathon/widgets/countdown_timer_detail_screen.dart +++ b/lib/ui/marathon/widgets/countdown_timer_detail_screen.dart @@ -123,10 +123,13 @@ class CountdownTimerForDetailScreen extends StatelessWidget { Widget buildCountdownTimer(CurrentRemainingTime? time) { if (provider.marathonDetailModel.startTime != null) { int remainingTimeInMinutes = DateTime.parse(provider.marathonDetailModel.startTime!).difference(DateTime.now()).inMinutes; - if (remainingTimeInMinutes <= 30) { - scheduleMicrotask(() { + if (remainingTimeInMinutes <= 30 && provider.canPlayDemo == true) { + // scheduleMicrotask(() { + // print("Timer TRUE!!!: ${time?.min.toString()}"); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { provider.canPlayDemo = false; }); + // }); } } diff --git a/lib/ui/marathon/widgets/marathon_banner.dart b/lib/ui/marathon/widgets/marathon_banner.dart index 223fb2c..536af9b 100644 --- a/lib/ui/marathon/widgets/marathon_banner.dart +++ b/lib/ui/marathon/widgets/marathon_banner.dart @@ -347,7 +347,10 @@ class MarathonBanner extends StatelessWidget { @override Widget build(BuildContext context) { MarathonProvider provider = context.read(); - + // if(provider.isUserWaiting) { + // provider.isUserWaiting = false; + // provider.getMarathonDetailsFromApi(); + // } return !provider.isPrivilegedWithMarathon ? getUnPrivilegedMarathon(context) : provider.isUpComingMarathon @@ -431,7 +434,12 @@ class MarathonBanner extends StatelessWidget { ), Flexible( child: Text( - (AppState().isArabic(context) ? provider.marathonDetailModel.titleAr ?? "" : provider.marathonDetailModel.titleEn ?? "").trimString(15), + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: provider.marathonDetailModel.selectedLanguage ?? 0, + englishContent: provider.marathonDetailModel.titleEn ?? "", + arabicContent: provider.marathonDetailModel.titleAr ?? "", + ), overflow: TextOverflow.ellipsis, style: TextStyle( fontStyle: FontStyle.italic, @@ -522,7 +530,6 @@ class MarathonBanner extends StatelessWidget { ], ).onPress(() async { int remainingTimeInMinutes = DateTime.parse(provider.marathonDetailModel.startTime!).difference(DateTime.now()).inMinutes; - if (remainingTimeInMinutes > 5 && provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) { Utils.showLoading(context); try { @@ -541,6 +548,7 @@ class MarathonBanner extends StatelessWidget { } else { Navigator.pushNamed(context, AppRoutes.marathonIntroScreen); } + provider.updateLanguageAsPerMarathon(context, provider.isUpComingMarathon ? provider.marathonDetailModel : provider.demoMarathonDetailModel); }), ) : getNoUpcomingMarathonWidget(context); diff --git a/lib/ui/marathon/widgets/marathon_details_card.dart b/lib/ui/marathon/widgets/marathon_details_card.dart index 11a3d84..65d6716 100644 --- a/lib/ui/marathon/widgets/marathon_details_card.dart +++ b/lib/ui/marathon/widgets/marathon_details_card.dart @@ -39,11 +39,23 @@ class MarathonDetailsCard extends StatelessWidget { ), 7.height, LocaleKeys.contestTopicAbout.tr().toText16(color: MyColors.grey77Color), - "${AppState().isArabic(context) ? marathonDetailModel.titleAr : marathonDetailModel.titleEn}".toText20(color: MyColors.textMixColor, isBold: true), + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: marathonDetailModel.selectedLanguage ?? 0, + englishContent: marathonDetailModel.titleEn ?? "", + arabicContent: marathonDetailModel.titleAr ?? "", + ).toText20(color: MyColors.textMixColor, isBold: true), Row( children: [ Flexible( - child: "${AppState().isArabic(context) ? marathonDetailModel.descAr : marathonDetailModel.descEn}".toText14(color: MyColors.grey77Color), + child: displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: marathonDetailModel.selectedLanguage ?? 0, + englishContent: marathonDetailModel.descEn ?? "", + arabicContent: marathonDetailModel.descAr ?? "", + ).toText14( + color: MyColors.grey77Color, + ), ) ], ), @@ -57,18 +69,22 @@ class MarathonDetailsCard extends StatelessWidget { child: SizedBox( height: 30, child: ListView.builder( - scrollDirection: Axis.horizontal, - shrinkWrap: true, - itemCount: marathonDetailModel.sponsors!.first.sponsorPrizes!.length, - itemBuilder: (BuildContext context, int index) { - SponsorPrizes prizes = marathonDetailModel.sponsors!.first.sponsorPrizes![index]; - return Container( - decoration: BoxDecoration(color: MyColors.backgroundColor, borderRadius: BorderRadius.circular(100), border: Border.all(color: MyColors.grey57Color.withOpacity(0.1))), - child: "${AppState().isArabic(context) ? prizes.marathonPrizeAr : prizes.marathonPrizeEn}" - .toText16(color: MyColors.greenColor, isBold: true) - .paddingOnly(left: 5, right: 5), - ).paddingOnly(left: 5); - }), + scrollDirection: Axis.horizontal, + shrinkWrap: true, + itemCount: marathonDetailModel.sponsors!.first.sponsorPrizes!.length, + itemBuilder: (BuildContext context, int index) { + SponsorPrizes prizes = marathonDetailModel.sponsors!.first.sponsorPrizes![index]; + return Container( + decoration: BoxDecoration(color: MyColors.backgroundColor, borderRadius: BorderRadius.circular(100), border: Border.all(color: MyColors.grey57Color.withOpacity(0.1))), + child: displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: marathonDetailModel.selectedLanguage ?? 0, + englishContent: prizes.marathonPrizeEn ?? "", + arabicContent: prizes.marathonPrizeAr ?? "", + ).toText16(color: MyColors.greenColor, isBold: true).paddingOnly(left: 5, right: 5), + ).paddingOnly(left: 5); + }, + ), ), ) ], @@ -77,7 +93,12 @@ class MarathonDetailsCard extends StatelessWidget { Row( children: [ "${LocaleKeys.sponsoredBy.tr()} ".toText16(color: MyColors.grey77Color), - "${AppState().isArabic(context) ? marathonDetailModel.sponsors?.first.nameAr : marathonDetailModel.sponsors?.first.nameEn}".toText16(color: MyColors.darkTextColor, isBold: true), + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: marathonDetailModel.selectedLanguage ?? 0, + englishContent: marathonDetailModel.sponsors?.first.nameEn ?? "", + arabicContent: marathonDetailModel.sponsors?.first.nameAr ?? "", + ).toText16(color: MyColors.darkTextColor, isBold: true), ], ), 10.height, diff --git a/lib/ui/marathon/widgets/marathon_header.dart b/lib/ui/marathon/widgets/marathon_header.dart index fed6caa..58820c1 100644 --- a/lib/ui/marathon/widgets/marathon_header.dart +++ b/lib/ui/marathon/widgets/marathon_header.dart @@ -3,8 +3,6 @@ import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; -import 'package:provider/provider.dart'; class MarathonHeader extends StatelessWidget { const MarathonHeader({Key? key}) : super(key: key); diff --git a/lib/ui/marathon/widgets/marathon_progress_container.dart b/lib/ui/marathon/widgets/marathon_progress_container.dart index 3de86ce..17f9d1c 100644 --- a/lib/ui/marathon/widgets/marathon_progress_container.dart +++ b/lib/ui/marathon/widgets/marathon_progress_container.dart @@ -66,7 +66,11 @@ class MarathonProgressContainer extends StatelessWidget { stepper( provider.currentQuestionNumber, provider.answerStatusesList, - AppState().getIsDemoMarathon ? provider.demoMarathonDetailModel.totalQuestions! : provider.marathonDetailModel.totalQuestions!, + (provider.demoMarathonDetailModel.totalQuestions != null || provider.marathonDetailModel.totalQuestions != null) + ? AppState().getIsDemoMarathon + ? provider.demoMarathonDetailModel.totalQuestions! + : provider.marathonDetailModel.totalQuestions! + : 10, provider.isUserOutOfGame, ), 8.height, diff --git a/lib/ui/marathon/widgets/question_card.dart b/lib/ui/marathon/widgets/question_card.dart index 5246426..8aa76bd 100644 --- a/lib/ui/marathon/widgets/question_card.dart +++ b/lib/ui/marathon/widgets/question_card.dart @@ -85,7 +85,12 @@ class CardContent extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 15), child: Text( - AppState().isArabic(context) ? "${provider.currentQuestion.titleAr}" ?? "" : provider.currentQuestion.titleEn ?? "", + displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: (AppState().getIsDemoMarathon ? provider.demoMarathonDetailModel.selectedLanguage : provider.marathonDetailModel.selectedLanguage) ?? 0, + englishContent: provider.currentQuestion.titleEn ?? "", + arabicContent: provider.currentQuestion.titleAr ?? "", + ), style: const TextStyle( color: MyColors.white, fontSize: 16, @@ -125,7 +130,7 @@ class AnswerContent extends StatelessWidget { return AnswerTileForText( index: index, onAnswerTapped: () { - if (provider.totalCurrentQuestionTime - provider.currentGapTime <= 1) { + if (provider.totalCurrentQuestionTime - provider.currentGapTime <= 0) { null; } else { provider.updateCurrentQuestionOptionStatus(QuestionsOptionStatus.selected, index); @@ -170,9 +175,12 @@ class AnswerTileForText extends StatelessWidget { alignment: Alignment.centerLeft, decoration: MyDecorations.getAnswersContainerColor(provider.currentQuestion.questionOptions![index].optionStatus!), child: Center( - child: (AppState().isArabic(context) ? provider.currentQuestion.questionOptions![index].titleAr! : provider.currentQuestion.questionOptions![index].titleEn!) - .toText16(color: provider.isUserOutOfGame ? MyColors.darkTextColor : getAnswerTextColor(provider.currentQuestion.questionOptions![index].optionStatus!)) - .paddingOnly(top: 13, bottom: 13), + child: displayLocalizedContent( + isPhoneLangArabic: AppState().isArabic(context), + selectedLanguage: (AppState().getIsDemoMarathon ? provider.demoMarathonDetailModel.selectedLanguage : provider.marathonDetailModel.selectedLanguage) ?? 0, + englishContent: provider.currentQuestion.questionOptions![index].titleEn ?? "", + arabicContent: provider.currentQuestion.questionOptions![index].titleAr ?? "", + ).toText16(color: provider.isUserOutOfGame ? MyColors.darkTextColor : getAnswerTextColor(provider.currentQuestion.questionOptions![index].optionStatus!)).paddingOnly(top: 13, bottom: 13), ), ), ); diff --git a/lib/ui/misc/request_submit_screen.dart b/lib/ui/misc/request_submit_screen.dart index d1264fd..3611241 100644 --- a/lib/ui/misc/request_submit_screen.dart +++ b/lib/ui/misc/request_submit_screen.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; @@ -71,27 +72,31 @@ class _RequestSubmitScreenState extends State { } } + Future>> addAttachments() async { + List> list = []; + if (attachmentFiles.isNotEmpty) { + attachmentFiles.asMap().forEach((index, value) { + String type = attachmentFiles[index].path.split('.').last; + String name = attachmentFiles[index].path.split('/').last; + List fileContent = value.readAsBytesSync(); + String encodedFile = base64Encode(fileContent); + list.add(AttachmentModel( + attachmentID: index, + pFILECONTENTTYPE: type, + pFILENAME: name, + pFILEDATA: encodedFile, + pTRANSACTIONID: params!.transactionId, + ).toJson()); + }); + } + return list; + } + void submitRequest() async { try { Utils.showLoading(context); - List> list = []; - if (attachmentFiles.isNotEmpty) { - attachments.asMap().forEach((index, value) async { - String type = attachmentFiles[index].path.split('.').last; - String name = attachmentFiles[index].path.split('/').last; - // List fileContent = await value.readAsBytes(); - // String encodedFile = base64Encode(fileContent); - list.add(AttachmentModel( - attachmentID: index, - pFILECONTENTTYPE: type, - pFILENAME: name, - pFILEDATA: value, - pTRANSACTIONID: params!.transactionId, - ).toJson()); - }); - } + List> list = await addAttachments(); await MyAttendanceApiClient().addAttachment(list); - if (params!.approvalFlag == 'phone_numbers') { await ProfileApiClient().startPhoneApprovalProcess( "SUBMIT", diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart index 4d11bb8..bd9305d 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart @@ -67,8 +67,8 @@ class _DynamicInputScreenState extends State { tempVar = e.eSERVICESDV?.pIDCOLUMNNAME ?? ""; if (tempVar.isNotEmpty) { if (!tempVar.contains("/")) { - DateTime date = DateFormat('yyyy-MM-dd').parse(tempVar); - tempVar = DateFormat('yyyy/MM/dd HH:mm:ss').format(date); + DateTime date = DateFormat('yyyy-MM-dd', "en_US").parse(tempVar); + tempVar = DateFormat('yyyy/MM/dd HH:mm:ss', "en_US").format(date); } } } @@ -506,7 +506,7 @@ class _DynamicInputScreenState extends State { displayText = displayText.replaceAll(" 00:00:00", ""); } if (displayText.contains("/")) { - displayText = DateFormat('yyyy-MM-dd').format(DateFormat("yyyy/MM/dd").parse(displayText)); + displayText = DateFormat('yyyy-MM-dd', "en_US").format(DateFormat("yyyy/MM/dd", "en_US").parse(displayText)); } } return DynamicTextFieldWidget( @@ -517,7 +517,7 @@ class _DynamicInputScreenState extends State { onTap: () async { if ((getEitDffStructureList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) { if (getEitDffStructureList![index].isDefaultTypeIsCDPS) { - selectedDate = DateFormat("yyyy/MM/dd").parse(getEitDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", "")); + selectedDate = DateFormat("yyyy/MM/dd", "en_US").parse(getEitDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", "")); } else { selectedDate = DateTime.parse(getEitDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!); } @@ -576,9 +576,9 @@ class _DynamicInputScreenState extends State { tempDate = tempDate.replaceAll("00:00:00", '').trim(); } if (tempDate.contains("/")) { - selectedDate = DateFormat("yyyy/MM/dd").parse(tempDate); + selectedDate = DateFormat("yyyy/MM/dd", "en_US").parse(tempDate); } else { - selectedDate = DateFormat("yyyy-MM-dd").parse(tempDate); + selectedDate = DateFormat("yyyy-MM-dd", "en_US").parse(tempDate); } } else { selectedDate = DateTime.parse(getEitDffStructureList![index].eSERVICESDV!.pVALUECOLUMNNAME!); @@ -700,7 +700,7 @@ class _DynamicInputScreenState extends State { displayText = displayText.replaceAll(" 00:00:00", ""); } if (!displayText.contains("-")) { - displayText = DateFormat('yyyy-MM-dd').format(DateFormat("yyyy/MM/dd").parse(displayText)); + displayText = DateFormat('yyyy-MM-dd', "en_US").format(DateFormat("yyyy/MM/dd", "en_US").parse(displayText)); } } return DynamicTextFieldWidget( diff --git a/lib/ui/my_team/view_attendance.dart b/lib/ui/my_team/view_attendance.dart index 0896d5e..a45f35c 100644 --- a/lib/ui/my_team/view_attendance.dart +++ b/lib/ui/my_team/view_attendance.dart @@ -159,7 +159,7 @@ class _ViewAttendanceState extends State { children: [ Row( children: [ - "${DateFormat("MMMM-yyyy").format(formattedDate)}".toText16(color: MyColors.grey3AColor), + "${DateFormat("MMMM-yyyy", "en_US").format(formattedDate)}".toText16(color: MyColors.grey3AColor), const Icon(Icons.keyboard_arrow_down_rounded, color: MyColors.grey3AColor), ], ).onPress( @@ -439,7 +439,7 @@ class _ViewAttendanceState extends State { expand: false, builder: (_, controller) { dynamic dmyString = getScheduleShiftsDetailsList!.sCHEDULEDATE; - DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss").parse(dmyString); + DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss", "en_US").parse(dmyString); return Column( children: [ Container( @@ -468,7 +468,7 @@ class _ViewAttendanceState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "${DateFormat("MMMM-dd-yyyy").format(dateTime1).replaceAll('-', " ")}".toText24(isBold: true, color: Colors.white), + "${DateFormat("MMMM-dd-yyyy", "en_US").format(dateTime1).replaceAll('-', " ")}".toText24(isBold: true, color: Colors.white), LocaleKeys.attendanceDetails.tr().toText16(color: MyColors.greyACColor), 12.height, CircularStepProgressBar( diff --git a/lib/ui/payslip/monthly_pay_slip_screen.dart b/lib/ui/payslip/monthly_pay_slip_screen.dart index 543d81c..5289390 100644 --- a/lib/ui/payslip/monthly_pay_slip_screen.dart +++ b/lib/ui/payslip/monthly_pay_slip_screen.dart @@ -105,7 +105,7 @@ class _MonthlyPaySlipScreenState extends State { Container(alignment: Alignment.centerLeft, child: LocaleKeys.month.tr().toText17(isBold: true, color: MyColors.darkIconColor)), Row( children: [ - DateFormat("MMMM-yyyy").format(DateFormat("MM/dd/yyyy").parse(paySlipList[selectedMonthIndex!].pAYMENTDATE!)).toText16(color: MyColors.greyACColor), + DateFormat("MMMM-yyyy", "en_US").format(DateFormat("MM/dd/yyyy", "en_US").parse(paySlipList[selectedMonthIndex!].pAYMENTDATE!)).toText16(color: MyColors.greyACColor), const Icon(Icons.keyboard_arrow_down_rounded, color: MyColors.greyACColor), ], ).onPress(() async { diff --git a/lib/ui/profile/add_update_family_member.dart b/lib/ui/profile/add_update_family_member.dart index 6263b95..5a31447 100644 --- a/lib/ui/profile/add_update_family_member.dart +++ b/lib/ui/profile/add_update_family_member.dart @@ -238,7 +238,7 @@ class _AddUpdateFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = (DateFormat('yyyy-MM-dd').format(dateValue)); + date = (DateFormat('yyyy-MM-dd', "en_US").format(dateValue)); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; setState(() {}); }, @@ -298,7 +298,7 @@ class _AddUpdateFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = (DateFormat('yyyy-MM-dd').format(dateValue)); + date = (DateFormat('yyyy-MM-dd', "en_US").format(dateValue)); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; setState(() {}); }, @@ -357,7 +357,7 @@ class _AddUpdateFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = (DateFormat('yyyy-MM-dd').format(dateValue)); + date = (DateFormat('yyyy-MM-dd', "en_US").format(dateValue)); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; setState(() {}); }, diff --git a/lib/ui/profile/delete_family_member.dart b/lib/ui/profile/delete_family_member.dart index d42c129..3b73512 100644 --- a/lib/ui/profile/delete_family_member.dart +++ b/lib/ui/profile/delete_family_member.dart @@ -88,8 +88,8 @@ class _DeleteFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = DateFormat('yyyy/MM/dd').format(dateValue); - datePar = DateFormat('yyyy/MM/dd hh:mm:ss').format(dateValue); + date = DateFormat('yyyy/MM/dd', "en_US").format(dateValue); + datePar = DateFormat('yyyy/MM/dd hh:mm:ss', "en_US").format(dateValue); setState(() {}); }, ).paddingOnly(bottom: 12), diff --git a/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart b/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart index 42f0758..a3b4656 100644 --- a/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart +++ b/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart @@ -242,10 +242,10 @@ class _DynamicInputScreenState extends State { DateTime date1 = DateTime(date.year, date.month, date.day); getAddressDffStructureList![index].dESCFLEXCONTEXTNAME = date.toString(); ESERVICESDV eservicesdv = ESERVICESDV( - pIDCOLUMNNAME: DateFormat('yyyy-MM-dd').format(date1), + pIDCOLUMNNAME: DateFormat('yyyy-MM-dd', "en_US").format(date1), pRETURNMSG: "null", pRETURNSTATUS: getAddressDffStructureList![index].dEFAULTVALUE, - pVALUECOLUMNNAME: DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); + pVALUECOLUMNNAME: DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date)); getAddressDffStructureList![index].eSERVICESDV = eservicesdv; setState(() {}); if (model.cHILDSEGMENTSVSSplited?.isNotEmpty ?? false) { @@ -270,8 +270,8 @@ class _DynamicInputScreenState extends State { // for date format type, date format is changed tempVar = e.eSERVICESDV?.pVALUECOLUMNNAME ?? ""; if (tempVar.isNotEmpty) { - DateTime date = DateFormat('yyyy-MM-dd').parse(tempVar); - tempVar = DateFormat('dd-MMM-yyy').format(date); + DateTime date = DateFormat('yyyy-MM-dd', "en_US").parse(tempVar); + tempVar = DateFormat('dd-MMM-yyy', "en_US").format(date); if (e.aPPLICATIONCOLUMNNAME == null) { effectiveDate = tempVar; } @@ -294,7 +294,7 @@ class _DynamicInputScreenState extends State { values, dynamicParams!.correctOrNew, countryCode, - effectiveDate.isEmpty ? DateFormat('dd-MMM-yyy').format(DateTime.now()) : effectiveDate, + effectiveDate.isEmpty ? DateFormat('dd-MMM-yyy', "en_US").format(DateTime.now()) : effectiveDate, ); Utils.hideLoading(context); diff --git a/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart b/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart index 93a8eb3..2b00e4c 100644 --- a/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart +++ b/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart @@ -233,10 +233,10 @@ class _DynamicInputScreenState extends State { DateTime date1 = DateTime(date.year, date.month, date.day); getBasicDetDffStructureList![index].userBasicDetail!.sEGMENTVALUEDSP = date.toString(); ESERVICESDV eservicesdv = ESERVICESDV( - pIDCOLUMNNAME: DateFormat('yyyy-MM-dd').format(date1), + pIDCOLUMNNAME: DateFormat('yyyy-MM-dd', "en_US").format(date1), pRETURNMSG: "null", pRETURNSTATUS: getBasicDetDffStructureList![index].dEFAULTVALUE, - pVALUECOLUMNNAME: DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); + pVALUECOLUMNNAME: DateFormat('yyyy-MM-ddThh:mm:ss.s', "en_US").format(date)); getBasicDetDffStructureList![index].eSERVICESDV = eservicesdv; setState(() {}); if (model.cHILDSEGMENTSVSSplited?.isNotEmpty ?? false) { @@ -371,8 +371,8 @@ class _DynamicInputScreenState extends State { // for date format type, date format is changed tempVar = e.eSERVICESDV?.pVALUECOLUMNNAME ?? ""; if (tempVar.isNotEmpty) { - DateTime date = DateFormat('yyyy-MM-dd').parse(tempVar); - tempVar = DateFormat('yyyy/MM/dd HH:mm:ss').format(date); + DateTime date = DateFormat('yyyy-MM-dd', "en_US").parse(tempVar); + tempVar = DateFormat('yyyy/MM/dd HH:mm:ss', "en_US").format(date); } } return ValidateEitTransactionModel(dATEVALUE: null, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: null, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: tempVar.toString()).toJson(); diff --git a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart index 5e3f749..75d1da2 100644 --- a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart +++ b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart @@ -18,7 +18,7 @@ import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/select_cat import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/button/simple_button.dart'; import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; -import 'package:mohem_flutter_app/widgets/image_picker.dart'; +import 'package:mohem_flutter_app/widgets/image_picker.dart' as imagePicker; import 'package:mohem_flutter_app/widgets/radio/show_radio.dart'; class AddItemDetailsFragment extends StatefulWidget { @@ -200,14 +200,22 @@ class _AddItemDetailsFragmentState extends State { children: [ title.toText16().expanded, 6.width, - SimpleButton(LocaleKeys.add.tr(), () { - ImageOptions.showImageOptionsNew(context, false, (String image, File file) { - setState(() { - images.add(image); - Navigator.of(context).pop(); - }); - }); - }, fontSize: 14), + SimpleButton( + LocaleKeys.add.tr(), + () { + if (images.length < 3) { + imagePicker.ImageOptions.showImageOptionsNew(context, false, (String image, File file) { + setState(() { + images.add(image); + Navigator.of(context).pop(); + }); + }); + } else { + Utils.showToast("The maximum no. of images allowed is 3."); + } + }, + fontSize: 14, + ), ], ), if (images.isNotEmpty) 12.height, diff --git a/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart index ae715fe..1ecd0c9 100644 --- a/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart +++ b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -98,9 +96,7 @@ class _ItemsForSaleFragmentState extends State { currentCategoryID == getSaleCategoriesList[index].categoryID ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), ], ).expanded, - AppState().isArabic(context) - ?getSaleCategoriesList[index].titleAr!.toText10() - :getSaleCategoriesList[index].title!.toText10() + AppState().isArabic(context) ? getSaleCategoriesList[index].titleAr!.toText10() : getSaleCategoriesList[index].title!.toText10() ], ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true), ), @@ -163,8 +159,8 @@ class _ItemsForSaleFragmentState extends State { aspectRatio: 148 / 127, child: ClipRRect( borderRadius: BorderRadius.circular(6), - child: Image.memory( - base64Decode(getItemsForSaleList.itemAttachments![0].content!), + child: Image.network( + getItemsForSaleList.itemAttachments![0].filePath!, fit: BoxFit.cover, ), ), diff --git a/lib/ui/screens/items_for_sale/item_for_sale_detail.dart b/lib/ui/screens/items_for_sale/item_for_sale_detail.dart index ee76943..dec86c0 100644 --- a/lib/ui/screens/items_for_sale/item_for_sale_detail.dart +++ b/lib/ui/screens/items_for_sale/item_for_sale_detail.dart @@ -1,5 +1,4 @@ -import 'dart:convert'; - +import 'package:carousel_slider/carousel_slider.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -24,6 +23,7 @@ class ItemForSaleDetailPage extends StatefulWidget { class _ItemForSaleDetailPageState extends State { late GetItemsForSaleList getItemsForSaleList; + int _current = 0; @override Widget build(BuildContext context) { @@ -46,15 +46,32 @@ class _ItemForSaleDetailPageState extends State { transitionOnUserGestures: true, child: AspectRatio( aspectRatio: 322 / 261, - child: ClipRRect( - borderRadius: BorderRadius.circular(6), - child: Image.memory( - base64Decode(getItemsForSaleList.itemAttachments![0].content!), - fit: BoxFit.cover, - ), + child: CarouselSlider( + items: getItemImages(), + options: CarouselOptions( + enableInfiniteScroll: false, + onPageChanged: (index, reason) { + setState(() { + _current = index; + }); + }), ), ), ).paddingAll(8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: getItemImages().asMap().entries.map((entry) { + return Container( + width: 8.0, + height: 8.0, + margin: const EdgeInsets.symmetric(horizontal: 4.0), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: (Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black).withOpacity(_current == entry.key ? 0.9 : 0.4), + ), + ); + }).toList(), + ), Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -88,22 +105,34 @@ class _ItemForSaleDetailPageState extends State { ).expanded, Row( children: [ - DefaultButton("Email", () async { - Uri emailLaunchUri = Uri( - scheme: 'mailto', - path: getItemsForSaleList.emailAddress, - ); - launchUrl(emailLaunchUri); - }, iconData: Icons.email_sharp, isTextExpanded: false) + DefaultButton( + "Email", + getItemsForSaleList.status == 'Approved' + ? () async { + Uri emailLaunchUri = Uri( + scheme: 'mailto', + path: getItemsForSaleList.emailAddress, + ); + launchUrl(emailLaunchUri); + } + : null, + iconData: Icons.email_sharp, + isTextExpanded: false) .expanded, 8.width, - DefaultButton("Call", () async { - Uri callLaunchUri = Uri( - scheme: 'tel', - path: getItemsForSaleList.mobileNumber, - ); - launchUrl(callLaunchUri); - }, iconData: Icons.call_sharp, isTextExpanded: false) + DefaultButton( + "Call", + getItemsForSaleList.status == 'Approved' + ? () async { + Uri callLaunchUri = Uri( + scheme: 'tel', + path: getItemsForSaleList.mobileNumber, + ); + launchUrl(callLaunchUri); + } + : null, + iconData: Icons.call_sharp, + isTextExpanded: false) .expanded, ], ).insideContainer, @@ -111,4 +140,23 @@ class _ItemForSaleDetailPageState extends State { ), ); } + + List getItemImages() { + List itemImages = []; + getItemsForSaleList.itemAttachments!.forEach((element) { + itemImages.add( + Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + getItemsForSaleList.itemAttachments![0].filePath!, + fit: BoxFit.cover, + ), + ), + ), + ); + }); + return itemImages; + } } diff --git a/lib/ui/screens/my_requests/new_request.dart b/lib/ui/screens/my_requests/new_request.dart index 7a73497..670b18d 100644 --- a/lib/ui/screens/my_requests/new_request.dart +++ b/lib/ui/screens/my_requests/new_request.dart @@ -189,7 +189,7 @@ class _NewRequestState extends State { displayText = displayText.replaceAll(" 00:00:00", ""); } if (!displayText.contains("-")) { - displayText = DateFormat('yyyy-MM-dd').format(DateFormat("yyyy/MM/dd").parse(displayText)); + displayText = DateFormat('yyyy-MM-dd', "en_US").format(DateFormat("yyyy/MM/dd", "en_US").parse(displayText)); } } return DynamicTextFieldWidget( @@ -200,7 +200,7 @@ class _NewRequestState extends State { onTap: () async { if ((getCCPDFFStructureModelList![index].eSERVICESDV?.pVALUECOLUMNNAME != null)) { if (getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS) { - selectedDate = DateFormat("yyyy/MM/dd").parse(getCCPDFFStructureModelList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", "")); + selectedDate = DateFormat("yyyy/MM/dd", "en_US").parse(getCCPDFFStructureModelList![index].eSERVICESDV!.pVALUECOLUMNNAME!.replaceAll('/"', '').replaceAll(" 00:00:00", "")); } else { selectedDate = DateTime.parse(getCCPDFFStructureModelList![index].eSERVICESDV!.pVALUECOLUMNNAME!); } @@ -211,16 +211,16 @@ class _NewRequestState extends State { ESERVICESDV eservicesdv; if (getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS) { eservicesdv = ESERVICESDV( - pIDCOLUMNNAME: DateFormat('yyyy/MM/dd HH:MM:SS').format(date1), + pIDCOLUMNNAME: DateFormat('yyyy/MM/dd HH:MM:SS', "en_US").format(date1), pRETURNMSG: "null", pRETURNSTATUS: getCCPDFFStructureModelList![index].dEFAULTVALUE, - pVALUECOLUMNNAME: getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS ? DateFormat('yyyy/MM/dd HH:MM:SS').format(date) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); + pVALUECOLUMNNAME: getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS ? DateFormat('yyyy/MM/dd HH:MM:SS', "en_US").format(date) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); } else { eservicesdv = ESERVICESDV( - pIDCOLUMNNAME: DateFormat('yyyy-MM-dd').format(date1), + pIDCOLUMNNAME: DateFormat('yyyy-MM-dd', "en_US").format(date1), pRETURNMSG: "null", pRETURNSTATUS: getCCPDFFStructureModelList![index].dEFAULTVALUE, - pVALUECOLUMNNAME: getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS ? DateFormat('yyyy-MM-dd hh:mm:ss').format(date) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); + pVALUECOLUMNNAME: getCCPDFFStructureModelList![index].isDefaultTypeIsCDPS ? DateFormat('yyyy-MM-dd hh:mm:ss', "en_US").format(date) : DateFormat('yyyy-MM-ddThh:mm:ss.s').format(date)); } getCCPDFFStructureModelList![index].eSERVICESDV = eservicesdv; setState(() {}); diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart index d980728..b4cb242 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -85,7 +85,7 @@ class _OffersAndDiscountsDetailsState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - getOffersList[0].discount!.toText16(isBold: true), + getOffersList[0].discountDescription!.toText16(isBold: true), InkWell( onTap: () { _shareOfferAsImage(); diff --git a/lib/ui/termination/end_employement.dart b/lib/ui/termination/end_employement.dart index cdfa46d..8837806 100644 --- a/lib/ui/termination/end_employement.dart +++ b/lib/ui/termination/end_employement.dart @@ -107,8 +107,8 @@ class _EndEmploymentScreenState extends State { tempVar = e.eSERVICESDV?.pIDCOLUMNNAME ?? ""; if (tempVar.isNotEmpty) { if (!tempVar.contains("/")) { - DateTime date = DateFormat('yyyy-MM-dd').parse(tempVar); - tempVar = DateFormat('yyyy/MM/dd HH:mm:ss').format(date); + DateTime date = DateFormat('yyyy-MM-dd', "en_US").parse(tempVar); + tempVar = DateFormat('yyyy/MM/dd HH:mm:ss', "en_US").format(date); } } } diff --git a/lib/ui/work_list/item_history_screen.dart b/lib/ui/work_list/item_history_screen.dart index a3c34a9..5aa18b3 100644 --- a/lib/ui/work_list/item_history_screen.dart +++ b/lib/ui/work_list/item_history_screen.dart @@ -7,6 +7,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/get_mo_Item_history_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_Item_history_list_model.dart'; +import 'package:mohem_flutter_app/models/get_pr_information_list.dart'; import 'package:mohem_flutter_app/models/get_quotation_analysis_list_model.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; @@ -18,8 +19,10 @@ class ItemHistoryScreenParams { int? pItemId; int? pPoHeaderId; int? pOrgId; + bool isPRInfo; + GetPRInformationList? getPRInformationList; - ItemHistoryScreenParams({@required this.title, this.isItemHistory = true, this.isMO = true, this.pItemId, this.pPoHeaderId, this.pOrgId}); + ItemHistoryScreenParams({@required this.title, this.isItemHistory = true, this.isMO = true, this.isPRInfo = false, this.getPRInformationList, this.pItemId, this.pPoHeaderId, this.pOrgId}); } class ItemHistoryScreen extends StatefulWidget { @@ -51,7 +54,9 @@ class _ItemHistoryScreenState extends State { void loadData() { if (_screenParams == null) { _screenParams = ModalRoute.of(context)!.settings.arguments as ItemHistoryScreenParams; - getDataFromApi(); + if (!_screenParams!.isPRInfo) { + getDataFromApi(); + } } } @@ -86,15 +91,45 @@ class _ItemHistoryScreenState extends State { padding: const EdgeInsets.all(21), physics: const BouncingScrollPhysics(), children: [ + if (_screenParams!.isPRInfo) prLinesDataView(), if (moItemHistoryList.isNotEmpty) loadMoItemHistoryData(), if (poItemHistoryList.isNotEmpty) loadPoItemHistoryData(), if (quotationAnalysisList.isNotEmpty) loadQuotationAnalysisData(), - if (moItemHistoryList.isEmpty && poItemHistoryList.isEmpty && quotationAnalysisList.isEmpty) Utils.getNoDataWidget(context), + if (moItemHistoryList.isEmpty && poItemHistoryList.isEmpty && quotationAnalysisList.isEmpty && !_screenParams!.isPRInfo) Utils.getNoDataWidget(context), ], ), ); } + Widget prLinesDataView() { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cxt, index) => Column( + children: [ + ItemDetailGrid( + ItemDetailViewCol("Cost Center", _screenParams!.getPRInformationList!.pRLines![index].cOSTCENTER ?? ""), + ItemDetailViewCol("Code", _screenParams!.getPRInformationList!.pRLines![index].iTEMCODE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol("Unit", _screenParams!.getPRInformationList!.pRLines![index].uOM ?? ""), + ItemDetailViewCol("Price (SAR)", _screenParams!.getPRInformationList!.pRLines![index].uNITPRICE.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol("Amount (SAR)", _screenParams!.getPRInformationList!.pRLines![index].lINEAMOUNT.toString() ?? ""), + ItemDetailViewCol("Quantity", _screenParams!.getPRInformationList!.pRLines![index].qUANTITY.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol("AMU (Last 3 months)", _screenParams!.getPRInformationList!.pRLines![index].iTEMAMU.toString() ?? ""), + Container(), + isItLast: true, + ), + ], + ).objectContainerView(), + separatorBuilder: (cxt, index) => 12.height, + itemCount: _screenParams!.getPRInformationList!.pRLines!.length); + } + Widget loadMoItemHistoryData() { return ListView.separated( shrinkWrap: true, diff --git a/lib/ui/work_list/worklist_detail_screen.dart b/lib/ui/work_list/worklist_detail_screen.dart index 9bc0222..7a489c3 100644 --- a/lib/ui/work_list/worklist_detail_screen.dart +++ b/lib/ui/work_list/worklist_detail_screen.dart @@ -1003,19 +1003,11 @@ class _WorkListDetailScreenState extends State { try { isActionHistoryLoaded = false; actionHistoryList.clear(); - // if (apiCallCount == 0) Utils.showLoading(context); - // apiCallCount++; actionHistoryList = await WorkListApiClient().getActionHistory(workListData!.nOTIFICATIONID!); - // apiCallCount--; - // if (apiCallCount == 0) { - // Utils.hideLoading(context); setState(() { isActionHistoryLoaded = true; }); - // } } catch (ex) { - // apiCallCount--; - // Utils.hideLoading(context); Utils.handleException(ex, context, null); } } @@ -1024,88 +1016,15 @@ class _WorkListDetailScreenState extends State { try { isAttachmentLoaded = false; getAttachmentList.clear(); - // if (apiCallCount == 0) Utils.showLoading(context); - // apiCallCount++; getAttachmentList = await WorkListApiClient().getAttachments(workListData!.nOTIFICATIONID!); - // apiCallCount--; - // if (apiCallCount == 0) { - // Utils.hideLoading(context); setState(() { isAttachmentLoaded = true; }); - // } } catch (ex) { - // apiCallCount--; - // Utils.hideLoading(context); Utils.handleException(ex, context, null); } } - // Widget showUpdateContinueSheet(List list) { - // double itemHeight = 0; - // double itemWidth = 0; - // var size = MediaQuery.of(context).size; - // itemHeight = (size.height - kToolbarHeight - 24) / 9; - // itemWidth = size.width / 2; - // return Column( - // children: [ - // if ((workListData?.sUBJECT ?? "").isNotEmpty) workListData!.sUBJECT!.toText14().paddingOnly(top: 10, right: 21, left: 21, bottom: 21), - // ListView.separated( - // shrinkWrap: true, - // physics: const NeverScrollableScrollPhysics(), - // itemBuilder: (cxt, index) { - // List dataList = list.isEmpty ? [] : (list[index].collectionNotification ?? []); - // dataList = dataList.where((o) => o.displayFlag == "Y").toList(); - // bool isOdd = false; - // if (dataList.length % 2 != 0) { - // isOdd = true; - // dataList.add(new CollectionNotificationEit()); - // } - // return GridView.builder( - // itemCount: dataList.length, - // shrinkWrap: true, - // physics: const NeverScrollableScrollPhysics(), - // itemBuilder: (context, index) => ItemDetailViewGridItem( - // index, - // dataList[index].segmentPrompt, - // dataList[index].segmentValueDsp, - // isNeedToShowEmptyDivider: (dataList.length == index + 1) - // ? isOdd - // ? true - // : false - // : false, - // ), - // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - // crossAxisCount: 2, - // childAspectRatio: (itemWidth / itemHeight), - // ), - // ).objectContainerView(); - // }, - // separatorBuilder: (cxt, index) => 12.height, - // itemCount: list.length, - // ), - // Padding( - // padding: const EdgeInsets.only(right: 21, left: 21, bottom: 21), - // child: Row( - // children: [ - // DefaultButton( - // LocaleKeys.edit.tr(), - // () => performEditAction(), - // colors: const [Color(0xffE47A7E), Color(0xffDE6D71)], - // ).expanded, - // 8.width, - // DefaultButton( - // LocaleKeys.next.tr(), - // () => performNextAction(), - // colors: const [Color(0xff28C884), Color(0xff1BB271)], - // ).expanded, - // ], - // ), - // ), - // ], - // ); - // } - Widget showLoadingAnimation() { return Lottie.asset( 'assets/lottie/loading.json', diff --git a/lib/ui/work_list/worklist_fragments/request_fragment.dart b/lib/ui/work_list/worklist_fragments/request_fragment.dart index a13f91f..5a77597 100644 --- a/lib/ui/work_list/worklist_fragments/request_fragment.dart +++ b/lib/ui/work_list/worklist_fragments/request_fragment.dart @@ -1,7 +1,9 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/worklist/worklist_api_client.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/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; @@ -10,7 +12,8 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/get_item_creation_ntf_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_mo_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_po_notification_body_list_model.dart'; -import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart'; +import 'package:mohem_flutter_app/models/get_pr_information_list.dart'; +import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart' as get_pr_notification_body_list_model; import 'package:mohem_flutter_app/ui/work_list/item_history_screen.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; @@ -19,14 +22,14 @@ class RequestFragment extends StatelessWidget { final List moNotificationBodyList; final List itemCreationLines; final List poLinesList; - final List prLinesList; + final List prLinesList; RequestFragment({ Key? key, this.moNotificationBodyList = const [], this.itemCreationLines = const [], this.poLinesList = const [], - this.prLinesList = const [], + this.prLinesList = const [], }) : super(key: key); @override @@ -37,7 +40,7 @@ class RequestFragment extends StatelessWidget { padding: const EdgeInsets.all(21), children: [ if (moNotificationBodyList.isNotEmpty) moNotificationDataView(), - if (poLinesList.isNotEmpty) poLinesDataView(), + if (poLinesList.isNotEmpty) poLinesDataView(context), if (itemCreationLines.isNotEmpty) itemCreationLinesView(), if (prLinesList.isNotEmpty) prLinesDataView(), ], @@ -45,7 +48,7 @@ class RequestFragment extends StatelessWidget { ); } - Widget poLinesDataView() { + Widget poLinesDataView(BuildContext context) { return ExpandableNotifier( child: ListView.separated( shrinkWrap: true, @@ -87,22 +90,38 @@ class RequestFragment extends StatelessWidget { 12.height, Row( children: [ - DefaultButton(LocaleKeys.itemHistory.tr(), () { - Navigator.pushNamed( - cxt, - AppRoutes.itemHistory, - arguments: ItemHistoryScreenParams(title: LocaleKeys.itemHistory.tr(), isMO: false, pItemId: poLinesList[index].iTEMID), - ); - }).expanded, + DefaultButton( + LocaleKeys.itemHistory.tr(), + () { + Navigator.pushNamed( + cxt, + AppRoutes.itemHistory, + arguments: ItemHistoryScreenParams(title: LocaleKeys.itemHistory.tr(), isMO: false, pItemId: poLinesList[index].iTEMID), + ); + }, + fontSize: 13, + ).expanded, 12.width, - DefaultButton(LocaleKeys.quotationAnalysis.tr(), () { - Navigator.pushNamed( - cxt, - AppRoutes.itemHistory, - arguments: ItemHistoryScreenParams( - isItemHistory: false, isMO: false, title: LocaleKeys.quotationAnalysis.tr(), pItemId: poLinesList[index].iTEMID, pPoHeaderId: poLinesList[index].pOHEADERID), - ); - }).expanded, + DefaultButton( + LocaleKeys.quotationAnalysis.tr(), + () { + Navigator.pushNamed( + cxt, + AppRoutes.itemHistory, + arguments: ItemHistoryScreenParams( + isItemHistory: false, isMO: false, title: LocaleKeys.quotationAnalysis.tr(), pItemId: poLinesList[index].iTEMID, pPoHeaderId: poLinesList[index].pOHEADERID), + ); + }, + fontSize: 13, + ).expanded, + 12.width, + DefaultButton( + "PR Details", + () { + getPRDetails(context, poLinesList[index].pOLINEID.toString()); + }, + fontSize: 13, + ).expanded, ], ) ], @@ -263,4 +282,23 @@ class RequestFragment extends StatelessWidget { separatorBuilder: (cxt, index) => 12.height, itemCount: itemCreationLines.length); } + + void getPRDetails(BuildContext context, String poLineID) async { + try { + Utils.showLoading(context); + GetPRInformationList? getPRInformationList = GetPRInformationList(); + getPRInformationList = await WorkListApiClient().getPRDetailsForPO(poLineID); + Utils.hideLoading(context); + + Navigator.pushNamed( + context, + AppRoutes.itemHistory, + arguments: ItemHistoryScreenParams( + isItemHistory: false, isMO: false, isPRInfo: true, getPRInformationList: getPRInformationList, title: "PR Details", pItemId: 0, pPoHeaderId: 0), + ); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } } diff --git a/lib/widgets/Updater.dart b/lib/widgets/Updater.dart index 82cc172..3042dbb 100644 --- a/lib/widgets/Updater.dart +++ b/lib/widgets/Updater.dart @@ -1,18 +1,20 @@ /* ZiK */ import 'dart:async'; + import 'package:flutter/cupertino.dart'; typedef ChildProvider = Widget Function(BuildContext context, E? data); -class Updater extends StatelessWidget{ +class Updater extends StatelessWidget { final ChildProvider childProvider; StreamController? sink; T? initialData; List _history = []; Stream? _stream; - Updater({T? initialData, required this.childProvider}){ + + Updater({T? initialData, required this.childProvider}) { this.sink = StreamController(); this.initialData = initialData; _stream = this.sink?.stream; @@ -23,17 +25,17 @@ class Updater extends StatelessWidget{ return StreamBuilder( initialData: this.initialData, stream: _stream, - builder: (ctx, snapshot){ - return childProvider(context, snapshot.data); - }); + builder: (ctx, snapshot) { + return childProvider(context, snapshot.data); + }); } void pushData(T? data) { _history.add(data); sink?.sink.add(data); } - + List getDataHistory() => _history; - T? getLatestData() => _history.last; -} \ No newline at end of file + T? getLatestData() => _history.last; +} diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart index 1096f24..6f9898e 100644 --- a/lib/widgets/app_bar_widget.dart +++ b/lib/widgets/app_bar_widget.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; 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/provider/chat_provider_model.dart'; -import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; -import 'package:provider/provider.dart'; AppBar AppBarWidget(BuildContext context, {required String title, diff --git a/lib/widgets/balances_dashboard_widget.dart b/lib/widgets/balances_dashboard_widget.dart index 8909dab..9c1fafe 100644 --- a/lib/widgets/balances_dashboard_widget.dart +++ b/lib/widgets/balances_dashboard_widget.dart @@ -63,7 +63,7 @@ class _BalancesDashboardWidgetState extends State { void changeAccrualDate(bool showLoading) async { try { if (showLoading) Utils.showLoading(context); - List accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(accrualDateTime), empID: widget.selectedEmp); + List accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy", "en_US").format(accrualDateTime), empID: widget.selectedEmp); if (accrualList.isNotEmpty) { if (widget.isLeaveBalance) { leaveBalanceAccrual = accrualList[0]; diff --git a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart index b20674e..d810457 100644 --- a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart +++ b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -26,7 +24,6 @@ import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; import 'package:provider/provider.dart'; -import 'package:pull_to_refresh/pull_to_refresh.dart'; class SearchEmployeeBottomSheet extends StatefulWidget { int? notificationID; @@ -243,11 +240,11 @@ class _SearchEmployeeBottomSheetState extends State { children: [ Stack( children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 48, - width: 48, - ), + SvgPicture.asset( + "assets/images/user.svg", + height: 48, + width: 48, + ), Positioned( right: 5, bottom: 1, @@ -307,13 +304,7 @@ class _SearchEmployeeBottomSheetState extends State { ).onPress( () { if (provider.chatUsersList![index].isFav == null || provider.chatUsersList![index].isFav == false) { - provider - .favoriteUser( - userID: AppState().chatDetails!.response!.id!, - targetUserID: provider.chatUsersList![index].id!, - fromSearch: true - ) - .then((value) { + provider.favoriteUser(userID: AppState().chatDetails!.response!.id!, targetUserID: provider.chatUsersList![index].id!, fromSearch: true).then((value) { setState(() {}); }); } else if (provider.chatUsersList![index].isFav == true) { @@ -326,13 +317,7 @@ class _SearchEmployeeBottomSheetState extends State { setState(() {}); }); } else { - provider - .favoriteUser( - userID: AppState().chatDetails!.response!.id!, - targetUserID: provider.chatUsersList![index].id!, - fromSearch: true - ) - .then((value) { + provider.favoriteUser(userID: AppState().chatDetails!.response!.id!, targetUserID: provider.chatUsersList![index].id!, fromSearch: true).then((value) { setState(() {}); }); } diff --git a/lib/widgets/button/default_button.dart b/lib/widgets/button/default_button.dart index 71de642..338a13d 100644 --- a/lib/widgets/button/default_button.dart +++ b/lib/widgets/button/default_button.dart @@ -5,8 +5,7 @@ import 'package:mohem_flutter_app/classes/colors.dart'; extension WithContainer on Widget { Widget get insideContainer => Container( color: Colors.white, - padding: - const EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), + padding: const EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), child: this, ); } @@ -76,8 +75,7 @@ class DefaultButton extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ if (iconData != null) Icon(iconData, color: textColor), - if (svgIcon != null) - SvgPicture.asset(svgIcon ?? "", color: textColor), + if (svgIcon != null) SvgPicture.asset(svgIcon ?? "", color: textColor), if (!isTextExpanded) Padding( padding: EdgeInsets.only( diff --git a/lib/widgets/chat_app_bar_widge.dart b/lib/widgets/chat_app_bar_widge.dart index 25ed34c..cc252ec 100644 --- a/lib/widgets/chat_app_bar_widge.dart +++ b/lib/widgets/chat_app_bar_widge.dart @@ -7,7 +7,6 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; -import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:provider/provider.dart'; AppBar ChatAppBarWidget(BuildContext context, diff --git a/lib/widgets/dialogs/confirm_dialog.dart b/lib/widgets/dialogs/confirm_dialog.dart index 4c94340..560bd26 100644 --- a/lib/widgets/dialogs/confirm_dialog.dart +++ b/lib/widgets/dialogs/confirm_dialog.dart @@ -12,8 +12,9 @@ class ConfirmDialog extends StatelessWidget { final String message; final String? okTitle; final VoidCallback? onTap; + final VoidCallback? onCloseTap; - const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap}) : super(key: key); + const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); @override Widget build(BuildContext context) { @@ -41,9 +42,8 @@ class ConfirmDialog extends StatelessWidget { icon: const Icon(Icons.close), color: MyColors.darkTextColor, constraints: const BoxConstraints(), - onPressed: () { - Navigator.pop(context); - }, + onPressed: () => onCloseTap ?? Navigator.pop(context), + // onPressed: () => Navigator.pop(context), ) ], ), diff --git a/lib/widgets/dialogs/dialogs.dart b/lib/widgets/dialogs/dialogs.dart index debb147..6765e38 100644 --- a/lib/widgets/dialogs/dialogs.dart +++ b/lib/widgets/dialogs/dialogs.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -void showMDialog(context, {Widget? child,Color? backgroundColor,bool isDismissable=true}) async { +void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true}) async { return showDialog( context: context, barrierDismissible: isDismissable, diff --git a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart index ec4146b..cc710fe 100644 --- a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart +++ b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart @@ -68,7 +68,9 @@ class DynamicTextFieldWidget extends StatelessWidget { enabled: isEnable, scrollPadding: EdgeInsets.zero, readOnly: isReadOnly, - keyboardType: (isInputTypeNum) ? (isInputTypeNumSigned ? const TextInputType.numberWithOptions(signed: true, decimal: true) : TextInputType.numberWithOptions(signed: true, decimal: true)) : TextInputType.text, + keyboardType: (isInputTypeNum) + ? (isInputTypeNumSigned ? const TextInputType.numberWithOptions(signed: true, decimal: true) : TextInputType.numberWithOptions(signed: true, decimal: true)) + : TextInputType.text, textInputAction: TextInputAction.done, //controller: controller, maxLines: lines, diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 834695b..ef04f84 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -45,7 +45,19 @@ class ImageOptions { onFilesTap: () async { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, - allowedExtensions: ['jpg', 'jpeg ', 'pdf', 'txt', 'docx', 'doc', 'pptx', 'xlsx', 'png', 'rar', 'zip',], + allowedExtensions: [ + 'jpg', + 'jpeg ', + 'pdf', + 'txt', + 'docx', + 'doc', + 'pptx', + 'xlsx', + 'png', + 'rar', + 'zip', + ], ); List files = result!.paths.map((path) => File(path!)).toList(); image(result.files.first.path.toString(), files.first); @@ -54,67 +66,68 @@ class ImageOptions { ); } - static void showImageOptions(BuildContext context, Function(String, File) image) { - showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - builder: (BuildContext bc) { - return _BottomSheet( - children: [ - _BottomSheetItem( - title: "Select File Source", - onTap: () {}, - icon: Icons.file_present, - color: MyColors.black, - ), - _BottomSheetItem( - title: "Gallery", - icon: Icons.image, - onTap: () async { - if (Platform.isAndroid) { - galleryImageAndroid(image); - } else { - File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 10))?.path ?? ""); - String fileName = _image.path; - var bytes = File(fileName).readAsBytesSync(); - String base64Encode = base64.encode(bytes); - if (base64Encode != null) { - image(base64Encode, _image); - } - } - }, - ), - _BottomSheetItem( - title: "Camera", - icon: Icons.camera_alt, - onTap: () async { - if (Platform.isAndroid) { - cameraImageAndroid(image); - } else { - File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 10))?.path ?? ""); - String fileName = _image.path; - var bytes = File(fileName).readAsBytesSync(); - String base64Encode = base64.encode(bytes); - if (base64Encode != null) { - image(base64Encode, _image); - } - } - }, - ), - _BottomSheetItem( - title: "Cancel", - onTap: () {}, - icon: Icons.cancel, - color: MyColors.redColor, - ) - ], - ); - }); - } +// static void showImageOptions(BuildContext context, Function(String, File) image) { +// showModalBottomSheet( +// backgroundColor: Colors.transparent, +// context: context, +// builder: (BuildContext bc) { +// return _BottomSheet( +// children: [ +// _BottomSheetItem( +// title: "Select File Source", +// onTap: () {}, +// icon: Icons.file_present, +// color: MyColors.black, +// ), +// _BottomSheetItem( +// title: "Gallery", +// icon: Icons.image, +// onTap: () async { +// if (Platform.isAndroid) { +// galleryImageAndroid(image); +// } else { +// File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 10))?.path ?? ""); +// String fileName = _image.path; +// var bytes = File(fileName).readAsBytesSync(); +// String base64Encode = base64.encode(bytes); +// if (base64Encode != null) { +// image(base64Encode, _image); +// } +// } +// }, +// ), +// _BottomSheetItem( +// title: "Camera", +// icon: Icons.camera_alt, +// onTap: () async { +// if (Platform.isAndroid) { +// cameraImageAndroid(image); +// } else { +// File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 10))?.path ?? ""); +// String fileName = _image.path; +// var bytes = File(fileName).readAsBytesSync(); +// String base64Encode = base64.encode(bytes); +// if (base64Encode != null) { +// image(base64Encode, _image); +// } +// } +// }, +// ), +// _BottomSheetItem( +// title: "Cancel", +// onTap: () {}, +// icon: Icons.cancel, +// color: MyColors.redColor, +// ) +// ], +// ); +// }); +// } } void galleryImageAndroid(Function(String, File) image) async { File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 20))?.path ?? ""); + String fileName = _image.path; var bytes = File(fileName).readAsBytesSync(); String base64Encode = base64.encode(bytes); diff --git a/lib/widgets/location/Location.dart b/lib/widgets/location/Location.dart index d407c1c..9329cb4 100644 --- a/lib/widgets/location/Location.dart +++ b/lib/widgets/location/Location.dart @@ -1,22 +1,11 @@ import 'dart:async'; -import 'dart:math'; -import 'dart:ui'; -import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:geolocator/geolocator.dart'; -import 'package:google_directions_api/google_directions_api.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:mohem_flutter_app/classes/app_permissions.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; -import 'package:mohem_flutter_app/theme/colors.dart'; -// import 'package:geodesy/geodesy.dart' as geodesy; -//Created By Mr.Zohaib class Location { - static _Map map = _Map(); - static void havePermission(Function(bool) callback) { Geolocator.checkPermission().then((value) async { if (value == LocationPermission.denied) { @@ -46,12 +35,11 @@ class Location { }); } - static void getCurrentLocation(Function(LatLng?, bool isMocked) callback, BuildContext context) { + static void getCurrentLocation(Function(Position position, bool isMocked) callback, BuildContext context) { void done(Position position) { //AppStorage.sp.saveLocation(position); bool isMocked = position.isMocked; - LatLng? myCurrentLocation = LatLng(position.latitude, position.longitude); - callback(myCurrentLocation, isMocked); + callback(position, isMocked); } AppPermissions.location((granted) { @@ -70,182 +58,4 @@ class Location { } }, context); } - - // static LatLng locationAwayFrom( - // {required LatLng loc1, num distanceMeters = 200.0, num bearing = 270.0}) { - // geodesy.LatLng l1 = geodesy.LatLng(loc1.latitude, loc1.longitude); - // geodesy.LatLng destinationPoint = geodesy.Geodesy() - // .destinationPointByDistanceAndBearing(l1, distanceMeters, bearing); - // return LatLng(destinationPoint.latitude, destinationPoint.longitude); - // } - - static Future distanceTo(LatLng destination) async { - var myLoc = await Geolocator.getLastKnownPosition(); - var distance = 0.0; - if (myLoc != null) { - distance = Geolocator.distanceBetween(destination.latitude, destination.longitude, myLoc.latitude, myLoc.longitude); - } - return distance; - } -} - -class _Map { - Marker createMarker( - String id, { - required LatLng coordinates, - BitmapDescriptor? icon, - VoidCallback? onTap, - }) { - MarkerId markerId = MarkerId(id); - return Marker( - icon: icon ?? BitmapDescriptor.defaultMarker, - markerId: markerId, - position: coordinates, - flat: false, - // infoWindow: InfoWindow(title: id, snippet: '*'), - onTap: onTap, - ); - } - - CameraPosition initialCamera({required Completer mapController, LatLng? position, double zoom = 12}) { - position = position ?? LatLng(24.7249303, 46.5416656); - CameraPosition riyadhEye = CameraPosition( - target: position, - zoom: zoom, - ); - mapController.future.then((controller) { - controller.animateCamera(CameraUpdate.newCameraPosition(riyadhEye)); - }); - return riyadhEye; - } - - CameraPosition moveTo(LatLng location, {double zoom = 12, double direction = 0.0, required Completer mapController, bool? animation}) { - var camera = CameraPosition(target: location, zoom: zoom, bearing: direction); - mapController.future.then((controller) { - animation ?? false ? controller.animateCamera(CameraUpdate.newCameraPosition(camera)) : controller.moveCamera(CameraUpdate.newCameraPosition(camera)); - }); - return camera; - } - - void moveCamera(CameraPosition camera, @required Completer mapController, bool animation) { - mapController.future.then((controller) { - animation ? controller.animateCamera(CameraUpdate.newCameraPosition(camera)) : controller.moveCamera(CameraUpdate.newCameraPosition(camera)); - }); - } - - void scrollBy({double x = 0, double y = 0, required Completer mapController, bool animation = true}) { - var camera = CameraUpdate.scrollBy(x, y); - mapController.future.then((controller) { - animation ? controller.animateCamera(camera) : controller.moveCamera(camera); - }); - } - - // void goToCurrentLocation({Completer? mapController, double? direction = 0.0, bool? animation}) { - // Location.getCurrentLocation((location) { - // moveTo(location!, zoom: 17, mapController: mapController!, animation: animation, direction: direction!); - // }); - // } - - var routes = Map(); - - void setRoutePolylines(LatLng? source, LatLng? destination, Set polylines, Completer mapController, Function(DirectionsRoute?) completion) { - if (source == null || destination == null) { - completion(null); - return; - } - - var origin = '${source.latitude},${source.longitude}'; - var destin = '${destination.latitude},${destination.longitude}'; - var routeId = '$origin->$destination'; - - void createPolyline(DirectionsRoute results) { - List polylineCoordinates = results.overviewPath!.map((e) => LatLng(e.latitude, e.longitude)).toList(); - PolylineId id = PolylineId("route"); - Polyline polyline = Polyline( - polylineId: id, - color: accentColor, - width: 5, - jointType: JointType.round, - startCap: Cap.roundCap, - endCap: Cap.roundCap, - points: polylineCoordinates, - ); - - polylines.removeWhere((element) => true); - polylines.add(polyline); - - LatLngBounds bound = getBounds(coordinates: polylineCoordinates); - focusCameraToLatLngBounds(bound: bound, mapController: mapController, padding: 100); - completion(routes[routeId]); - } - - var availableRoute = routes[routeId]; - if (availableRoute == null) { - var request = DirectionsRequest(origin: origin, destination: destin); - DirectionsService().route(request, (response, status) { - if (status == DirectionsStatus.ok && response.routes!.isNotEmpty) { - routes[routeId] = response.routes!.first; - createPolyline(response.routes!.first); - } - }); - } else { - createPolyline(availableRoute); - } - } - - LatLngBounds getBounds({required List coordinates}) { - var lngs = coordinates.map((c) => c.longitude).toList(); - var lats = coordinates.map((c) => c.latitude).toList(); - - double bottomMost = lngs.reduce(min); - double topMost = lngs.reduce(max); - double leftMost = lats.reduce(min); - double rightMost = lats.reduce(max); - - LatLngBounds bounds = LatLngBounds( - northeast: LatLng(rightMost, topMost), - southwest: LatLng(leftMost, bottomMost), - ); - return bounds; - - double? x0, x1, y0, y1; - for (LatLng latLng in coordinates) { - if (x0 == null) { - x0 = x1 = latLng.latitude; - y0 = y1 = latLng.longitude; - } else { - if (latLng.latitude > x1!) x1 = latLng.latitude; - if (latLng.latitude < x0) x0 = latLng.latitude; - if (latLng.longitude > y1!) y1 = latLng.longitude; - if (latLng.longitude < y0!) y0 = latLng.longitude; - } - } - return LatLngBounds(northeast: LatLng(x1!, y1!), southwest: LatLng(x0!, y0!)); - } - - void focusCameraToLatLngBounds({LatLngBounds? bound, Completer? mapController, double? padding}) async { - if (bound == null) return; - - CameraUpdate camera = CameraUpdate.newLatLngBounds(bound, padding!); - GoogleMapController controller = await mapController!.future; - controller.animateCamera(camera); - } - - void focusCameraTo2Points({LatLng? point1, LatLng? point2, Completer? mapController, double? padding}) async { - var source = point1; - var destination = point2; - if (source != null && destination != null) { - // 'package:google_maps_flutter_platform_interface/src/types/location.dart': Failed assertion: line 72 pos 16: 'southwest.latitude <= northeast.latitude': is not true. - LatLngBounds bound; - if (source.latitude <= destination.latitude) { - bound = LatLngBounds(southwest: source, northeast: destination); - } else { - bound = LatLngBounds(southwest: destination, northeast: source); - } - - if (bound == null) return; - - focusCameraToLatLngBounds(bound: bound, mapController: mapController, padding: padding); - } - } } diff --git a/lib/widgets/mark_attendance_widget.dart b/lib/widgets/mark_attendance_widget.dart index 5cfc347..2ffa961 100644 --- a/lib/widgets/mark_attendance_widget.dart +++ b/lib/widgets/mark_attendance_widget.dart @@ -3,7 +3,11 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:huawei_location/location/fused_location_provider_client.dart'; +import 'package:huawei_location/location/location_request.dart'; +import 'package:huawei_location/location/location_settings_request.dart'; +import 'package:huawei_location/permission/permission_handler.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -21,6 +25,7 @@ import 'package:mohem_flutter_app/widgets/location/Location.dart'; import 'package:mohem_flutter_app/widgets/nfc/nfc_reader_sheet.dart'; import 'package:mohem_flutter_app/widgets/qr_scanner_dialog.dart'; import 'package:nfc_manager/nfc_manager.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:wifi_iot/wifi_iot.dart'; class MarkAttendanceWidget extends StatefulWidget { @@ -39,6 +44,8 @@ class MarkAttendanceWidget extends StatefulWidget { class _MarkAttendanceWidgetState extends State { bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false; + int _locationUpdateCbId = 0; + @override void initState() { super.initState(); @@ -66,6 +73,36 @@ class _MarkAttendanceWidgetState extends State { }); } + void checkHuaweiLocationPermission(String attendanceType) async { + PermissionHandler permissionHandler = PermissionHandler(); + + if (await permissionHandler.hasLocationPermission()) { + getHuaweiCurrentLocation(attendanceType); + } else { + bool has = await requestPermissions(); + if (has) { + getHuaweiCurrentLocation(attendanceType); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give location permission to mark attendance", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } + } + } + + Future requestPermissions() async { + var result = await [ + Permission.location, + ].request(); + return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); + } + @override void dispose() { super.dispose(); @@ -75,7 +112,6 @@ class _MarkAttendanceWidgetState extends State { @override Widget build(BuildContext context) { - return Container( padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding), decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), @@ -96,13 +132,17 @@ class _MarkAttendanceWidgetState extends State { // if (isNfcEnabled) attendanceMethod("NFC", "assets/images/nfc.svg", isNfcEnabled, () { if (isNfcLocationEnabled) { - Location.getCurrentLocation((LatLng? latlng, bool isMocked) { - if (isMocked) { - markFakeAttendance("NFC", latlng?.latitude.toString() ?? "", latlng?.longitude.toString() ?? ""); - } else { - performNfcAttendance(widget.model, lat: latlng?.latitude.toString() ?? "", lng: latlng?.longitude.toString() ?? ""); - } - }, context); + if (AppState().getIsHuawei) { + checkHuaweiLocationPermission("NFC"); + } else { + Location.getCurrentLocation((Position position, bool isMocked) { + if (isMocked) { + markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, context); + } } else { performNfcAttendance(widget.model); } @@ -110,28 +150,35 @@ class _MarkAttendanceWidgetState extends State { if (isWifiEnabled) attendanceMethod("Wifi", "assets/images/wufu.svg", isWifiEnabled, () { if (isWifiLocationEnabled) { - Location.getCurrentLocation((LatLng? latlng, bool isMocked) { - if (isMocked) { - markFakeAttendance("WIFI", latlng?.latitude.toString() ?? "", latlng?.longitude.toString() ?? ""); - } else { - performWifiAttendance(widget.model, lat: latlng?.latitude.toString() ?? "", lng: latlng?.longitude.toString() ?? ""); - } - }, context); + if (AppState().getIsHuawei) { + checkHuaweiLocationPermission("WIFI"); + } else { + Location.getCurrentLocation((Position position, bool isMocked) { + if (isMocked) { + markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, context); + } } else { performWifiAttendance(widget.model); } - // connectWifi(); }), if (isQrEnabled) attendanceMethod("QR", "assets/images/ic_qr.svg", isQrEnabled, () async { if (isQrLocationEnabled) { - Location.getCurrentLocation((LatLng? latlng, bool isMocked) { - if (isMocked) { - markFakeAttendance("QR", latlng?.latitude.toString() ?? "", latlng?.longitude.toString() ?? ""); - } else { - performQrCodeAttendance(widget.model, lat: latlng?.latitude.toString() ?? "", lng: latlng?.longitude.toString() ?? ""); - } - }, context); + if (AppState().getIsHuawei) { + checkHuaweiLocationPermission("QR"); + } else { + Location.getCurrentLocation((Position position, bool isMocked) { + if (isMocked) { + markFakeAttendance("QR", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + performQrCodeAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, context); + } } else { performQrCodeAttendance(widget.model); } @@ -144,9 +191,58 @@ class _MarkAttendanceWidgetState extends State { ); } + void getHuaweiCurrentLocation(String attendanceType) { + try { + FusedLocationProviderClient locationService = FusedLocationProviderClient(); + LocationRequest locationRequest = LocationRequest(); + locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; + locationRequest.interval = 1000; + List locationRequestList = [locationRequest]; + LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); + + locationService.checkLocationSettings(locationSettingsRequest).then((settings) async { + await locationService.getLastLocation().then((value) { + if (value.latitude == null || value.longitude == null) { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "Unable to get your location, Please check your location settings & try again.", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } else { + if (attendanceType == "QR") { + performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + } + if (attendanceType == "WIFI") { + performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + } + if (attendanceType == "NFC") { + performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + } + } + }).catchError((error) { + print("HUAWEI LOCATION getLastLocation ERROR!!!!!"); + print(error); + }); + }).catchError((error) { + print("HUAWEI LOCATION checkLocationSettings ERROR!!!!!"); + print(error); + if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") { + // Location service not enabled. + } + }); + } catch(error) { + print("HUAWEI LOCATION ERROR!!!!!"); + print(error); + } + + } + Future performNfcAttendance(DashboardProviderModel model, {String lat = "0", String lng = "0"}) async { if (Platform.isIOS) { - Utils.readNFc(onRead: (String nfcId) async { Utils.showLoading(context); try { @@ -215,7 +311,25 @@ class _MarkAttendanceWidgetState extends State { } } + Future checkSession() async { + try { + Utils.showLoading(context); + await DashboardApiClient().getOpenMissingSwipes(); + Utils.hideLoading(context); + return true; + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + return false; + } + } + Future performWifiAttendance(DashboardProviderModel model, {String lat = "0", String lng = "0"}) async { + if (Platform.isAndroid) { + if (!(await checkSession())) { + return; + } + } Utils.showLoading(context); bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "", password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); @@ -228,7 +342,7 @@ class _MarkAttendanceWidgetState extends State { } } - if (isConnected) { + if (isConnected && AppState().isAuthenticated) { await WiFiForIoTPlugin.forceWifiUsage(true); await Future.delayed(const Duration(seconds: 6)); try { @@ -236,20 +350,38 @@ class _MarkAttendanceWidgetState extends State { bool status = await model.fetchAttendanceTracking(context); Utils.hideLoading(context); await closeWifiRequest(); - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: false, - child: SuccessDialog(widget.isFromDashboard), - ); + if (g?.messageStatus == 2) { + showDialog( + barrierDismissible: true, + context: context, + builder: (cxt) => ConfirmDialog( + message: g?.errorEndUserMessage ?? "", + onTap: () { + Navigator.pop(context); + }, + onCloseTap: () {}, + ), + ); + } else { + showMDialog( + context, + backgroundColor: Colors.transparent, + isDismissable: false, + child: SuccessDialog(widget.isFromDashboard), + ); + } } catch (ex) { await closeWifiRequest(); Utils.hideLoading(context); Utils.handleException(ex, context, null); } } else { - Utils.hideLoading(context); - Utils.confirmDialog(context, LocaleKeys.comeNearHMGWifi.tr()); + if (AppState().isAuthenticated) { + Utils.hideLoading(context); + Utils.confirmDialog(context, LocaleKeys.comeNearHMGWifi.tr()); + } else { + await closeWifiRequest(); + } } } @@ -272,12 +404,26 @@ class _MarkAttendanceWidgetState extends State { GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 1, isGpsRequired: isQrLocationEnabled, lat: lat, long: lng, QRValue: qrCodeValue); bool status = await model.fetchAttendanceTracking(context); Utils.hideLoading(context); - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: true, - child: SuccessDialog(widget.isFromDashboard), - ); + if (g?.messageStatus == 2) { + showDialog( + barrierDismissible: true, + context: context, + builder: (cxt) => ConfirmDialog( + message: g?.errorEndUserMessage ?? "", + onTap: () { + Navigator.pop(context); + }, + onCloseTap: () {}, + ), + ); + } else { + showMDialog( + context, + backgroundColor: Colors.transparent, + isDismissable: true, + child: SuccessDialog(widget.isFromDashboard), + ); + } } catch (ex) { print(ex); Utils.hideLoading(context); diff --git a/lib/widgets/otp_widget.dart b/lib/widgets/otp_widget.dart index e90308d..7994b1e 100644 --- a/lib/widgets/otp_widget.dart +++ b/lib/widgets/otp_widget.dart @@ -108,7 +108,7 @@ class OTPWidgetState extends State with SingleTickerProviderStateMixi } } - void calculateStrList() { + void calculateStrList() { if (strList.length > widget.maxLength) { strList.length = widget.maxLength; } diff --git a/lib/widgets/qr_scanner_dialog.dart b/lib/widgets/qr_scanner_dialog.dart index 6082d4c..11e2601 100644 --- a/lib/widgets/qr_scanner_dialog.dart +++ b/lib/widgets/qr_scanner_dialog.dart @@ -1,9 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; - import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; class QrScannerDialog extends StatefulWidget { @override @@ -68,6 +67,8 @@ class _QrScannerDialogState extends State { } }); }); + controller.pauseCamera(); + controller.resumeCamera(); } @override diff --git a/pubspec.yaml b/pubspec.yaml index 367db22..cd1ef74 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 3.2.0+300020 +version: 3.2.93+300032 environment: sdk: ">=2.16.0 <3.0.0" @@ -46,7 +46,7 @@ dependencies: local_auth: ^1.1.9 fluttertoast: ^8.0.8 syncfusion_flutter_calendar: ^19.4.48 - flutter_calendar_carousel: ^2.1.0 +# flutter_calendar_carousel: ^2.1.0 pie_chart: ^5.1.0 shared_preferences: ^2.0.12 firebase_messaging: ^13.0.4 @@ -54,24 +54,21 @@ dependencies: logger: ^1.1.0 flutter_countdown_timer: ^4.1.0 nfc_manager: ^3.2.0 - uuid: ^3.0.6 +# uuid: ^3.0.6 +# device_info_plus: ^4.0.0 +# android_id: ^0.1.3+1 + platform_device_id: ^1.0.1 image_picker: ^0.8.5+3 file_picker: ^4.6.1 - # maps - google_maps_flutter: ^2.0.2 - google_maps_utils: ^1.4.0+1 - google_directions_api: ^0.9.0 geolocator: ^9.0.2 - # flutter_compass: ^0.6.1 - google_maps_flutter_web: ^0.3.2 month_year_picker: ^0.2.0+1 month_picker_dialog_2: 0.5.5 open_file: ^3.2.1 wifi_iot: ^0.3.18 flutter_html: ^3.0.0-alpha.6 # flutter_barcode_scanner: ^2.0.0 - qr_code_scanner: ^1.0.0 - qr_flutter: ^4.0.0 + qr_code_scanner: ^1.0.1 +# qr_flutter: ^4.0.0 url_launcher: ^6.0.15 share: 2.0.4 flutter_rating_bar: ^4.0.1 @@ -91,7 +88,7 @@ dependencies: logging: ^1.0.1 swipe_to: ^1.0.2 flutter_webrtc: ^0.9.16 - camera: ^0.10.0+4 + camera: ^0.10.3 flutter_local_notifications: any #firebase_analytics: any @@ -102,11 +99,24 @@ dependencies: #Encryption flutter_des: ^2.1.0 - video_player: ^2.4.7 + video_player: ^2.5.1 just_audio: ^0.9.30 - safe_device: ^1.1.2 +# safe_device: ^1.1.2 flutter_layout_grid: ^2.0.1 + #Huawei Dependencies +# huawei_hmsavailability: ^6.6.0+300 + huawei_location: 6.0.0+302 + huawei_push: ^6.7.0+300 + firebase_crashlytics: ^2.9.0 + + #Items for sale Image Carousel Slider + carousel_slider: ^4.2.1 + + #Huawei Specified +# store_checker: ^1.1.0 + google_api_availability: ^3.0.1 + dependency_overrides: firebase_core_platform_interface: 4.5.1