diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index dd9a8de..30555e1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ NSPhotoLibraryUsageDescription This app requires photo library access to select image as document & upload it. NSMicrophoneUsageDescription - This app requires microphone access to for call. + This app requires microphone access to for call. UIBackgroundModes remote-notification @@ -69,6 +69,10 @@ UIViewControllerBasedStatusBarAppearance + 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 bea5b7c..3ef8fba 100644 --- a/lib/api/chat/chat_api_client.dart +++ b/lib/api/chat/chat_api_client.dart @@ -2,11 +2,11 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; -import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/exceptions/api_exception.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; @@ -29,6 +29,9 @@ class ChatApiClient { "password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG", }, ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } user.UserAutoLoginModel userLoginResponse = user.userAutoLoginModelFromJson(response.body); return userLoginResponse; } @@ -38,33 +41,36 @@ class ChatApiClient { "${ApiConsts.chatLoginTokenUrl}getUserWithStatusAndFavAsync/$sName/$cUserId", token: AppState().chatDetails!.response!.token, ); - return searchUserJsonModel(response.body); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } + return List.from(json.decode(response.body).map((x) => ChatUser.fromJson(x))); } - List searchUserJsonModel(String str) => List.from(json.decode(str).map((x) => ChatUser.fromJson(x))); - Future getRecentChats() async { try { Response response = await ApiClient().getJsonForResponse( "${ApiConsts.chatRecentUrl}getchathistorybyuserid", token: AppState().chatDetails!.response!.token, ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } return ChatUserModel.fromJson( json.decode(response.body), ); } catch (e) { - e as APIException; - if (e.message == "api_common_unauthorized") { - user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); - if (userLoginResponse.response != null) { - AppState().setchatUserDetails = userLoginResponse; - getRecentChats(); - } else { - Utils.showToast( - userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", - ); - } - } + // if (e.message == "api_common_unauthorized") { + // user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); + // if (userLoginResponse.response != null) { + // AppState().setchatUserDetails = userLoginResponse; + // getRecentChats(); + // } else { + // Utils.showToast( + // userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", + // ); + // } + // } throw e; } } @@ -74,9 +80,10 @@ class ChatApiClient { "${ApiConsts.chatFavUser}getFavUserById/${AppState().chatDetails!.response!.id}", token: AppState().chatDetails!.response!.token, ); - return ChatUserModel.fromJson( - json.decode(favRes.body), - ); + if (!kReleaseMode) { + logger.i("res: " + favRes.body); + } + return ChatUserModel.fromJson(json.decode(favRes.body)); } Future getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false, required int paginationVal}) async { @@ -85,24 +92,30 @@ class ChatApiClient { "${ApiConsts.chatSingleUserHistoryUrl}GetUserChatHistory/$senderUID/$receiverUID/$paginationVal", token: AppState().chatDetails!.response!.token, ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } return response; } catch (e) { - e as APIException; - if (e.message == "api_common_unauthorized") { - user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); - if (userLoginResponse.response != null) { - AppState().setchatUserDetails = userLoginResponse; - getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal); - } else { - Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr"); - } - } + // e as APIException; + // if (e.message == "api_common_unauthorized") { + // user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); + // if (userLoginResponse.response != null) { + // AppState().setchatUserDetails = userLoginResponse; + // getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal); + // } else { + // Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr"); + // } + // } throw e; } } Future favUser({required int userID, required int targetUserID}) async { Response response = await ApiClient().postJsonForResponse("${ApiConsts.chatFavUser}addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); return favoriteChatUser; } @@ -114,20 +127,23 @@ class ChatApiClient { {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token, ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } + fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); return favoriteChatUser; } catch (e) { e as APIException; - if (e.message == "api_common_unauthorized") { - logger.d("Token Generated On APIIIIII"); - user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); - if (userLoginResponse.response != null) { - AppState().setchatUserDetails = userLoginResponse; - unFavUser(userID: userID, targetUserID: targetUserID); - } else { - Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr"); - } - } + // if (e.message == "api_common_unauthorized") { + // user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); + // if (userLoginResponse.response != null) { + // AppState().setchatUserDetails = userLoginResponse; + // unFavUser(userID: userID, targetUserID: targetUserID); + // } else { + // Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr"); + // } + // } throw e; } } @@ -138,6 +154,7 @@ class ChatApiClient { request.files.add(await MultipartFile.fromPath('files', file.path)); request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'}); StreamedResponse response = await request.send(); + if (!kReleaseMode) {} return response; } @@ -159,6 +176,9 @@ class ChatApiClient { {"encryptedEmails": encryptedEmails, "fromClient": false}, token: AppState().chatDetails!.response!.token, ); + if (!kReleaseMode) { + logger.i("res: " + response.body); + } return chatUserImageModelFromJson(response.body); } } diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index e30af91..ace0881 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -65,10 +65,13 @@ class DashboardApiClient { }, url, postParams); } - Future> getAccrualBalances(String effectiveDate) async { + Future> getAccrualBalances(String effectiveDate, {String? empID}) async { String url = "${ApiConsts.erpRest}GET_ACCRUAL_BALANCES"; Map postParams = {"P_EFFECTIVE_DATE": effectiveDate}; postParams.addAll(AppState().postParamsJson); + if (empID != null && empID.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); return responseData.getAccrualBalancesList ?? []; @@ -180,12 +183,7 @@ class DashboardApiClient { }, url, postParams); } - Future getChatCount() async { - Response response = await ApiClient().getJsonForResponse( - "${ApiConsts.chatLoginTokenUrl}unreadconversationcount/${AppState().getUserName}", - ); - return chatUnreadCovnCountModelFromJson(response.body); - } + // Future setAdvertisementViewed(String masterID, int advertisementId) async { // String url = "${ApiConsts.cocRest}Mohemm_ITG_UpdateAdvertisementAsViewed"; diff --git a/lib/api/leave_balance_api_client.dart b/lib/api/leave_balance_api_client.dart index cd577ba..f9c5e21 100644 --- a/lib/api/leave_balance_api_client.dart +++ b/lib/api/leave_balance_api_client.dart @@ -17,27 +17,33 @@ class LeaveBalanceApiClient { factory LeaveBalanceApiClient() => _instance; - Future> getAbsenceTransactions(int pSelectedResopID) async { + Future> getAbsenceTransactions(int pSelectedResopID, {String? empID}) async { String url = "${ApiConsts.erpRest}GET_ABSENCE_TRANSACTIONS"; Map postParams = {"P_PAGE_LIMIT": 50, "P_PAGE_NUM": 1, "P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": pSelectedResopID}; postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.getAbsenceTransactionList ?? []; }, url, postParams); } - Future> getAbsenceAttendanceTypes() async { + Future> getAbsenceAttendanceTypes({String? empID}) async { String url = "${ApiConsts.erpRest}GET_ABSENCE_ATTENDANCE_TYPES"; Map postParams = {}; postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.getAbsenceAttendanceTypesList ?? []; }, url, postParams); } - Future calculateAbsenceDuration(int pAbsenceAttendanceTypeID, String pDateStart, String pDateEnd, int pSelectedResopID) async { + Future calculateAbsenceDuration(int pAbsenceAttendanceTypeID, String pDateStart, String pDateEnd, int pSelectedResopID, {String? empID}) async { String url = "${ApiConsts.erpRest}CALCULATE_ABSENCE_DURATION"; Map postParams = { "P_ABSENCE_ATTENDANCE_TYPE_ID": pAbsenceAttendanceTypeID, @@ -49,16 +55,22 @@ class LeaveBalanceApiClient { "P_TIME_START": null, }; postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.calculateAbsenceDuration!; }, url, postParams); } - Future> getAbsenceDffStructure(String pDescFlexContextCode, String pFunctionName, int pSelectedResopID) async { + Future> getAbsenceDffStructure(String pDescFlexContextCode, String pFunctionName, int pSelectedResopID, {String? empID}) async { String url = "${ApiConsts.erpRest}GET_ABSENCE_DFF_STRUCTURE"; Map postParams = {"P_DESC_FLEX_CONTEXT_CODE": pDescFlexContextCode, "P_FUNCTION_NAME": pFunctionName, "P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": pSelectedResopID}; postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.getAbsenceDffStructureList ?? []; @@ -67,7 +79,7 @@ class LeaveBalanceApiClient { Future validateAbsenceTransaction( String pDescFlexContextCode, String pFunctionName, int pAbsenceAttendanceTypeID, String pReplacementUserName, String pDateStart, String pDateEnd, int pSelectedResopID, Map data, - {String comments = ""}) async { + {String comments = "", String? empID}) async { String url = "${ApiConsts.erpRest}VALIDATE_ABSENCE_TRANSACTION"; Map postParams = { "P_DESC_FLEX_CONTEXT_CODE": pDescFlexContextCode, @@ -86,6 +98,9 @@ class LeaveBalanceApiClient { }; postParams.addAll(data); postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData; @@ -94,7 +109,7 @@ class LeaveBalanceApiClient { Future submitAbsenceTransaction( String pDescFlexContextCode, String pFunctionName, int pAbsenceAttendanceTypeID, String pReplacementUserName, String pDateStart, String pDateEnd, int pSelectedResopID, Map data, - {String comments = ""}) async { + {String comments = "", String? empID}) async { String url = "${ApiConsts.erpRest}SUBMIT_ABSENCE_TRANSACTION"; Map postParams = { "P_DESC_FLEX_CONTEXT_CODE": pDescFlexContextCode, @@ -113,6 +128,9 @@ class LeaveBalanceApiClient { }; postParams.addAll(data); postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.sumbitAbsenceTransactionList!; @@ -129,7 +147,7 @@ class LeaveBalanceApiClient { }, url, postParams); } - Future startAbsenceApprovalProcess(int pTransactionID, String comments, int pSelectedResopID) async { + Future startAbsenceApprovalProcess(int pTransactionID, String comments, int pSelectedResopID,{String? empID}) async { String url = "${ApiConsts.erpRest}START_ABSENCE_APPROVAL_PROCESS"; Map postParams = { "P_TRANSACTION_ID": pTransactionID, @@ -138,6 +156,9 @@ class LeaveBalanceApiClient { "P_MENU_TYPE": "E", }; postParams.addAll(AppState().postParamsJson); + if (empID!.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.startAbsenceApprovalProccess!; diff --git a/lib/api/marathon/marathon_api_client.dart b/lib/api/marathon/marathon_api_client.dart index 132ec30..a9266d2 100644 --- a/lib/api/marathon/marathon_api_client.dart +++ b/lib/api/marathon/marathon_api_client.dart @@ -8,6 +8,7 @@ import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; +import 'package:mohem_flutter_app/models/marathon/winner_model.dart'; import 'package:signalr_netcore/hub_connection.dart'; class MarathonApiClient { @@ -19,6 +20,7 @@ class MarathonApiClient { String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? ""; Map jsonObject = {"userName": employeeUserName, "password": employeeSession}; + Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonParticipantLoginUrl, jsonObject); var json = jsonDecode(response.body); @@ -80,10 +82,11 @@ class MarathonApiClient { return marathonDetailModel; } - Future joinMarathonAsParticipant() async { + Future joinMarathonAsParticipant() async { Map jsonObject = { "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "", - "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "", + "employeeNameAr": AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr ?? "", + "employeeNameEn": AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn ?? "", "marathonId": AppState().getMarathonProjectId!, }; @@ -95,31 +98,33 @@ class MarathonApiClient { if (marathonModel.statusCode == 208) { // means participant is already in the marathon i.e already joined - return true; + return marathonModel.data["remainingTime"]; } if (marathonModel.statusCode == 200) { if (marathonModel.data != null && marathonModel.isSuccessful == true) { logger.i("joinMarathonAsParticipant: ${marathonModel.data}"); - return true; + return marathonModel.data["remainingTime"]; } else { - return false; + return null; } } else { - return false; + return null; } } Future getNextQuestion({required String? questionId, required String marathonId}) async { Map jsonObject = { - "questionId": questionId, + "previousQuestionId": questionId, "marathonId": marathonId, - }; + }; Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonNextQuestionUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken()); var json = jsonDecode(response.body); + + logger.i("json in NextQuestion: $json"); var data = json["data"]; if (data != null) { @@ -130,13 +135,13 @@ class MarathonApiClient { } } - Future submitSelectedOption({required String? selectedAnswerId}) async { - Map jsonObject = {"selectedOptionId": selectedAnswerId}; + Future submitSelectedOption({required String marathonId, required String? questionId, required String? selectedAnswerId}) async { + Map jsonObject = {"marathonId": marathonId, "questionId": questionId, "selectedOptionId" : selectedAnswerId}; Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonSubmitAnswerUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken()); var json = jsonDecode(response.body); - logger.i("json: $json"); + logger.i("json in submitSelectedOption : $json"); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json); @@ -147,6 +152,42 @@ class MarathonApiClient { return marathonModel.isSuccessful!; } + Future getQualifiers({required String marathonId}) async { + Map params = {"marathonId": marathonId}; + Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonQualifiersUrl, queryParameters: params, token: AppState().getMarathonToken ?? await getMarathonToken()); + + var json = jsonDecode(response.body); + logger.i("json in getQualifiers: $json"); + + MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json); + + if (marathonGenericModel.isSuccessful == true && marathonGenericModel.statusCode == 200 && marathonGenericModel.data != null) { + return marathonGenericModel.data["winnerCount"]; + } + return null; + } + + Future?> getSelectedWinner({required String marathonId}) async { + Map params = {"marathonId": marathonId}; + Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonSelectedWinner, queryParameters: params, token: AppState().getMarathonToken ?? await getMarathonToken()); + + var json = jsonDecode(response.body); + logger.i("json in getSelectedWinner: $json"); + + MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json); + + if (marathonGenericModel.isSuccessful == true && marathonGenericModel.statusCode == 200 && marathonGenericModel.data != null) { + List winners = []; + List data = marathonGenericModel.data as List; + + for (Map winner in data) { + winners.add(WinnerModel.fromJson(winner)); + } + return winners; + } + return null; + } + // Future buildHubConnection(BuildContext context, String prizeId) async { // HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); // hubConnection = HubConnectionBuilder() diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index b0620ad..5060195 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -38,6 +38,7 @@ class AppState { String? get getForgetPasswordTokenID => forgetPasswordTokenID; + //Wifi info String? _mohemmWifiSSID; diff --git a/lib/classes/colors.dart b/lib/classes/colors.dart index 4394279..5c9b30e 100644 --- a/lib/classes/colors.dart +++ b/lib/classes/colors.dart @@ -63,4 +63,5 @@ class MyColors { static const Color darkDigitColor = Color(0xff2D2F39); static const Color grey71Color = Color(0xff717171); static const Color darkGrey3BColor = Color(0xff3B3B3B); + static const Color lightGreyIconColor = Color(0xff919191); } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 97cb8e6..3d58efd 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -3,6 +3,7 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; class ApiConsts { //static String baseUrl = "http://10.200.204.20:2801/"; // Local server // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server + // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server static String baseUrl = "https://hmgwebservices.com"; // Live server static String baseUrlServices = baseUrl + "/Services/"; // server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server @@ -30,15 +31,16 @@ class ApiConsts { static String marathonParticipantLoginUrl = marathonBaseUrl + "auth/participantlogin"; static String marathonProjectGetUrl = marathonBaseUrl + "Project/Project_Get"; static String marathonUpcomingUrl = marathonBaseUrl + "marathon/upcoming/"; - static String marathonJoinParticipantUrl = marathonBaseUrl + "participant/participant_join"; + static String marathonJoinParticipantUrl = marathonBaseUrl + "participant/join"; static String marathonNextQuestionUrl = marathonBaseUrl + "question/next"; static String marathonSubmitAnswerUrl = marathonBaseUrl + "question/submit"; static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/"; static String marathonSelectedWinner = marathonBaseUrl + "winner/getSelectedWinner/"; //DummyCards for the UI - static CardContent dummyQuestion = const CardContent(); + + static int tabletMinLength = 500; } class SharedPrefsConsts { diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 3b30d05..3be8356 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -37,7 +37,7 @@ class Utils { timeInSecForIosWeb: 1, backgroundColor: Colors.black54, textColor: Colors.white, - fontSize: 16.0); + fontSize: 13.0); } static dynamic getNotNullValue(List list, int index) { @@ -122,6 +122,7 @@ class Utils { ); } else { showToast(errorMessage); + // confirmDialog(cxt, errorMessage); } } } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 365a1c0..dc6b553 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -22,7 +22,6 @@ import 'package:mohem_flutter_app/ui/marathon/marathon_intro_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart'; -import 'package:mohem_flutter_app/ui/marathon/marathon_winner_selection.dart'; import 'package:mohem_flutter_app/ui/marathon/winner_screen.dart'; import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart'; @@ -187,7 +186,6 @@ class AppRoutes { //Marathon static const String marathonIntroScreen = "/marathonIntroScreen"; static const String marathonScreen = "/marathonScreen"; - static const String marathonWinnerSelection = "/marathonWinnerSelection"; static const String marathonWinnerScreen = "/marathonWinnerScreen"; static const String marathonSponsorVideoScreen = "/marathonSponsorVideoScreen"; static const String marathonWaitingScreen = "/marathonWaitingScreen"; @@ -299,7 +297,6 @@ class AppRoutes { // Marathon marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(), marathonScreen: (BuildContext context) => MarathonScreen(), - marathonWinnerSelection: (BuildContext context) => MarathonWinnerSelection(), marathonWinnerScreen: (BuildContext context) => WinnerScreen(), marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(), marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(), diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 01b7a08..8e1637e 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -141,8 +141,9 @@ extension EmailValidator on String { style: TextStyle(color: color ?? MyColors.grey3AColor, fontSize: 21, letterSpacing: -0.84, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)), ); - Widget toText22({Color? color, bool isBold = false}) => Text( + Widget toText22({Color? color, bool isBold = false, bool isCentered = false}) => Text( this, + textAlign: isCentered ? TextAlign.center : null, style: TextStyle(height: 1, color: color ?? MyColors.darkTextColor, fontSize: 22, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); @@ -152,12 +153,13 @@ extension EmailValidator on String { ); Widget toText30({Color? color, bool isBold = false}) => Text( - this, - style: TextStyle(height: 20 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.2, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), - ); + this, + style: TextStyle(height: 20 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.2, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), + ); - Widget toText32({Color? color, bool isBold = false}) => Text( + Widget toText32({Color? color, bool isBold = false, bool isCentered = false}) => Text( this, + textAlign: isCentered ? TextAlign.center : null, style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index a54ff37..b2a92ad 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -440,6 +440,9 @@ class CodegenLoader extends AssetLoader{ "typeCurrentPasswordBelow": "اكتب كلمة المرور الحاليه", "currentPassword": "كلمة المرور الحاليه", "concurrentReports": "التقارير المتزامنه", + "EnterNewAddressMoved": "أدخل عنوان جديد إذا كنت قد انتقلت", + "CorrectAddress": "تصحيح أو تعديل هذا العنوان", + "SelectChangeWantToMake": " حدد نوع التغيير الذي تريد القيام به.", "profile": { "reset_password": { "label": "Reset Password", @@ -516,7 +519,16 @@ class CodegenLoader extends AssetLoader{ "codeExpire": "انتهت صلاحية رمز التحقق", "typeheretoreply": "اكتب هنا للرد", "favorite": "مفضلتي", - "searchfromchat": "البحث من الدردشة" + "searchfromchat": "البحث من الدردشة", + "yourAnswerCorrect": "إجابتك صحيحة", + "youMissedTheQuestion": "نفد منك الوقت. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.", + "wrongAnswer": "إجابتك غير صحيحة. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.", + "oops": "أوه!!!", + "winner": "الفائز", + "youWantToLeaveMarathon": "هل أنت متأكد أنك تريد العودة؟ سوف تخرج من المسابقة.", + "ourSponsor": "راعينا:", + "startingIn": "يبدأ في", + "youAreOutOfContest": "أنت خارج المسابقة." }; static const Map en_US = { "mohemm": "Mohemm", @@ -944,6 +956,9 @@ static const Map en_US = { "typeCurrentPasswordBelow": "Type Your Current password below", "currentPassword": "Current password", "concurrentReports": "Concurrent Reports", + "EnterNewAddressMoved": "Enter a new address if you have moved", + "CorrectAddress": "Correct or amend this address", + "SelectChangeWantToMake": "Select the type of change you want to make", "profile": { "reset_password": { "label": "Reset Password", @@ -1020,7 +1035,16 @@ static const Map en_US = { "allQuestionsCorrect": "You have answered all questions correct", "typeheretoreply": "Type here to reply", "favorite": "My Favorites", - "searchfromchat": "Search from chat" + "searchfromchat": "Search from chat", + "yourAnswerCorrect": "Your answer is correct", + "youMissedTheQuestion": "You ran out of time. You are out of the game. But you can continue and as a viewer.", + "wrongAnswer": "Your answer is Incorrect. You are out of the game. But you can continue and as a viewer.", + "oops": "Ooopsss!!!!", + "winner": "WINNER", + "youWantToLeaveMarathon": "Are you sure you want to go back? You will be out of the contest.", + "ourSponsor": "Our Sponsor:", + "startingIn": "Starting in", + "youAreOutOfContest": "You are out of the contest." }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 1d28232..8960278 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -425,6 +425,9 @@ abstract class LocaleKeys { static const typeCurrentPasswordBelow = 'typeCurrentPasswordBelow'; static const currentPassword = 'currentPassword'; static const concurrentReports = 'concurrentReports'; + static const EnterNewAddressMoved = 'EnterNewAddressMoved'; + static const CorrectAddress = 'CorrectAddress'; + static const SelectChangeWantToMake = 'SelectChangeWantToMake'; static const profile_reset_password_label = 'profile.reset_password.label'; static const profile_reset_password_username = 'profile.reset_password.username'; static const profile_reset_password_password = 'profile.reset_password.password'; @@ -496,5 +499,6 @@ abstract class LocaleKeys { static const ourSponsor = 'ourSponsor'; static const startingIn = 'startingIn'; static const youAreOutOfContest = 'youAreOutOfContest'; + static const winners = 'winners'; } diff --git a/lib/main.dart b/lib/main.dart index 4709be1..fdc4ad6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,8 +2,10 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:logger/logger.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; +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'; @@ -32,11 +34,19 @@ class MyHttpOverrides extends HttpOverrides { } } +bool isTablet = false; + Future main() async { WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); await EasyLocalization.ensureInitialized(); AppState().setPostParamsInitConfig(); HttpOverrides.global = MyHttpOverrides(); + isTablet = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide >= ApiConsts.tabletMinLength; + runApp( EasyLocalization( supportedLocales: const [ diff --git a/lib/models/chat/chat_count_conversation_model.dart b/lib/models/chat/chat_count_conversation_model.dart index e584d32..1906803 100644 --- a/lib/models/chat/chat_count_conversation_model.dart +++ b/lib/models/chat/chat_count_conversation_model.dart @@ -1,8 +1,8 @@ -import 'dart:convert'; - -ChatUnreadCovnCountModel chatUnreadCovnCountModelFromJson(String str) => ChatUnreadCovnCountModel.fromJson(json.decode(str)); +// To parse this JSON data, do +// +// final chatUnreadCovnCountModel = chatUnreadCovnCountModelFromMap(jsonString); -String chatUnreadCovnCountModelToJson(ChatUnreadCovnCountModel data) => json.encode(data.toJson()); +import 'dart:convert'; class ChatUnreadCovnCountModel { ChatUnreadCovnCountModel({ @@ -13,13 +13,17 @@ class ChatUnreadCovnCountModel { int? singleChatCount; int? groupChatCount; - factory ChatUnreadCovnCountModel.fromJson(Map json) => ChatUnreadCovnCountModel( - singleChatCount: json["singleChatCount"] == null ? null : json["singleChatCount"], - groupChatCount: json["groupChatCount"] == null ? null : json["groupChatCount"], - ); + factory ChatUnreadCovnCountModel.fromJson(String str) => ChatUnreadCovnCountModel.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory ChatUnreadCovnCountModel.fromMap(Map json) => ChatUnreadCovnCountModel( + singleChatCount: json["singleChatCount"] == null ? null : json["singleChatCount"], + groupChatCount: json["groupChatCount"] == null ? null : json["groupChatCount"], + ); - Map toJson() => { - "singleChatCount": singleChatCount == null ? null : singleChatCount, - "groupChatCount": groupChatCount == null ? null : groupChatCount, - }; + Map toMap() => { + "singleChatCount": singleChatCount == null ? null : singleChatCount, + "groupChatCount": groupChatCount == null ? null : groupChatCount, + }; } diff --git a/lib/models/chat/get_search_user_chat_model.dart b/lib/models/chat/get_search_user_chat_model.dart index fe87061..3d023fd 100644 --- a/lib/models/chat/get_search_user_chat_model.dart +++ b/lib/models/chat/get_search_user_chat_model.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + class ChatUserModel { ChatUserModel({ this.response, @@ -35,6 +37,7 @@ class ChatUser { this.isTyping, this.isImageLoaded, this.isImageLoading, + this.userLocalDownlaodedImage, }); int? id; @@ -52,24 +55,25 @@ class ChatUser { bool? isTyping; bool? isImageLoaded; bool? isImageLoading; + File? userLocalDownlaodedImage; factory ChatUser.fromJson(Map json) => ChatUser( - id: json["id"] == null ? null : json["id"], - userName: json["userName"] == null ? null : json["userName"], - email: json["email"] == null ? null : json["email"], - phone: json["phone"], - title: json["title"], - userStatus: json["userStatus"] == null ? null : json["userStatus"], - image: json["image"], - unreadMessageCount: json["unreadMessageCount"] == null ? null : json["unreadMessageCount"], - userAction: json["userAction"], - isPin: json["isPin"] == null ? null : json["isPin"], - isFav: json["isFav"] == null ? null : json["isFav"], - isAdmin: json["isAdmin"] == null ? null : json["isAdmin"], - isTyping: false, - isImageLoaded: false, - isImageLoading: true, - ); + id: json["id"] == null ? null : json["id"], + userName: json["userName"] == null ? null : json["userName"], + email: json["email"] == null ? null : json["email"], + phone: json["phone"], + title: json["title"], + userStatus: json["userStatus"] == null ? null : json["userStatus"], + image: json["image"], + unreadMessageCount: json["unreadMessageCount"] == null ? null : json["unreadMessageCount"], + userAction: json["userAction"], + isPin: json["isPin"] == null ? null : json["isPin"], + isFav: json["isFav"] == null ? null : json["isFav"], + isAdmin: json["isAdmin"] == null ? null : json["isAdmin"], + isTyping: false, + isImageLoaded: false, + isImageLoading: true, + userLocalDownlaodedImage: null); Map toJson() => { "id": id == null ? null : id, diff --git a/lib/models/chat/get_single_user_chat_list_model.dart b/lib/models/chat/get_single_user_chat_list_model.dart index 80ffa5a..246a515 100644 --- a/lib/models/chat/get_single_user_chat_list_model.dart +++ b/lib/models/chat/get_single_user_chat_list_model.dart @@ -19,6 +19,8 @@ class SingleUserChatModel { this.targetUserName, this.encryptedTargetUserId, this.encryptedTargetUserName, + this.currentUserEmail, + this.targetUserEmail, this.chatEventId, this.fileTypeId, this.isSeen, @@ -38,8 +40,10 @@ class SingleUserChatModel { String? contantNo; int? currentUserId; String? currentUserName; + String? currentUserEmail; int? targetUserId; String? targetUserName; + String? targetUserEmail; String? encryptedTargetUserId; String? encryptedTargetUserName; int? chatEventId; @@ -64,6 +68,8 @@ class SingleUserChatModel { currentUserName: json["currentUserName"] == null ? null : json["currentUserName"], targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], + targetUserEmail: json["targetUserEmail"] == null ? null : json["targetUserEmail"], + currentUserEmail: json["currentUserEmail"] == null ? null : json["currentUserEmail"], encryptedTargetUserId: json["encryptedTargetUserId"] == null ? null : json["encryptedTargetUserId"], encryptedTargetUserName: json["encryptedTargetUserName"] == null ? null : json["encryptedTargetUserName"], chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], @@ -90,6 +96,8 @@ class SingleUserChatModel { "targetUserName": targetUserName == null ? null : targetUserName, "encryptedTargetUserId": encryptedTargetUserId == null ? null : encryptedTargetUserId, "encryptedTargetUserName": encryptedTargetUserName == null ? null : encryptedTargetUserName, + "currentUserEmail": currentUserEmail == null ? null : currentUserEmail, + "targetUserEmail": targetUserEmail == null ? null : targetUserEmail, "chatEventId": chatEventId == null ? null : chatEventId, "fileTypeId": fileTypeId, "isSeen": isSeen == null ? null : isSeen, diff --git a/lib/models/get_time_card_summary_list_model.dart b/lib/models/get_time_card_summary_list_model.dart index 85a2a8e..becc1d2 100644 --- a/lib/models/get_time_card_summary_list_model.dart +++ b/lib/models/get_time_card_summary_list_model.dart @@ -1,41 +1,41 @@ class GetTimeCardSummaryList { - int? aBSENTDAYS; + num? aBSENTDAYS; dynamic? aCTUALHRS; dynamic? aPPROVEDTIMEBACKHRS; - int? aSSIGNMENTID; - int? aTTENDEDDAYS; - int? bUSINESSTRIP; + num? aSSIGNMENTID; + num? aTTENDEDDAYS; + num? bUSINESSTRIP; dynamic? cOMPOFFHHRS; dynamic? cOMPOFFNHRS; dynamic? cOMPOFFWHRS; dynamic? dESIREDSCHEDULEDHRS; dynamic? eARLYOUTHRS; dynamic? eXCESSHRS; - int? hALFDAYLEAVE; + num? hALFDAYLEAVE; dynamic? lATEINHRS; dynamic? lEAVESHOLIDAYSHRS; dynamic? nONSCHEDULEDAYS; dynamic? nOTANALYZEDDAYS; - int? oFFDAYS; + num? oFFDAYS; dynamic? oNCALLHRS; dynamic? pAIDLEAVE; - int? pERIODDAYS; + num? pERIODDAYS; dynamic? pLANNEDOTHRS; - int? pUBLICHOLIDAY; + num? pUBLICHOLIDAY; dynamic? sCHEDULEDHRS; dynamic? sCHEDULEDONCALLHRS; dynamic? sCHEDULEDPLANNEDOTHRS; - int? sCHEDULEDAYS; + num? sCHEDULEDAYS; dynamic? sHORTAGEHRS; dynamic? sHORTAGESCHEDULEHRS; - int? sICKLEAVE; + num? sICKLEAVE; dynamic? tIMEBACKHRS; dynamic? tIMEBACKBALANCE; - int? uNAUTHORIZEDLEAVE; + num? uNAUTHORIZEDLEAVE; dynamic? uNCOVERDSHORTAGEHRS; - int? uNPAIDLEAVE; + num? uNPAIDLEAVE; GetTimeCardSummaryList( {this.aBSENTDAYS, diff --git a/lib/models/marathon/winner_model.dart b/lib/models/marathon/winner_model.dart new file mode 100644 index 0000000..5663bf1 --- /dev/null +++ b/lib/models/marathon/winner_model.dart @@ -0,0 +1,17 @@ +class WinnerModel { + String? id; + String? marathoneId; + String? employeeId; + String? nameEn; + String? nameAr; + + WinnerModel({id, marathoneId, employeeId, nameEn, nameAr}); + + WinnerModel.fromJson(Map json) { + id = json['id']; + marathoneId = json['marathoneId']; + employeeId = json['employeeId']; + nameEn = json['nameEn']; + nameAr = json['nameAr']; + } +} diff --git a/lib/models/member_information_list_model.dart b/lib/models/member_information_list_model.dart index 4225905..4871855 100644 --- a/lib/models/member_information_list_model.dart +++ b/lib/models/member_information_list_model.dart @@ -48,7 +48,7 @@ class MemberInformationListModel { String? nATIONALITYCODE; String? nATIONALITYMEANING; String? nATIONALIDENTIFIER; - String? nORMALHOURS; + dynamic? nORMALHOURS; int? nOOFROWS; int? oRGANIZATIONID; String? oRGANIZATIONNAME; diff --git a/lib/provider/chat_provider_model.dart b/lib/provider/chat_provider_model.dart index 3f55417..67899c5 100644 --- a/lib/provider/chat_provider_model.dart +++ b/lib/provider/chat_provider_model.dart @@ -13,6 +13,7 @@ import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/encryption.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/chat_count_conversation_model.dart'; import 'package:mohem_flutter_app/models/chat/chat_user_image_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; @@ -20,6 +21,8 @@ import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' a import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart'; +import 'package:open_file/open_file.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/signalr_client.dart'; import 'package:uuid/uuid.dart'; @@ -40,6 +43,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List repliedMsg = []; List favUsersList = []; int paginationVal = 0; + bool currentUserTyping = false; + int? cTypingUserId = 0; + + //Chat + int chatUConvCounter = 0; Future getUserAutoLoginToken() async { userLoginToken.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); @@ -57,30 +65,23 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { await chatHubConnection.start(); print("Startedddddddd"); chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); + chatHubConnection.on("OnGetChatConversationCount", onNewChatConversion); } Future getHubConnection() async { HubConnection hub; - // try { HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); hub = HubConnectionBuilder() .withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=${AppState().chatDetails!.response!.id}&source=Web&access_token=${AppState().chatDetails!.response!.token}", options: httpOp) .withAutomaticReconnect(retryDelays: [2000, 5000, 10000, 20000]).build(); - // isChatHubLoding = false; return hub; - // } catch (e) { - // getUserAutoLoginToken().whenComplete(() { - // getHubConnection(); - // }); - // throw e; - // } } void registerEvents() { chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus); // chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); // hubConnection.on("OnSeenChatUserAsync", onChatSeen); - //hubConnection.on("OnUserTypingAsync", onUserTyping); + chatHubConnection.on("OnUserTypingAsync", onUserTyping); chatHubConnection.on("OnUserCountAsync", userCountAsync); // hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); chatHubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); @@ -124,8 +125,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { AppState().chatDetails!.response!.id.toString(), ), ); + sort(); notifyListeners(); - if (searchedChats!.isNotEmpty) { + if (searchedChats!.isNotEmpty || favUsersList.isNotEmpty) { getUserImages(); } } @@ -140,19 +142,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { if (isNewChat) userChatHistory = []; if (!loadMore) paginationVal = 0; isChatScreenActive = true; - // if (chatHubConnection.state != HubConnectionState.Connected) { - // getUserAutoLoginToken().whenComplete(() async { - // await buildHubConnection(); - // getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore); - // }); - // return; - // } Response response = await ChatApiClient().getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal); if (response.statusCode == 204) { if (isNewChat) { userChatHistory = []; } else if (loadMore) { - // userChatHistory = []; Utils.showToast("No More Data To Load"); } } else { @@ -165,7 +159,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } isLoading = false; notifyListeners(); + markRead(userChatHistory, receiverUID); + generateConvId(); } @@ -177,26 +173,28 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void markRead(List data, int receiverID) { if (data != null) { for (SingleUserChatModel element in data!) { - if (element.isSeen != null) { - if (!element.isSeen!) { - element.isSeen = true; - dynamic data = [ - { - "userChatHistoryId": element.userChatHistoryId, - "TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId, - "isDelivered": true, - "isSeen": true, - } - ]; - updateUserChatHistoryStatusAsync(data); - notifyListeners(); + if (AppState().chatDetails!.response!.id! == element.targetUserId) { + if (element.isSeen != null) { + if (!element.isSeen!) { + element.isSeen = true; + dynamic data = [ + { + "userChatHistoryId": element.userChatHistoryId, + "TargetUserId": element.currentUserId == receiverID ? element.currentUserId : element.targetUserId, + "isDelivered": true, + "isSeen": true, + } + ]; + updateUserChatHistoryStatusAsync(data); + notifyListeners(); + } + } + for (ChatUser element in searchedChats!) { + if (element.id == receiverID) { + element.unreadMessageCount = 0; + chatUConvCounter = 0; + } } - } - } - for (ChatUser element in searchedChats!) { - if (element.id == receiverID) { - element.unreadMessageCount = 0; - // notifyListeners(); } } notifyListeners(); @@ -332,8 +330,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { temp = getSingleUserChatModel(jsonEncode(msg)); data.first.targetUserId = temp.first.currentUserId; data.first.targetUserName = temp.first.currentUserName; + data.first.targetUserEmail = temp.first.currentUserEmail; data.first.currentUserId = temp.first.targetUserId; data.first.currentUserName = temp.first.targetUserName; + data.first.currentUserEmail = temp.first.targetUserEmail; + if (data.first.fileTypeId == 12 || data.first.fileTypeId == 4 || data.first.fileTypeId == 3) { data.first.image = await ChatApiClient().downloadURL(fileName: data.first.contant!, fileTypeDescription: data.first.fileTypeResponse!.fileTypeDescription ?? "image/jpg"); } @@ -347,11 +348,28 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } } - dynamic contain = searchedChats!.where((ChatUser element) => element.id == data.first.currentUserId); - if (contain.isEmpty) { - searchedChats!.add(ChatUser(id: data.first.currentUserId, userName: data.first.currentUserName, unreadMessageCount: 0, isImageLoading: false, image: "", isImageLoaded: true, userStatus: 1)); - } + if (searchedChats != null) { + dynamic contain = searchedChats!.where((ChatUser element) => element.id == data.first.currentUserId); + if (contain.isEmpty) { + List emails = []; + emails.add(await EmailImageEncryption().encrypt(val: data.first.currentUserEmail!)); + List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); + searchedChats!.add( + ChatUser( + id: data.first.currentUserId, + userName: data.first.currentUserName, + unreadMessageCount: 0, + isImageLoading: false, + image: chatImages!.first.profilePicture ?? "", + isImageLoaded: true, + userStatus: 1, + isTyping: false, + userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, data.first.currentUserId.toString()), + ), + ); + } + } setMsgTune(); userChatHistory.insert(0, data.first); @@ -369,6 +387,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { {"userChatHistoryId": data.first.userChatHistoryId, "TargetUserId": temp.first.targetUserId, "isDelivered": true, "isSeen": isChatScreenActive ? true : false} ]; updateUserChatHistoryOnMsg(list); + invokeChatCounter(userId: AppState().chatDetails!.response!.id!); notifyListeners(); } @@ -474,7 +493,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { required bool isImageLoaded}) async { Uuid uuid = const Uuid(); String contentNo = uuid.v4(); - String msg = message.text; SingleUserChatModel data = SingleUserChatModel( chatEventId: chatEventId, @@ -493,8 +511,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { fileTypeResponse: isAttachment ? FileTypeResponse( fileTypeId: fileTypeId, - fileTypeName: getFileType(getFileExtension(selectedFile.path).toString()), - fileKind: getFileExtension(selectedFile.path), + fileTypeName: getFileExtension(selectedFile.path).toString(), + fileKind: "file", fileName: selectedFile.path.split("/").last, fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()), ) @@ -514,11 +532,26 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { await chatHubConnection.invoke("AddChatUserAsync", args: [json.decode(chatData)]); } - void sendChatMessage(int targetUserId, String targetUserName, BuildContext context) async { + void sendChatMessage(BuildContext context, {required int targetUserId, required int userStatus, required String userEmail, required String targetUserName}) async { dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); if (contain.isEmpty) { + List emails = []; + emails.add(await EmailImageEncryption().encrypt(val: userEmail)); + List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); searchedChats!.add( - ChatUser(id: targetUserId, userName: targetUserName, unreadMessageCount: 0, isImageLoading: false, image: "", isImageLoaded: true), + ChatUser( + id: targetUserId, + userName: targetUserName, + unreadMessageCount: 0, + email: userEmail, + isImageLoading: false, + image: chatImages.first.profilePicture ?? "", + isImageLoaded: true, + isTyping: false, + isFav: false, + userStatus: userStatus, + userLocalDownlaodedImage: await downloadImageLocal(chatImages.first.profilePicture, targetUserId.toString()), + ), ); notifyListeners(); } @@ -531,6 +564,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false, isImageLoaded: false, image: null); } // normal Text msg if (isFileSelected && !isMsgReply) { + bool isImage = false; print("Normal Attachment Msg"); Utils.showLoading(context); dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); @@ -606,6 +640,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { String? getFileExtension(String fileName) { try { + print("Ext: " + "." + fileName.split('.').last); return "." + fileName.split('.').last; } catch (e) { return null; @@ -733,14 +768,24 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void getUserImages() async { List emails = []; - for (ChatUser element in searchedChats!) { + List exists = [], unique = []; + exists.addAll(searchedChats!); + exists.addAll(favUsersList!); + Map profileMap = {}; + for (ChatUser item in exists) { + profileMap[item.email!] = item; + } + unique = profileMap.values.toList(); + for (ChatUser element in unique!) { emails.add(await EmailImageEncryption().encrypt(val: element.email!)); } + List chatImages = await ChatApiClient().getUsersImages(encryptedEmails: emails); for (ChatUser user in searchedChats!) { for (ChatUserImageModel uImage in chatImages) { if (user.email == uImage.email) { user.image = uImage.profilePicture ?? ""; + user.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, user.id.toString()); user.isImageLoading = false; user.isImageLoaded = true; } @@ -750,6 +795,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { for (ChatUserImageModel uImage in chatImages) { if (favUser.email == uImage.email) { favUser.image = uImage.profilePicture ?? ""; + favUser.userLocalDownlaodedImage = await downloadImageLocal(uImage.profilePicture, favUser.id.toString()); favUser.isImageLoading = false; favUser.isImageLoaded = true; } @@ -759,6 +805,35 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } + Future downloadImageLocal(String? encodedBytes, String userID) async { + File? myfile; + if (encodedBytes == null) { + return myfile; + } else { + await deleteFile(userID); + Uint8List decodedBytes = base64Decode(encodedBytes); + Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1 + late File imageFile = File("${appDocumentsDirectory.path}/$userID.jpg"); + imageFile.writeAsBytesSync(decodedBytes); + return imageFile; + } + } + + Future deleteFile(String userID) async { + Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); + late File imageFile = File('${appDocumentsDirectory.path}/$userID.jpg'); + if (await imageFile.exists()) { + await imageFile.delete(); + } + } + + Future downChatMedia(Uint8List bytes, String ext) async { + String dir = (await getApplicationDocumentsDirectory()).path; + File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext); + await file.writeAsBytes(bytes); + return file.path; + } + void setMsgTune() async { AudioPlayer player = AudioPlayer(); await player.setVolume(1.0); @@ -776,4 +851,30 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { print("Error: $e"); } } + + Future getChatMedia(BuildContext context, {required String fileName, required String fileTypeName, required int fileTypeID}) async { + Utils.showLoading(context); + if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) { + Uint8List encodedString = await ChatApiClient().downloadURL(fileName: fileName, fileTypeDescription: getFileTypeDescription(fileTypeName)); + try { + String path = await downChatMedia(encodedString, fileTypeName ?? ""); + Utils.hideLoading(context); + OpenFile.open(path); + } catch (e) { + Utils.showToast("Cannot open file."); + } + } + } + + void onNewChatConversion(List? params) { + dynamic items = params!.toList(); + logger.d(items); + chatUConvCounter = items[0]["singleChatCount"] ?? 0; + notifyListeners(); + } + + Future invokeChatCounter({required int userId}) async { + await chatHubConnection.invoke("GetChatCounversationCount", args: [userId]); + return ""; + } } diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 48531b7..c1a186a 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -7,7 +7,6 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/main.dart'; -import 'package:mohem_flutter_app/models/chat/chat_count_conversation_model.dart'; import 'package:mohem_flutter_app/models/dashboard/drawer_menu_item_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; @@ -20,6 +19,7 @@ import 'package:mohem_flutter_app/models/dashboard/mohemm_itg_pending_task_respo import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; +import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; /// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool @@ -35,9 +35,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool isWorkListLoading = true; int workListCounter = 0; - //Chat - bool isChatCounterLoding = true; - int chatUConvCounter = 0; + //Misssing Swipe bool isMissingSwipeLoading = true; @@ -96,8 +94,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { accrualList = null; leaveBalanceAccrual = null; - isChatCounterLoding = true; - chatUConvCounter = 0; ticketBalance = 0; isServicesMenusLoading = true; @@ -273,21 +269,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { MohemmItgResponseItem? res = await DashboardApiClient().getITGPageNotification(); return res; } - - void fetchChatCounts() async { - try { - ChatUnreadCovnCountModel response = await DashboardApiClient().getChatCount(); - chatUConvCounter = response.singleChatCount!; - isChatCounterLoding = false; - notifyListeners(); - } catch (ex) { - logger.wtf(ex); - notifyListeners(); - Utils.handleException(ex, null, null); - } - } - - void notify() { notifyListeners(); } diff --git a/lib/ui/chat/chat_bubble.dart b/lib/ui/chat/chat_bubble.dart index ea43004..c79c66b 100644 --- a/lib/ui/chat/chat_bubble.dart +++ b/lib/ui/chat/chat_bubble.dart @@ -1,17 +1,23 @@ +import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/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/chat/get_single_user_chat_list_model.dart'; +import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; +import 'package:open_file/open_file.dart'; +import 'package:provider/provider.dart'; // todo: @aamir use extension methods, and use correct widgets. @@ -19,11 +25,12 @@ class ChatBubble extends StatelessWidget { ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key); final String dateTime; final SingleUserChatModel cItem; - bool isCurrentUser = false; bool isSeen = false; bool isReplied = false; int? fileTypeID; + String? fileTypeName; + late ChatProviderModel data; String? fileTypeDescription; bool isDelivered = false; @@ -35,6 +42,7 @@ class ChatBubble extends StatelessWidget { isSeen = cItem.isSeen == true ? true : false; isReplied = cItem.userChatReplyResponse != null ? true : false; fileTypeID = cItem.fileTypeId; + fileTypeName = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeName : ""; fileTypeDescription = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeDescription : ""; isDelivered = cItem.currentUserId == AppState().chatDetails!.response!.id && cItem.isDelivered == true ? true : false; userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString(); @@ -45,10 +53,11 @@ class ChatBubble extends StatelessWidget { Size windowSize = MediaQuery.of(context).size; screenOffset = Offset(windowSize.width / 2, windowSize.height / 2); makeAssign(); + data = Provider.of(context, listen: false); return isCurrentUser ? currentUser(context) : receiptUser(context); } - Widget currentUser(context) { + Widget currentUser(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -77,22 +86,6 @@ class ChatBubble extends StatelessWidget { if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || cItem.userChatReplyResponse!.fileTypeId == 3 || cItem.userChatReplyResponse!.fileTypeId == 4) - // Container( - // padding: EdgeInsets.all(0), // Border width - // decoration: BoxDecoration(color: Colors.red, borderRadius: const BorderRadius.all(Radius.circular(8))), - // child: ClipRRect( - // borderRadius: const BorderRadius.all( - // Radius.circular(8), - // ), - // child: SizedBox.fromSize( - // size: Size.fromRadius(8), // Image radius - // child: showImage( - // isReplyPreview: true, - // fileName: cItem.userChatReplyResponse!.contant!, - // fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg"), - // ), - // ), - // ), ClipRRect( borderRadius: BorderRadius.circular(8.0), child: SizedBox( @@ -116,8 +109,15 @@ class ChatBubble extends StatelessWidget { anchorPoint: screenOffset, builder: (BuildContext context) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!), ); - }), - cItem.contant!.toText12(), + }) + else + Row( + children: [ + if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) + SvgPicture.asset(data.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 0, right: 10), + (cItem.contant ?? "").toText12().expanded, + ], + ), Align( alignment: Alignment.centerRight, child: Row( @@ -200,10 +200,19 @@ class ChatBubble extends StatelessWidget { ); }) else - (cItem.contant ?? "").toText12(color: Colors.white), + Row( + children: [ + if (fileTypeID == 1 || fileTypeID == 5 || fileTypeID == 7 || fileTypeID == 6 || fileTypeID == 8 || fileTypeID == 2) + SvgPicture.asset(data.getType(fileTypeName ?? ""), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly( + left: 0, + right: 10, + ), + (cItem.contant ?? "").toText12(color: Colors.white).expanded, + ], + ), Align( alignment: Alignment.centerRight, - child: dateTime.toText10(color: Colors.white.withOpacity(.71)), + child: dateTime.toText10(color: Colors.white.withOpacity(.71),), ), ], ), diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index 2ab5fd8..be685df 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -12,20 +12,28 @@ 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/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/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/ui/chat/call/chat_outgoing_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/chat_app_bar_widge.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:signalr_netcore/signalr_client.dart'; import 'package:swipe_to/swipe_to.dart'; +class ChatDetailedScreenParams { + ChatUser? chatUser; + bool? isNewChat; + + ChatDetailedScreenParams(this.chatUser, this.isNewChat); +} + class ChatDetailScreen extends StatefulWidget { - // ignore: prefer_const_constructors_in_immutables - ChatDetailScreen({Key? key}) : super(key: key); + const ChatDetailScreen({Key? key}) : super(key: key); @override State createState() => _ChatDetailScreenState(); @@ -33,16 +41,16 @@ class ChatDetailScreen extends StatefulWidget { class _ChatDetailScreenState extends State { final RefreshController _rc = RefreshController(initialRefresh: false); - dynamic userDetails; late ChatProviderModel data; + ChatDetailedScreenParams? params; void getMoreChat() async { - if (userDetails != null) { + if (params != null) { data.paginationVal = data.paginationVal + 10; - if (userDetails != null) { + if (params != null) { data.getSingleUserChatHistory( senderUID: AppState().chatDetails!.response!.id!.toInt(), - receiverUID: userDetails["targetUser"].id, + receiverUID: params!.chatUser!.id!, loadMore: true, isNewChat: false, ); @@ -56,24 +64,26 @@ class _ChatDetailScreenState extends State { @override Widget build(BuildContext context) { - userDetails = ModalRoute.of(context)!.settings.arguments; + params = ModalRoute.of(context)!.settings.arguments as ChatDetailedScreenParams; data = Provider.of(context, listen: false); - if (userDetails != null) { + if (params != null) { data.getSingleUserChatHistory( senderUID: AppState().chatDetails!.response!.id!.toInt(), - receiverUID: userDetails["targetUser"].id, + receiverUID: params!.chatUser!.id!, loadMore: false, - isNewChat: userDetails["isNewChat"], + isNewChat: params!.isNewChat!, ); } return Scaffold( backgroundColor: MyColors.backgroundColor, - appBar: AppBarWidget( + appBar: ChatAppBarWidget( context, - title: userDetails["targetUser"].userName.toString().replaceAll(".", " ").capitalizeFirstofEach, + title: params!.chatUser!.userName.toString().replaceAll(".", " ").capitalizeFirstofEach, showHomeButton: false, - image: userDetails["targetUser"].image == null || userDetails["targetUser"].image.isEmpty ? null : userDetails["targetUser"].image, + image: params!.chatUser!.image == null || params!.chatUser!.image.isEmpty ? null : params!.chatUser!.image, + showTyping: true, + chatUser: params!.chatUser, actions: [ SvgPicture.asset("assets/icons/chat/call.svg", width: 21, height: 23).onPress(() { // makeCall(callType: "AUDIO", con: hubConnection); @@ -85,145 +95,154 @@ class _ChatDetailScreenState extends State { 21.width, ], ), - body: Consumer( - builder: (BuildContext context, ChatProviderModel m, Widget? child) { - return (m.isLoading - ? ChatHomeShimmer( - isDetailedScreen: true, - ) - : Column( - children: [ - SmartRefresher( - enablePullDown: false, - enablePullUp: true, - onLoading: () { - getMoreChat(); - }, - header: const MaterialClassicHeader( - color: MyColors.gradiantEndColor, - ), - controller: _rc, - reverse: true, - child: ListView.separated( - controller: m.scrollController, - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - reverse: true, - itemCount: m.userChatHistory.length, - padding: const EdgeInsets.all(21), - separatorBuilder: (cxt, index) => 8.height, - itemBuilder: (BuildContext context, int i) { - return SwipeTo( - iconColor: MyColors.lightGreenColor, - child: ChatBubble( - dateTime: m.dateFormte(m.userChatHistory[i].createdDate!), - cItem: m.userChatHistory[i], - ), - onRightSwipe: () { - m.chatReply( - m.userChatHistory[i], - ); - }, - ).onPress(() { - logger.d(jsonEncode(m.userChatHistory[i])); - }); + body: SafeArea( + child: Consumer( + builder: (BuildContext context, ChatProviderModel m, Widget? child) { + return (m.isLoading + ? ChatHomeShimmer( + isDetailedScreen: true, + ) + : Column( + children: [ + SmartRefresher( + enablePullDown: false, + enablePullUp: true, + onLoading: () { + getMoreChat(); }, - ), - ).expanded, - if (m.isMsgReply) - SizedBox( - height: 82, - child: Row( - children: [ - Container(height: 82, color: MyColors.textMixColor, width: 6), - Container( - color: MyColors.darkTextColor.withOpacity(0.10), - padding: const EdgeInsets.only(top: 11, left: 14, bottom: 14, right: 21), - child: Row( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (AppState().chatDetails!.response!.userName == m.repliedMsg.first.currentUserName.toString() - ? "You" - : m.repliedMsg.first.currentUserName.toString().replaceAll(".", " ")) - .toText14(color: MyColors.lightGreenColor), - (m.repliedMsg.isNotEmpty ? m.repliedMsg.first.contant! : "").toText12(color: MyColors.grey71Color, maxLine: 2) - ], - ).expanded, - 12.width, - if (m.isMsgReply && m.repliedMsg.isNotEmpty) showReplyImage(m.repliedMsg), - 12.width, - const Icon(Icons.cancel, size: 23, color: MyColors.grey7BColor).onPress(m.closeMe), - ], - ), - ).expanded, - ], + header: const MaterialClassicHeader( + color: MyColors.gradiantEndColor, ), - ), - if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg") - SizedBox(height: 200, width: double.infinity, child: Image.file(m.selectedFile, fit: BoxFit.cover)).paddingOnly(left: 21, right: 21, top: 21), - const Divider( - height: 1, - color: MyColors.lightGreyEFColor, - ), - TextField( - controller: m.message, - decoration: InputDecoration( - hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), - hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - filled: true, - fillColor: MyColors.white, - contentPadding: const EdgeInsets.only( - left: 21, - top: 20, - bottom: 20, + controller: _rc, + reverse: true, + child: ListView.separated( + controller: m.scrollController, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + reverse: true, + itemCount: m.userChatHistory.length, + padding: const EdgeInsets.all(21), + separatorBuilder: (cxt, index) => 8.height, + itemBuilder: (BuildContext context, int i) { + return SwipeTo( + iconColor: MyColors.lightGreenColor, + child: ChatBubble( + dateTime: m.dateFormte(m.userChatHistory[i].createdDate!), + cItem: m.userChatHistory[i], + ), + onRightSwipe: () { + m.chatReply( + m.userChatHistory[i], + ); + }, + ).onPress(() async { + if (m.userChatHistory[i].fileTypeResponse != null) { + m.getChatMedia(context, + fileTypeName: m.userChatHistory[i].fileTypeResponse!.fileTypeName ?? "", fileTypeID: m.userChatHistory[i].fileTypeId!, fileName: m.userChatHistory[i].contant!); + } + }); + }, ), - prefixIconConstraints: const BoxConstraints(), - prefixIcon: m.sFileType.isNotEmpty - ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) - : null, - suffixIcon: SizedBox( - width: 100, + ).expanded, + if (m.isMsgReply) + SizedBox( + height: 82, child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.center, // added line children: [ - if (m.sFileType.isNotEmpty) - Row( - children: [ - const Icon(Icons.cancel, size: 15, color: MyColors.redA3Color).paddingOnly(right: 5), - ("Clear").toText11(color: MyColors.redA3Color, isUnderLine: true).paddingOnly(left: 0), + Container(height: 82, color: MyColors.textMixColor, width: 6), + Container( + color: MyColors.darkTextColor.withOpacity(0.10), + padding: const EdgeInsets.only(top: 11, left: 14, bottom: 14, right: 21), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (AppState().chatDetails!.response!.userName == m.repliedMsg.first.currentUserName.toString() + ? "You" + : m.repliedMsg.first.currentUserName.toString().replaceAll(".", " ")) + .toText14(color: MyColors.lightGreenColor), + (m.repliedMsg.isNotEmpty ? m.repliedMsg.first.contant! : "").toText12(color: MyColors.grey71Color, maxLine: 2) + ], + ).expanded, + 12.width, + if (m.isMsgReply && m.repliedMsg.isNotEmpty) showReplyImage(m.repliedMsg, m), + 12.width, + const Icon(Icons.cancel, size: 23, color: MyColors.grey7BColor).onPress(m.closeMe), ], - ).onPress(() => m.removeAttachment()).paddingOnly(right: 25), - if (m.sFileType.isEmpty) - RotationTransition( - turns: const AlwaysStoppedAnimation(45 / 360), - child: const Icon(Icons.attach_file_rounded, size: 26, color: MyColors.grey3AColor).onPress( - () => m.selectImageToUpload(context), - ), - ).paddingOnly(right: 25), - SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26).onPress( - () => m.sendChatMessage(userDetails["targetUser"].id, userDetails["targetUser"].userName, context), - ), + ), + ).expanded, ], ), - ).paddingOnly(right: 21), + ), + if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg") + SizedBox(height: 200, width: double.infinity, child: Image.file(m.selectedFile, fit: BoxFit.cover)).paddingOnly(left: 21, right: 21, top: 21), + const Divider( + height: 1, + color: MyColors.lightGreyEFColor, + ), + TextField( + controller: m.message, + decoration: InputDecoration( + hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), + hintStyle: TextStyle(color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, fontSize: 14), + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + filled: true, + fillColor: MyColors.white, + contentPadding: const EdgeInsets.only( + left: 21, + top: 20, + bottom: 20, + ), + prefixIconConstraints: const BoxConstraints(), + prefixIcon: m.sFileType.isNotEmpty + ? SvgPicture.asset(m.getType(m.sFileType), height: 30, width: 22, alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 21, right: 15) + : null, + suffixIcon: SizedBox( + width: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, // added line + children: [ + if (m.sFileType.isNotEmpty) + Row( + children: [ + const Icon(Icons.cancel, size: 15, color: MyColors.redA3Color).paddingOnly(right: 5), + ("Clear").toText11(color: MyColors.redA3Color, isUnderLine: true).paddingOnly(left: 0), + ], + ).onPress(() => m.removeAttachment()).paddingOnly(right: 25), + if (m.sFileType.isEmpty) + RotationTransition( + turns: const AlwaysStoppedAnimation(45 / 360), + child: const Icon(Icons.attach_file_rounded, size: 26, color: MyColors.grey3AColor).onPress( + () => m.selectImageToUpload(context), + ), + ).paddingOnly(right: 25), + SvgPicture.asset("assets/icons/chat/chat_send_icon.svg", height: 26, width: 26).onPress( + () => m.sendChatMessage(context, + targetUserId: params!.chatUser!.id!, + userStatus: params!.chatUser!.userStatus ?? 0, + userEmail: params!.chatUser!.email!, + targetUserName: params!.chatUser!.userName!), + ), + ], + ), + ).paddingOnly(right: 21), + ), ), - ), - ], - )); - }, + ], + )); + }, + ), ), ); } - Widget showReplyImage(List data) { + Widget showReplyImage(List data, ChatProviderModel m) { if (data.first.isImageLoaded! && data.first.image != null) { return Container( width: 43, @@ -232,7 +251,14 @@ class _ChatDetailScreenState extends State { border: Border.all(color: MyColors.darkGrey3BColor, width: 1), borderRadius: BorderRadius.circular(10.0), image: DecorationImage(image: MemoryImage(data.first.image!), fit: BoxFit.cover)), ); } else { - return const SizedBox(); + return data.first.fileTypeResponse != null && data.first.fileTypeResponse!.fileTypeName != null + ? Container( + width: 43, + height: 43, + constraints: const BoxConstraints(), + decoration: BoxDecoration(border: Border.all(color: MyColors.darkGrey3BColor, width: 1), borderRadius: BorderRadius.circular(10.0), color: Colors.white), + child: SvgPicture.asset(m.getType(data.first.fileTypeResponse!.fileTypeName ?? ""), alignment: Alignment.center, fit: BoxFit.cover).paddingOnly(left: 5, right: 5, top: 5, bottom: 5)) + : const SizedBox(); } } @@ -240,7 +266,7 @@ class _ChatDetailScreenState extends State { print("================== Make call Triggered ============================"); Map json = { "callerID": AppState().chatDetails!.response!.id!.toString(), - "callReceiverID": userDetails["targetUser"].id.toString(), + "callReceiverID": params!.chatUser!.id.toString(), "notification_foreground": "true", "message": "Aamir is calling", "title": "Video Call", @@ -259,7 +285,7 @@ class _ChatDetailScreenState extends State { { "isSeen": false, "isDelivered": false, - "targetUserId": userDetails["targetUser"].id, + "targetUserId": params!.chatUser!.id!, "targetUserStatus": 4, } ], diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index 76aa027..330242a 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -30,6 +30,15 @@ class _ChatHomeState extends State { super.initState(); data = Provider.of(context, listen: false); data.registerEvents(); + } + + @override + void dispose() { + super.dispose(); + data.clearAll(); + } + + void fetchAgain() { if (chatHubConnection.state != HubConnectionState.Connected) { data.getUserAutoLoginToken().whenComplete(() async { await data.buildHubConnection(); @@ -42,14 +51,9 @@ class _ChatHomeState extends State { } } - @override - void dispose() { - super.dispose(); - data.clearAll(); - } - @override Widget build(BuildContext context) { + fetchAgain(); return Scaffold( backgroundColor: MyColors.white, appBar: AppBarWidget(context, title: LocaleKeys.chat.tr(), showHomeButton: true), @@ -85,7 +89,6 @@ class _ChatHomeState extends State { onPageChanged: (int pageIndex) { setState(() { tabIndex = pageIndex; - }); }, children: [ diff --git a/lib/ui/chat/chat_home_screen.dart b/lib/ui/chat/chat_home_screen.dart index 804332a..a9814ee 100644 --- a/lib/ui/chat/chat_home_screen.dart +++ b/lib/ui/chat/chat_home_screen.dart @@ -1,22 +1,20 @@ import 'dart:convert'; -import 'dart:typed_data'; +import 'package:animated_text_kit/animated_text_kit.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_des/flutter_des.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/classes/encryption.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart'; -import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; @@ -53,7 +51,9 @@ class _ChatHomeScreenState extends State { body: Consumer( builder: (BuildContext context, ChatProviderModel m, Widget? child) { return m.isLoading - ? ChatHomeShimmer(isDetailedScreen: false,) + ? ChatHomeShimmer( + isDetailedScreen: false, + ) : Column( children: [ TextField( @@ -102,20 +102,24 @@ class _ChatHomeScreenState extends State { height: 48, width: 48, ).toShimmer().circle(30), - if (m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image.isNotEmpty) - CircularAvatar( - radius: 20, - height: 48, - width: 48, - url: m.searchedChats![index].image, - isImageBase64: true, - ), - if (!m.searchedChats![index].isImageLoading! && m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image.isEmpty) + if (!m.searchedChats![index].isImageLoading! && m.searchedChats![index].userLocalDownlaodedImage == null) SvgPicture.asset( "assets/images/user.svg", height: 48, width: 48, ), + if (!m.searchedChats![index].isImageLoading! && m.searchedChats![index].userLocalDownlaodedImage != null) + Container( + width: 48.0, + height: 48.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.cover, + image: FileImage(m.searchedChats![index].userLocalDownlaodedImage!), + ), + ), + ), Positioned( right: 5, bottom: 1, @@ -128,14 +132,20 @@ class _ChatHomeScreenState extends State { ).circle(10), ) ], - ).onPress(() { - print(jsonEncode(m.searchedChats![index])); - }), + ), Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ (m.searchedChats![index].userName!.replaceFirst(".", " ").capitalizeFirstofEach ?? "").toText14(color: MyColors.darkTextColor).paddingOnly(left: 11, top: 13), + m.searchedChats![index].isTyping! + ? AnimatedTextKit( + animatedTexts: [ + ScaleAnimatedText('Typing...', textStyle: const TextStyle(color: MyColors.textMixColor, fontSize: 10, letterSpacing: -0.4, fontStyle: FontStyle.normal)), + ], + ).paddingOnly(left: 11) + : const SizedBox() + //(m.searchedChats![index].isTyping! ? "Typing ..." : "").toText10(color: MyColors.textMixColor).paddingOnly(left: 11, top: 0), ], ).expanded, SizedBox( @@ -194,7 +204,7 @@ class _ChatHomeScreenState extends State { Navigator.pushNamed( context, AppRoutes.chatDetailed, - arguments: {"targetUser": m.searchedChats![index], "isNewChat": false}, + arguments: ChatDetailedScreenParams(m.searchedChats![index], false), ).then((Object? value) { m.clearSelections(); m.notifyListeners(); diff --git a/lib/ui/chat/favorite_users_screen.dart b/lib/ui/chat/favorite_users_screen.dart index 9378625..a978ccb 100644 --- a/lib/ui/chat/favorite_users_screen.dart +++ b/lib/ui/chat/favorite_users_screen.dart @@ -9,6 +9,7 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -45,20 +46,24 @@ class ChatFavoriteUsersScreen extends StatelessWidget { height: 48, width: 48, ).toShimmer().circle(30), - if (m.favUsersList![index].isImageLoaded! && m.favUsersList![index].image != null && m.favUsersList![index].image.isNotEmpty) - CircularAvatar( - radius: 20, - height: 48, - width: 48, - url: m.favUsersList![index].image, - isImageBase64: true, - ), - if (!m.favUsersList![index].isImageLoading! && m.favUsersList![index].isImageLoaded! && m.favUsersList![index].image.isEmpty) + if (!m.favUsersList![index].isImageLoading! && m.favUsersList![index].userLocalDownlaodedImage == null) SvgPicture.asset( "assets/images/user.svg", height: 48, width: 48, ), + if (!m.favUsersList![index].isImageLoading! && m.favUsersList![index].userLocalDownlaodedImage != null) + Container( + width: 48.0, + height: 48.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: DecorationImage( + fit: BoxFit.cover, + image: FileImage(m.favUsersList![index].userLocalDownlaodedImage!), + ), + ), + ), Positioned( right: 5, bottom: 1, @@ -106,7 +111,7 @@ class ChatFavoriteUsersScreen extends StatelessWidget { Navigator.pushNamed( context, AppRoutes.chatDetailed, - arguments: {"targetUser": m.favUsersList![index], "isNewChat": false}, + arguments: ChatDetailedScreenParams(m.favUsersList![index], true), ).then( (Object? value) { m.clearSelections(); diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 4e6988b..b1dac53 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -58,7 +58,7 @@ class _DashboardScreenState extends State { data = Provider.of(context, listen: false); marathonProvider = Provider.of(context, listen: false); cProvider = Provider.of(context, listen: false); - _bHubCon(); + _onRefresh(); }); } @@ -72,11 +72,15 @@ class _DashboardScreenState extends State { void _bHubCon() { cProvider.getUserAutoLoginToken().whenComplete(() { cProvider.buildHubConnection(); + Future.delayed(const Duration(seconds: 2), () { + cProvider.invokeChatCounter(userId: AppState().chatDetails!.response!.id!); + }); }); } void _onRefresh() async { data.initProvider(); + _bHubCon(); // data.getITGNotification().then((value) { // print("--------------------detail_1-----------------"); // print(value!.result!.data!.notificationMasterId); @@ -90,7 +94,6 @@ class _DashboardScreenState extends State { data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); - data.fetchChatCounts(); _refreshController.refreshCompleted(); } @@ -503,8 +506,8 @@ class _DashboardScreenState extends State { "assets/icons/chat/chat.svg", color: currentIndex == 4 ? MyColors.grey3AColor : MyColors.grey98Color, ).paddingAll(4), - Consumer( - builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { + Consumer( + builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { return Positioned( right: 0, top: 0, @@ -538,9 +541,7 @@ class _DashboardScreenState extends State { } else if (index == 3) { Navigator.pushNamed(context, AppRoutes.itemsForSale); } else if (index == 4) { - Navigator.pushNamed(context, AppRoutes.chat).then((Object? value) { - data.fetchChatCounts(); - }); + Navigator.pushNamed(context, AppRoutes.chat); } }, ), diff --git a/lib/ui/leave_balance/add_leave_balance_screen.dart b/lib/ui/leave_balance/add_leave_balance_screen.dart index d1b7d77..5ef03f4 100644 --- a/lib/ui/leave_balance/add_leave_balance_screen.dart +++ b/lib/ui/leave_balance/add_leave_balance_screen.dart @@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/leave_balance_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/date_uitl.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; @@ -24,7 +25,8 @@ import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; class AddLeaveBalanceScreen extends StatefulWidget { - AddLeaveBalanceScreen({Key? key}) : super(key: key); + final String selectedEmp; + AddLeaveBalanceScreen({this.selectedEmp = '', Key? key}) : super(key: key); @override _AddLeaveBalanceScreenState createState() { @@ -42,19 +44,21 @@ class _AddLeaveBalanceScreenState extends State { int? totalDays; String comment = ""; ReplacementList? selectedReplacementEmployee; + dynamic dynamicParams; + String selectedEmp = ""; DateTime selectedDate = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day); @override void initState() { super.initState(); - getAbsenceAttendanceTypes(); + //getAbsenceAttendanceTypes(); } void getAbsenceAttendanceTypes() async { try { Utils.showLoading(context); - absenceList = await LeaveBalanceApiClient().getAbsenceAttendanceTypes(); + absenceList = await LeaveBalanceApiClient().getAbsenceAttendanceTypes(empID: selectedEmp); Utils.hideLoading(context); setState(() {}); } catch (ex) { @@ -67,7 +71,7 @@ class _AddLeaveBalanceScreenState extends State { try { Utils.showLoading(context); getabsenceDffStructureList.clear(); - getabsenceDffStructureList = await LeaveBalanceApiClient().getAbsenceDffStructure(selectedAbsenceType!.dESCFLEXCONTEXTCODE!, "HR_LOA_SS", -999); + getabsenceDffStructureList = await LeaveBalanceApiClient().getAbsenceDffStructure(selectedAbsenceType!.dESCFLEXCONTEXTCODE!, "HR_LOA_SS", -999, empID: selectedEmp); Utils.hideLoading(context); setState(() {}); } catch (ex) { @@ -80,7 +84,7 @@ class _AddLeaveBalanceScreenState extends State { try { Utils.showLoading(context); CalculateAbsenceDuration duration = await LeaveBalanceApiClient() - .calculateAbsenceDuration(selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, Utils.getMonthNamedFormat(startDateTime!), Utils.getMonthNamedFormat(endDateTime!), -999); + .calculateAbsenceDuration(selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, Utils.getMonthNamedFormat(startDateTime!), Utils.getMonthNamedFormat(endDateTime!), -999, empID: selectedEmp); totalDays = duration.pABSENCEDAYS?.toInt(); Utils.hideLoading(context); @@ -91,54 +95,56 @@ class _AddLeaveBalanceScreenState extends State { } } - void validateAbsenceTransaction() async { + void validateAbsenceTransaction(selectedID) async { try { - Utils.showLoading(context); - Map dffDataMap = {}; - for (int i = 1; i <= 20; i++) { - dffDataMap["P_ATTRIBUTE$i"] = null; - for (int dffIndex = 0; dffIndex < getabsenceDffStructureList.length; dffIndex++) { - if ("ATTRIBUTE$i" == getabsenceDffStructureList[dffIndex].aPPLICATIONCOLUMNNAME) { - if (getabsenceDffStructureList[dffIndex].fORMATTYPE == "X") { - dffDataMap["P_ATTRIBUTE$i"] = - getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME != null ? Utils.formatDate(getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME!) : ""; - } else { - dffDataMap["P_ATTRIBUTE$i"] = getabsenceDffStructureList[dffIndex].eSERVICESDV?.pIDCOLUMNNAME; - } - break; + Utils.showLoading(context); + Map dffDataMap = {}; + for (int i = 1; i <= 20; i++) { + dffDataMap["P_ATTRIBUTE$i"] = null; + for (int dffIndex = 0; dffIndex < getabsenceDffStructureList.length; dffIndex++) { + if ("ATTRIBUTE$i" == getabsenceDffStructureList[dffIndex].aPPLICATIONCOLUMNNAME) { + if (getabsenceDffStructureList[dffIndex].fORMATTYPE == "X") { + dffDataMap["P_ATTRIBUTE$i"] = + getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME != null ? Utils.formatDate(getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME!) : ""; + } else { + dffDataMap["P_ATTRIBUTE$i"] = getabsenceDffStructureList[dffIndex].eSERVICESDV?.pIDCOLUMNNAME; } + break; } } - await LeaveBalanceApiClient().validateAbsenceTransaction( - selectedAbsenceType!.dESCFLEXCONTEXTCODE!, - "HR_LOA_SS", - selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, - selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", - DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), - DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), - -999, - dffDataMap, - comments: comment); + } + await LeaveBalanceApiClient().validateAbsenceTransaction( + selectedAbsenceType!.dESCFLEXCONTEXTCODE!, + "HR_LOA_SS", + selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, + selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", + DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), + DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), + -999, + dffDataMap, + comments: comment, + empID: selectedEmp); - SumbitAbsenceTransactionList submit = await LeaveBalanceApiClient().submitAbsenceTransaction( - selectedAbsenceType!.dESCFLEXCONTEXTCODE!, - "HR_LOA_SS", - selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, - selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", - DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), - DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), - -999, - dffDataMap, - comments: comment); + SumbitAbsenceTransactionList submit = await LeaveBalanceApiClient().submitAbsenceTransaction( + selectedAbsenceType!.dESCFLEXCONTEXTCODE!, + "HR_LOA_SS", + selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, + selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", + DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), + DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), + -999, + dffDataMap, + comments: comment, + empID: selectedEmp); - Utils.hideLoading(context); + Utils.hideLoading(context); - var res = await Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, arguments: RequestSubmitScreenParams(LocaleKeys.submit.tr(), submit.pTRANSACTIONID!, "", "add_leave_balance")); - if (res != null && res == true) { - Utils.showLoading(context); - } - await LeaveBalanceApiClient().cancelHrTransaction(submit.pTRANSACTIONID!); - Utils.hideLoading(context); + var res = await Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, arguments: RequestSubmitScreenParams(LocaleKeys.submit.tr(), submit.pTRANSACTIONID!, "", "add_leave_balance")); + if (res != null && res == true) { + Utils.showLoading(context); + } + await LeaveBalanceApiClient().cancelHrTransaction(submit.pTRANSACTIONID!); + Utils.hideLoading(context); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); @@ -152,6 +158,16 @@ class _AddLeaveBalanceScreenState extends State { @override Widget build(BuildContext context) { + if (dynamicParams == null) { + dynamicParams = ModalRoute.of(context)!.settings.arguments; + if (dynamicParams!= null && dynamicParams.isNotEmpty) { + AppState().postParamsJson['P_SELECTED_EMPLOYEE_NUMBER'] = dynamicParams; + selectedEmp = dynamicParams; + getAbsenceAttendanceTypes(); + } else { + getAbsenceAttendanceTypes(); + } + } return Scaffold( backgroundColor: Colors.white, appBar: AppBarWidget( @@ -269,7 +285,7 @@ class _AddLeaveBalanceScreenState extends State { validateFieldData() ? null : () { - validateAbsenceTransaction(); + validateAbsenceTransaction(selectedEmp); }, ).insideContainer ], diff --git a/lib/ui/leave_balance/leave_balance_screen.dart b/lib/ui/leave_balance/leave_balance_screen.dart index 548ca99..d1a64c6 100644 --- a/lib/ui/leave_balance/leave_balance_screen.dart +++ b/lib/ui/leave_balance/leave_balance_screen.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/leave_balance_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/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; @@ -13,7 +14,8 @@ import 'package:mohem_flutter_app/widgets/balances_dashboard_widget.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; class LeaveBalance extends StatefulWidget { - LeaveBalance({Key? key}) : super(key: key); + final String selectedEmp; + LeaveBalance({this.selectedEmp = '', Key? key}) : super(key: key); @override _LeaveBalanceState createState() { @@ -25,12 +27,13 @@ class _LeaveBalanceState extends State { List? absenceTransList; DateTime accrualDateTime = DateTime.now(); + dynamic dynamicParams; + String selectedEmp = ""; @override void initState() { super.initState(); - - getAbsenceTransactions(); + absenceTransList = []; } @override @@ -40,9 +43,9 @@ class _LeaveBalanceState extends State { void getAbsenceTransactions() async { try { - Utils.showLoading(context); - absenceTransList = await LeaveBalanceApiClient().getAbsenceTransactions(-999); - Utils.hideLoading(context); + // Utils.showLoading(context); + absenceTransList = await LeaveBalanceApiClient().getAbsenceTransactions(-999, empID: selectedEmp); + //Utils.hideLoading(context); setState(() {}); } catch (ex) { Utils.hideLoading(context); @@ -52,6 +55,16 @@ class _LeaveBalanceState extends State { @override Widget build(BuildContext context) { + if (dynamicParams == null) { + dynamicParams = ModalRoute.of(context)!.settings.arguments; + if (dynamicParams!= null && dynamicParams.isNotEmpty) { + AppState().postParamsJson['P_SELECTED_EMPLOYEE_NUMBER'] = dynamicParams; + selectedEmp = dynamicParams; + getAbsenceTransactions(); + } else { + getAbsenceTransactions(); + } + } return Scaffold( backgroundColor: Colors.white, appBar: AppBarWidget( @@ -62,7 +75,7 @@ class _LeaveBalanceState extends State { physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(21), children: [ - BalancesDashboardWidget(LocaleKeys.currentLeaveBalance.tr(), true), + BalancesDashboardWidget(LocaleKeys.currentLeaveBalance.tr(), true, selectedEmp), 12.height, absenceTransList == null ? const SizedBox() @@ -111,7 +124,7 @@ class _LeaveBalanceState extends State { ), child: const Icon(Icons.add, color: Colors.white, size: 30), ).onPress(() { - Navigator.pushNamed(context, AppRoutes.addLeaveBalance); + Navigator.pushNamed(context, AppRoutes.addLeaveBalance, arguments: selectedEmp ?? ''); }), ); } diff --git a/lib/ui/login/verify_login_screen.dart b/lib/ui/login/verify_login_screen.dart index ce21b63..b5eb7df 100644 --- a/lib/ui/login/verify_login_screen.dart +++ b/lib/ui/login/verify_login_screen.dart @@ -642,6 +642,7 @@ class _VerifyLoginScreenState extends State { PrivilegeListModel.saveToPrefs(genericResponseModel.privilegeList ?? []); AppState().setMohemmWifiSSID = genericResponseModel.mohemmWifiSSID; AppState().setMohemmWifiPassword = genericResponseModel.mohemmWifiPassword; + AppState().setMohemmWifiPassword = genericResponseModel.mohemmWifiPassword; Utils.saveStringFromPrefs(SharedPrefsConsts.username, AppState().getUserName!); Utils.saveStringFromPrefs(SharedPrefsConsts.password, AppState().password!); Utils.saveStringFromPrefs(SharedPrefsConsts.mohemmWifiSSID, genericResponseModel.mohemmWifiSSID!); diff --git a/lib/ui/marathon/marathon_provider.dart b/lib/ui/marathon/marathon_provider.dart index 1cf8efd..175b7be 100644 --- a/lib/ui/marathon/marathon_provider.dart +++ b/lib/ui/marathon/marathon_provider.dart @@ -5,10 +5,13 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/marathon/marathon_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; +import 'package:mohem_flutter_app/models/marathon/winner_model.dart'; +import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; import 'package:video_player/video_player.dart'; @@ -22,10 +25,13 @@ class MarathonProvider extends ChangeNotifier { QuestionModel currentQuestion = QuestionModel(); List answerStatusesList = []; QuestionCardStatus questionCardStatus = QuestionCardStatus.question; - + List? selectedWinners; int? selectedOptionIndex; String? selectedOptionId; - int totalQualifiers = 0; + int? totalQualifiers; + + bool iAmWinner = false; + bool isPrivilegedWithMarathon = false; bool _isLoading = false; @@ -110,6 +116,7 @@ class MarathonProvider extends ChangeNotifier { //****************TIMERS********** + int sponsorsSecondsCounter = 0; int totalSponsorVideoSeconds = 0; Timer timerForSponsorVideo = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); @@ -118,6 +125,7 @@ class MarathonProvider extends ChangeNotifier { timerForSponsorVideo = Timer.periodic( oneSec, (Timer timer) async { + sponsorsSecondsCounter++; if (totalSponsorVideoSeconds == 0) { timer.cancel(); notifyListeners(); @@ -134,12 +142,15 @@ class MarathonProvider extends ChangeNotifier { int totalSecondsToWaitForMarathon = 20; Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); - void startTimerToMarathon(BuildContext context) { + void startTimerToWaitForMarathon() { const Duration oneSec = Duration(seconds: 1); timerToWaitForMarathon = Timer.periodic( oneSec, (Timer timer) async { if (totalSecondsToWaitForMarathon == 0) { + callNextQuestionApi(); + timer.cancel(); + return; } else { totalSecondsToWaitForMarathon--; } @@ -158,11 +169,13 @@ class MarathonProvider extends ChangeNotifier { oneSec, (Timer timer) async { // This 2 is just to show the color of answer tile for 2 seconds and then update card status - if (totalCurrentQuestionTime - currentGapTime == currentQuestion.questionTime! - 2) { + if (totalCurrentQuestionTime - currentGapTime == 1) { getCorrectAnswerAndUpdateAnswerColor(); } if (totalCurrentQuestionTime == currentGapTime) { + totalCurrentQuestionTime--; + updateCardStatusToAnswer(); await callSubmitOptionApi().then((bool value) async { @@ -174,7 +187,8 @@ class MarathonProvider extends ChangeNotifier { if (totalCurrentQuestionTime == 0) { updateCardData(); - if (currentQuestionNumber == marathonDetailModel.totalQuestions! - 1) { + if (currentQuestionNumber == marathonDetailModel.totalQuestions! ) { + callGetQualifiersApi(); updateQuestionCardStatus(QuestionCardStatus.findingWinner); timer.cancel(); cancelTimer(); @@ -182,7 +196,9 @@ class MarathonProvider extends ChangeNotifier { } return; } else { - totalCurrentQuestionTime--; + if (totalCurrentQuestionTime != currentGapTime) { + totalCurrentQuestionTime--; + } } notifyListeners(); @@ -214,7 +230,30 @@ class MarathonProvider extends ChangeNotifier { //****************FUNCTIONS********* Future callSubmitOptionApi() async { - return await MarathonApiClient().submitSelectedOption(selectedAnswerId: selectedOptionId); + return await MarathonApiClient().submitSelectedOption(marathonId: marathonDetailModel.id!, questionId: currentQuestion.id, selectedAnswerId: selectedOptionId); + } + + Future callGetQualifiersApi() async { + totalQualifiers = await MarathonApiClient().getQualifiers(marathonId: marathonDetailModel.id!); + notifyListeners(); + } + + Future callGetSelectedWinnersApi() async { + selectedWinners = await MarathonApiClient().getSelectedWinner(marathonId: marathonDetailModel.id!); + if (selectedWinners != null) { + selectedWinners!.removeWhere((WinnerModel element) { + print("matching : ${AppState().memberInformationList!.eMPLOYEENUMBER} with ${element.employeeId}"); + if (element.employeeId == AppState().memberInformationList!.eMPLOYEENUMBER) { + iAmWinner = true; + return true; + } else { + return false; + } + }); + } + print("selectedWinners Length : ${selectedWinners!.length}"); + + notifyListeners(); } // TODO: here I need to add a logic where I should call this function for Api but for the 1st question it should behave differently @@ -222,6 +261,7 @@ class MarathonProvider extends ChangeNotifier { Future callNextQuestionApi() async { if (currentQuestionNumber < marathonDetailModel.totalQuestions!) { if (currentQuestionNumber == 0) { + Utils.showLoading(AppRoutes.navigatorKey.currentContext!); currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!))!; if (Utils.isLoading) { Utils.hideLoading(AppRoutes.navigatorKey.currentContext!); @@ -239,8 +279,7 @@ class MarathonProvider extends ChangeNotifier { void updateCardData() { if (currentQuestionNumber > 0) { - print("swiped it away!!"); - swipeCardLeft(); + swiperController.swipeLeft(); } selectedOptionIndex = null; currentQuestionNumber++; @@ -249,6 +288,7 @@ class MarathonProvider extends ChangeNotifier { currentGapTime = currentQuestion.nextQuestGap!; totalMarathoners = currentQuestion.remainingParticipantCount!; questionCardStatus = QuestionCardStatus.question; + notifyListeners(); } void populateQuestionStatusesList() { @@ -316,20 +356,21 @@ class MarathonProvider extends ChangeNotifier { } } - void swipeCardLeft() { - swiperController.swipeLeft(); - notifyListeners(); - } - void resetValues() async { _currentQuestionNumber = 0; cardContentList.clear(); timerForWinnerSelection.cancel(); + timerForSponsorVideo.cancel(); + timerToWaitForMarathon.cancel(); timerForQuestion.cancel(); _isMarathonCompleted = false; + isUserOutOfGame = false; totalCurrentQuestionTime = 0; + sponsorsSecondsCounter = 0; + totalSponsorVideoSeconds = 0; + totalSecondsToWaitForMarathon = 20; + currentGapTime = 0; currentQuestion = QuestionModel(); - notifyListeners(); } @@ -338,56 +379,54 @@ class MarathonProvider extends ChangeNotifier { notifyListeners(); } + bool checkIfPrivilegedForMarathon() { + for (PrivilegeListModel element in AppState().privilegeListModel!) { + if (element.serviceName == "Marathon") { + if (element.previlege != null) { + return element.previlege!; + } + } + } + return false; + } + Future getMarathonDetailsFromApi() async { isLoading = true; notifyListeners(); - await MarathonApiClient().getMarathonToken().whenComplete(() async { - marathonDetailModel = await MarathonApiClient().getMarathonDetails(); - if (marathonDetailModel.id == null) { - isUpComingMarathon = false; - notifyListeners(); - return; - } - populateQuestionStatusesList(); - isLoading = false; - notifyListeners(); - }); + isPrivilegedWithMarathon = checkIfPrivilegedForMarathon(); + if (isPrivilegedWithMarathon) { + await MarathonApiClient().getMarathonToken().whenComplete(() async { + marathonDetailModel = await MarathonApiClient().getMarathonDetails(); + if (marathonDetailModel.id == null) { + isUpComingMarathon = false; + isLoading = false; + notifyListeners(); + return; + } + populateQuestionStatusesList(); + }); + } + isLoading = false; + notifyListeners(); } - Future buildConnectionWithSignalR(BuildContext context) async { - Utils.showLoading(context); + Future onJoinMarathonPressed(BuildContext context) async { try { + Utils.showLoading(context); resetValues(); - // await MarathonApiClient().buildHubConnection(context, marathonDetailModel.sponsors!.first.sponsorPrizes!.first.id!); + int? remainingTime = await MarathonApiClient().joinMarathonAsParticipant(); + if (remainingTime != null) { + totalSecondsToWaitForMarathon = remainingTime; + notifyListeners(); + startTimerToWaitForMarathon(); + Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen); + } } catch (e) { if (kDebugMode) { - print("error in buildConnectionWithSignalR: ${e.toString()}"); + print("error in onJoinMarathonPressed: ${e.toString()}"); } Utils.hideLoading(context); Utils.confirmDialog(context, e.toString()); } } - - Future onJoinMarathonPressed(BuildContext context) async { - //TODO: here we need to put a check to make sure we should not display sponsor when remaining time to marathon is less than 30 seconds plus video duration e.g. 30 seconds + video duration time - // if (marathonDetailModel.sponsors!.first.video != null && marathonDetailModel.sponsors!.first.video != "") { - if (false) { - await initializeVideoPlayer().then((_) { - startTimerForSponsorVideo(); - Navigator.pushNamed(context, AppRoutes.marathonSponsorVideoScreen); - }); - } else { - try { - Utils.showLoading(context); - bool isJoined = await MarathonApiClient().joinMarathonAsParticipant(); - if (isJoined) { - print("joined"); - callNextQuestionApi(); - } - } catch (e, s) { - Utils.hideLoading(context); - Utils.confirmDialog(context, e.toString()); - } - } - } } diff --git a/lib/ui/marathon/marathon_screen.dart b/lib/ui/marathon/marathon_screen.dart index e14f8c9..71ae3d6 100644 --- a/lib/ui/marathon/marathon_screen.dart +++ b/lib/ui/marathon/marathon_screen.dart @@ -8,7 +8,7 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.dart'; import 'package:mohem_flutter_app/classes/lottie_consts.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'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; @@ -21,6 +21,7 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_qualifiers_contai import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card_builder.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; import 'package:provider/provider.dart'; class MarathonScreen extends StatelessWidget { @@ -42,65 +43,102 @@ class MarathonScreen extends StatelessWidget { return Image.asset(MyLottieConsts.congratsGif, height: 200); } + // SizedBox( + // height: 200, + // child: Stack( + // fit: StackFit.expand, + // children: [ + // Lottie.asset(MyLottieConsts.celebrate1Lottie, height: 200), + // // Lottie.asset(MyLottieConsts.celebrate2Lottie, height: 200), + // ], + // ), + // ), + Widget getWinnerWidget(BuildContext context, {required MarathonProvider provider}) { return Container( width: double.infinity, decoration: MyDecorations.shadowDecoration, padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - height: 200, - child: Stack( - children: [ - Lottie.asset(MyLottieConsts.celebrate1Lottie, height: 200), - Lottie.asset(MyLottieConsts.celebrate2Lottie, height: 200), + child: Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 50, + child: Stack( + children: [ + Align( + alignment: Alignment.center, + child: SvgPicture.asset("assets/images/winner_ribbon.svg", height: 50), + ), + Align( + alignment: Alignment.center, + child: LocaleKeys.winners.tr().toText32(color: MyColors.white, isBold: true, isCentered: true).paddingOnly(top: 07), + ) + ], + ), + ), + 16.height, + !provider.iAmWinner + ? Column( + children: [ + (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn)!.toText22( + color: MyColors.grey3AColor, + isCentered: true, + ), + 8.height, + AppState().memberInformationList!.eMPLOYEENUMBER!.toText22(color: MyColors.grey57Color), + ], + ) + : 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), + ], + ); + }, + ), ], - ), - ), - 26.height, - SizedBox( - height: 50, - child: Stack( - children: [ - Align( - alignment: Alignment.center, - child: SvgPicture.asset("assets/images/winner_ribbon.svg", height: 50), + 60.height, + if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...[ + Row( + 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( + color: MyColors.darkTextColor, + isBold: true, + ), + ], ), - Align( - alignment: Alignment.center, - child: LocaleKeys.winner.tr().toText32(color: MyColors.white, isBold: true).paddingOnly(top: 07), + 5.height, + Image.network( + provider.marathonDetailModel.sponsors!.first.image!, + height: 40, + width: 150, + fit: BoxFit.fill, + errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { + return const Center(); + }, ) ], - ), + ], ), - 12.height, - "Muhammad Shrouff".toText22(color: MyColors.grey3AColor), - "837436".toText22(color: MyColors.grey57Color), - 80.height, - if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...[ - Row( - 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( - color: MyColors.darkTextColor, - isBold: true, - ), - ], - ), - 5.height, - Image.network( - provider.marathonDetailModel.sponsors!.first.image!, - height: 40, - width: 150, - fit: BoxFit.fill, - errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { - return const Center(); - }, - ) - ], + Lottie.asset(MyLottieConsts.celebrate1Lottie), ], ), ); @@ -118,12 +156,12 @@ class MarathonScreen extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr!) + (AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!) .toText17(isBold: true, color: MyColors.white), AppState().memberInformationList!.eMPLOYEENUMBER!.toText17(isBold: true, color: MyColors.white), ], ), - ).paddingOnly(left: 20, right: 20, top: 12, bottom: 20); + ).paddingOnly(left: 20, right: 20, top: 12, bottom: 10); } @override @@ -135,10 +173,38 @@ class MarathonScreen extends StatelessWidget { context, title: LocaleKeys.brainMarathon.tr(), onHomeTapped: () { - Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr()); + if (provider.questionCardStatus == QuestionCardStatus.winnerFound) { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + } else { + showDialog( + context: context, + builder: (BuildContext context) => ConfirmDialog( + message: LocaleKeys.youWantToLeaveMarathon.tr(), + onTap: () { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + }, + ), + ); + } }, onBackTapped: () { - Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr()); + if (provider.questionCardStatus == QuestionCardStatus.winnerFound) { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + } else { + showDialog( + context: context, + builder: (BuildContext context) => ConfirmDialog( + message: LocaleKeys.youWantToLeaveMarathon.tr(), + onTap: () { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + }, + ), + ); + } }, ), body: SingleChildScrollView( @@ -156,7 +222,7 @@ class MarathonScreen extends StatelessWidget { getNameContainer(context), ], QuestionCardBuilder( - onQuestion: (BuildContext context) => QuestionCard(provider: provider), + onQuestion: (BuildContext context) => const QuestionCard(), onCompleted: (BuildContext context) => CustomStatusWidget( asset: Lottie.asset(MyLottieConsts.allQuestions, height: 200), title: LocaleKeys.congrats.tr().toText22(color: MyColors.greenColor), diff --git a/lib/ui/marathon/marathon_sponsor_video_screen.dart b/lib/ui/marathon/marathon_sponsor_video_screen.dart index c81f59b..12d5949 100644 --- a/lib/ui/marathon/marathon_sponsor_video_screen.dart +++ b/lib/ui/marathon/marathon_sponsor_video_screen.dart @@ -25,6 +25,9 @@ class SponsorVideoScreen extends StatelessWidget { return WillPopScope( onWillPop: () { provider.videoController.dispose(); + provider.sponsorsSecondsCounter = 0; + provider.totalSponsorVideoSeconds = 0; + provider.timerForSponsorVideo.cancel(); return Future.value(true); }, child: Scaffold( @@ -52,9 +55,11 @@ class SponsorVideoScreen extends StatelessWidget { child: provider.totalSponsorVideoSeconds == 0 ? InkWell( onTap: () { - Navigator.pop(context); provider.videoController.dispose(); - provider.buildConnectionWithSignalR(AppRoutes.navigatorKey.currentState!.overlay!.context); + provider.sponsorsSecondsCounter = 0; + provider.totalSponsorVideoSeconds = 0; + provider.timerForSponsorVideo.cancel(); + Navigator.pushReplacementNamed(context, AppRoutes.marathonIntroScreen); }, child: const Icon(Icons.close, size: 12), ) @@ -64,24 +69,30 @@ class SponsorVideoScreen extends StatelessWidget { ), ), ).paddingOnly(top: 20, right: 18), - Align( - alignment: Alignment.topLeft, - child: InkWell( - onTap: () { - Navigator.pop(context); - provider.videoController.dispose(); - provider.buildConnectionWithSignalR(AppRoutes.navigatorKey.currentState!.overlay!.context); - }, - child: Container( - decoration: BoxDecoration(color: MyColors.white, borderRadius: BorderRadius.circular(15)), - padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 6), - child: Directionality( - textDirection: ui.TextDirection.ltr, - child: LocaleKeys.skip.tr().toText11(color: MyColors.darkTextColor), - ), - ), - ), - ).paddingOnly(top: 20, left: 18), + + //TODO: WE WILL INCREASE THIS 2 BEFORE GOING LIVE + provider.sponsorsSecondsCounter >= 2 + ? Align( + alignment: Alignment.topLeft, + child: InkWell( + onTap: () { + provider.videoController.dispose(); + provider.sponsorsSecondsCounter = 0; + provider.totalSponsorVideoSeconds = 0; + provider.timerForSponsorVideo.cancel(); + Navigator.pushReplacementNamed(context, AppRoutes.marathonIntroScreen); + }, + child: Container( + decoration: BoxDecoration(color: MyColors.white, borderRadius: BorderRadius.circular(15)), + padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 6), + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: LocaleKeys.skip.tr().toText11(color: MyColors.darkTextColor), + ), + ), + ), + ).paddingOnly(top: 20, left: 18) + : const SizedBox(), ], ), ), diff --git a/lib/ui/marathon/marathon_waiting_screen.dart b/lib/ui/marathon/marathon_waiting_screen.dart index 27f0c08..7191ff2 100644 --- a/lib/ui/marathon/marathon_waiting_screen.dart +++ b/lib/ui/marathon/marathon_waiting_screen.dart @@ -4,12 +4,13 @@ import 'package:lottie/lottie.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.dart'; import 'package:mohem_flutter_app/classes/lottie_consts.dart'; -import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; import 'package:provider/provider.dart'; class MarathonWaitingScreen extends StatelessWidget { @@ -18,44 +19,90 @@ class MarathonWaitingScreen extends StatelessWidget { @override Widget build(BuildContext context) { MarathonProvider provider = context.watch(); - return Scaffold( - appBar: AppBarWidget( - context, - title: LocaleKeys.brainMarathon.tr(), - onHomeTapped: () { - Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr()); - }, - onBackTapped: () { - Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr()); - }, - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: double.infinity, - margin: const EdgeInsets.all(21), - decoration: MyDecorations.shadowDecoration, - child: Stack( - children: [ - Align( - child: Lottie.asset(MyLottieConsts.marathonWaiting, height: 200), - ), - Align( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - LocaleKeys.startingIn.tr().toText16(), - "00:${provider.totalSecondsToWaitForMarathon < 10 ? "0${provider.totalSecondsToWaitForMarathon}" : provider.totalSecondsToWaitForMarathon}" - .toText18(color: provider.totalSecondsToWaitForMarathon < 5 ? MyColors.redColor : MyColors.black), - ], + return WillPopScope( + onWillPop: () { + showDialog( + context: context, + builder: (BuildContext context) => ConfirmDialog( + message: LocaleKeys.youWantToLeaveMarathon.tr(), + onTap: () { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + }, + ), + ); + return Future.value(false); + }, + child: Scaffold( + appBar: AppBarWidget( + context, + title: LocaleKeys.brainMarathon.tr(), + onHomeTapped: () { + showDialog( + context: context, + builder: (BuildContext context) => ConfirmDialog( + message: LocaleKeys.youWantToLeaveMarathon.tr(), + onTap: () { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + }, + ), + ); + }, + onBackTapped: () { + showDialog( + context: context, + builder: (BuildContext context) => ConfirmDialog( + message: LocaleKeys.youWantToLeaveMarathon.tr(), + onTap: () { + provider.resetValues(); + Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); + }, + ), + ); + }, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: double.infinity, + margin: const EdgeInsets.all(21), + decoration: MyDecorations.shadowDecoration, + child: Stack( + children: [ + Align( + child: Lottie.asset(MyLottieConsts.marathonWaiting, height: 200), + ), + Align( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + LocaleKeys.startingIn.tr().toText16(), + provider.totalSecondsToWaitForMarathon.toString().toText18(color: provider.totalSecondsToWaitForMarathon < 5 ? MyColors.redColor : MyColors.black), + ], + ), + ), + Align( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: () { + provider.callNextQuestionApi(); + provider.timerToWaitForMarathon.cancel(); + }, + child: "Join Now".toText16(), + ).paddingOnly(bottom: 20), + ], + ), ), - ), - ], - ), - ).expanded, - ], + ], + ), + ).expanded, + ], + ), ), ); } diff --git a/lib/ui/marathon/marathon_winner_selection.dart b/lib/ui/marathon/marathon_winner_selection.dart deleted file mode 100644 index ed49f04..0000000 --- a/lib/ui/marathon/marathon_winner_selection.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; -import 'package:lottie/lottie.dart'; -import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/classes/lottie_consts.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/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; -import 'package:mohem_flutter_app/ui/marathon/widgets/custom_status_widget.dart'; -import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_qualifiers_container.dart'; -import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; -import 'package:provider/provider.dart'; - -class MarathonWinnerSelection extends StatelessWidget { - const MarathonWinnerSelection({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - MarathonProvider provider = context.read(); - return Scaffold( - appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()), - body: SingleChildScrollView( - child: Column( - children: [ - 20.height, - QualifiersContainer(provider: provider,).paddingOnly(left: 21, right: 21), - 12.height, - InkWell( - onTap: () { - Navigator.pushNamed(context, AppRoutes.marathonWinnerScreen); - }, - child: Container( - height: 50, - decoration: BoxDecoration( - color: MyColors.greenColor, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: const Color(0xff000000).withOpacity(.05), - blurRadius: 26, - offset: const Offset(0, -3), - ), - ], - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Muhammad Shrouff".toText17(isBold: true, color: MyColors.white), - "837436".toText17(isBold: true, color: MyColors.white), - ], - ), - ), - ).paddingOnly(left: 20, right: 20), - ), - CustomStatusWidget( - asset: Lottie.asset( - MyLottieConsts.winnerLottie, - height: 168, - ), - title: Text( - LocaleKeys.fingersCrossed.tr(), - style: const TextStyle( - height: 27 / 27, - color: MyColors.greenColor, - fontSize: 27, - letterSpacing: -1.08, - fontWeight: FontWeight.w600, - ), - ), - subTitle: Text( - LocaleKeys.winnerSelectedRandomly.tr(), - textAlign: TextAlign.center, - style: const TextStyle( - color: MyColors.darkTextColor, - fontSize: 18, - letterSpacing: -0.72, - fontWeight: FontWeight.w600, - ), - )).paddingOnly(left: 21, right: 21, top: 20, bottom: 20), - ], - ), - ), - ); - } -} diff --git a/lib/ui/marathon/widgets/countdown_timer.dart b/lib/ui/marathon/widgets/countdown_timer.dart index cfa3b1b..3a33697 100644 --- a/lib/ui/marathon/widgets/countdown_timer.dart +++ b/lib/ui/marathon/widgets/countdown_timer.dart @@ -8,6 +8,7 @@ import 'package:flutter_countdown_timer/current_remaining_time.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; class BuildCountdownTimer extends StatelessWidget { @@ -29,9 +30,10 @@ class BuildCountdownTimer extends StatelessWidget { letterSpacing: -0.4, ); - final TextStyle styleDigitHome = const TextStyle( + final TextStyle styleDigitHome = TextStyle( height: 22 / 27, color: MyColors.white, + fontSize: isTablet ? 30 : 15, fontStyle: FontStyle.italic, letterSpacing: -1.44, fontWeight: FontWeight.bold, @@ -53,6 +55,33 @@ class BuildCountdownTimer extends StatelessWidget { fontWeight: FontWeight.bold, ); + Widget buildSeparator() { + return AutoSizeText( + " : ", + maxFontSize: 24, + minFontSize: 20, + style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, + ); + } + + Widget getTimeDigit(String text) { + return AutoSizeText( + text, + maxFontSize: 24, + minFontSize: 20, + style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, + ); + } + + Widget getTimeText(String text) { + return AutoSizeText( + text, + minFontSize: 7, + maxFontSize: 8, + style: screenFlag == 0 ? styleTextHome : styleTextMarathon, + ); + } + Widget buildEmptyWidget() { return Directionality( textDirection: ui.TextDirection.ltr, @@ -63,70 +92,29 @@ class BuildCountdownTimer extends StatelessWidget { children: [ Column( children: [ - // todo @faiz: Make a separate method and pass string , so we can minimize code replication - AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.days.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + getTimeDigit("00"), + getTimeText(LocaleKeys.days.tr()), ], ), buildSeparator(), Column( children: [ - AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.hours.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + getTimeDigit("00"), + getTimeText(LocaleKeys.hours.tr()), ], ), buildSeparator(), Column( children: [ - AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.minutes.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + getTimeDigit("00"), + getTimeText(LocaleKeys.minutes.tr()), ], ), buildSeparator(), Column( children: [ - AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.seconds.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + getTimeDigit("00"), + getTimeText(LocaleKeys.seconds.tr()), ], ), ], @@ -134,15 +122,6 @@ class BuildCountdownTimer extends StatelessWidget { ); } - Widget buildSeparator() { - return AutoSizeText( - " : ", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ); - } - Widget buildCountdownTimer(CurrentRemainingTime? time) { if (time == null) { if (!provider.itsMarathonTime) { @@ -162,98 +141,29 @@ class BuildCountdownTimer extends StatelessWidget { children: [ Column( children: [ - // todo @faiz: Make a separate method and pass value and string , so we can minimize code replication - time.days == null - ? AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ) - : AutoSizeText( - time.days! < 10 ? "0${time.days.toString()}" : time.days.toString(), - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.days.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + time.days == null ? getTimeDigit("00") : getTimeDigit(time.days! < 10 ? "0${time.days.toString()}" : time.days.toString()), + getTimeText(LocaleKeys.days.tr()), ], ), buildSeparator(), Column( children: [ - time.hours == null - ? AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ) - : AutoSizeText( - time.hours! < 10 ? "0${time.hours.toString()}" : time.hours.toString(), - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.hours.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + time.hours == null ? getTimeDigit("00") : getTimeDigit(time.hours! < 10 ? "0${time.hours.toString()}" : time.hours.toString()), + getTimeText(LocaleKeys.hours.tr()), ], ), buildSeparator(), Column( children: [ - time.min == null - ? AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ) - : AutoSizeText( - time.min! < 10 ? "0${time.min.toString()}" : time.min.toString(), - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.minutes.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + time.min == null ? getTimeDigit("00") : getTimeDigit(time.min! < 10 ? "0${time.min.toString()}" : time.min.toString()), + getTimeText(LocaleKeys.minutes.tr()), ], ), buildSeparator(), Column( children: [ - time.sec == null - ? AutoSizeText( - "00", - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ) - : AutoSizeText( - time.sec! < 10 ? "0${time.sec.toString()}" : time.sec.toString(), - maxFontSize: 24, - minFontSize: 20, - style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, - ), - AutoSizeText( - LocaleKeys.seconds.tr(), - minFontSize: 7, - maxFontSize: 8, - style: screenFlag == 0 ? styleTextHome : styleTextMarathon, - ), + time.sec == null ? getTimeDigit("00") : getTimeDigit(time.sec! < 10 ? "0${time.sec.toString()}" : time.sec.toString()), + getTimeText(LocaleKeys.seconds.tr()), ], ), ], diff --git a/lib/ui/marathon/widgets/marathon_banner.dart b/lib/ui/marathon/widgets/marathon_banner.dart index d805362..0487c74 100644 --- a/lib/ui/marathon/widgets/marathon_banner.dart +++ b/lib/ui/marathon/widgets/marathon_banner.dart @@ -1,16 +1,18 @@ import 'dart:math' as math; -import 'package:auto_size_text/auto_size_text.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.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/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/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart'; import 'package:provider/provider.dart'; @@ -20,182 +22,362 @@ class MarathonBanner extends StatelessWidget { const MarathonBanner({Key? key, required this.isMarathonUpcoming}) : super(key: key); - @override - Widget build(BuildContext context) { - MarathonProvider provider = context.read(); - return provider.marathonDetailModel.startTime != null - ? Container( - decoration: MyDecorations.shadowDecoration, - height: MediaQuery.of(context).size.height * 0.11, - clipBehavior: Clip.antiAlias, - child: Stack( - children: [ - Transform( - alignment: Alignment.center, - transform: Matrix4.rotationY( - AppState().isArabic(context) ? math.pi : 0, + Widget getUnPrivilegedMarathon(BuildContext context) { + return Container( + decoration: MyDecorations.shadowDecoration, + height: isTablet ? MediaQuery.of(context).size.height * 0.17 : MediaQuery.of(context).size.height * 0.11, + clipBehavior: Clip.antiAlias, + child: Stack( + children: [ + Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY( + AppState().isArabic(context) ? math.pi : 0, + ), + child: SvgPicture.asset( + "assets/images/marathon_banner_bg.svg", + fit: BoxFit.fill, + width: double.infinity, + ), + ), + AppState().isArabic(context) + ? Positioned( + right: -15, + top: -10, + child: Transform.rotate( + angle: 10, + child: Container( + width: isTablet ? 70 : 65, + height: isTablet ? 40 : 32, + color: MyColors.darkDigitColor, + ), + ), + ) + : Positioned( + left: -20, + top: -10, + child: Transform.rotate( + angle: 15, + child: Container( + width: isTablet ? 70 : 65, + height: isTablet ? 40 : 32, + color: MyColors.darkDigitColor, + ), ), - child: SvgPicture.asset( - "assets/images/marathon_banner_bg.svg", - fit: BoxFit.fill, + ), + SizedBox( + width: double.infinity, + height: double.infinity, + child: Row( + children: [ + const Expanded( + flex: 3, + child: SizedBox( width: double.infinity, + height: double.infinity, ), ), - AppState().isArabic(context) - ? Positioned( - right: -15, - top: -10, - child: Transform.rotate( - angle: 10, - child: Container( - width: 65, - height: 32, - color: MyColors.darkDigitColor, - ), - ), - ) - : Positioned( - left: -20, - top: -10, - child: Transform.rotate( - angle: 15, - child: Container( - width: 65, - height: 32, - color: MyColors.darkDigitColor, - ), + Expanded( + flex: AppState().isArabic(context) ? 4 : 5, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AppState().isArabic(context) ? 0.height : 5.height, + Text( + LocaleKeys.getReadyForContest.tr(), + style: TextStyle( + fontSize: isTablet ? 20 : 11, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.w600, + color: MyColors.white.withOpacity(0.83), + letterSpacing: -0.4, + ), + ), + Text( + LocaleKeys.brainMarathon.tr(), + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: isTablet ? 30 : 19, + fontWeight: FontWeight.bold, + color: MyColors.white.withOpacity(0.83), + height: 32 / 22, + ), + ), + ], + ).paddingOnly( + left: AppState().isArabic(context) ? 12 : 3, + right: AppState().isArabic(context) ? 3 : 12, + ) + ], + ), + ), + ), + ], + ), + ), + AppState().isArabic(context) + ? Align( + alignment: Alignment.topRight, + child: SizedBox( + height: isTablet ? 30 : 20, + width: isTablet ? 45 : 35, + child: Transform.rotate( + angle: math.pi / 4.5, + child: Text( + LocaleKeys.brainMarathon.tr(), + textAlign: TextAlign.center, + maxLines: 2, + style: TextStyle( + color: MyColors.white, + fontWeight: FontWeight.bold, + fontSize: isTablet ? 8 : 6, + height: 1.2, ), ), - SizedBox( - width: double.infinity, - height: double.infinity, - child: Row( - children: [ - const Expanded( - flex: 3, - child: SizedBox( - width: double.infinity, - height: double.infinity, + ), + ), + ).paddingOnly(top: 5) + : Align( + alignment: Alignment.topLeft, + child: SizedBox( + height: isTablet ? 30 : 20, + width: isTablet ? 45 : 35, + child: Transform.rotate( + angle: -math.pi / 4.5, + child: Text( + LocaleKeys.brainMarathon.tr(), + textAlign: TextAlign.center, + maxLines: 2, + style: TextStyle( + color: MyColors.kWhiteColor, + fontWeight: FontWeight.bold, + fontSize: isTablet ? 8 : 6, + height: 1.2, ), ), - Expanded( - flex: AppState().isArabic(context) ? 4 : 5, - child: SizedBox( - width: double.infinity, - height: double.infinity, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + ), + ), + ).paddingOnly(top: 5), + Container( + height: double.infinity, + width: double.infinity, + color: Colors.black.withOpacity(0.6), + child: const Icon( + Icons.lock_rounded, + color: MyColors.lightGreyIconColor, + ), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + MarathonProvider provider = context.read(); + + return !provider.isPrivilegedWithMarathon + ? getUnPrivilegedMarathon(context) + : provider.marathonDetailModel.startTime != null + ? Container( + decoration: MyDecorations.shadowDecoration, + height: isTablet ? MediaQuery.of(context).size.height * 0.17 : MediaQuery.of(context).size.height * 0.11, + clipBehavior: Clip.antiAlias, + child: Stack( + children: [ + Transform( + alignment: Alignment.center, + transform: Matrix4.rotationY( + AppState().isArabic(context) ? math.pi : 0, + ), + child: SvgPicture.asset( + "assets/images/marathon_banner_bg.svg", + fit: BoxFit.fill, + width: double.infinity, + ), + ), + AppState().isArabic(context) + ? Positioned( + right: -15, + top: -10, + child: Transform.rotate( + angle: 10, + child: Container( + width: isTablet ? 70 : 65, + height: isTablet ? 40 : 32, + color: MyColors.darkDigitColor, + ), + ), + ) + : Positioned( + left: -20, + top: -10, + child: Transform.rotate( + angle: 15, + child: Container( + width: isTablet ? 70 : 65, + height: isTablet ? 40 : 32, + color: MyColors.darkDigitColor, + ), + ), + ), + SizedBox( + width: double.infinity, + height: double.infinity, + child: Row( + children: [ + const Expanded( + flex: 3, + child: SizedBox( + width: double.infinity, + height: double.infinity, + ), + ), + Expanded( + flex: AppState().isArabic(context) ? 4 : 5, + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, children: [ - AppState().isArabic(context) ? 0.height : 5.height, - AutoSizeText( - LocaleKeys.getReadyForContest.tr(), - minFontSize: 08, - maxFontSize: 11, - style: TextStyle( - fontStyle: FontStyle.italic, - fontWeight: FontWeight.w600, - color: MyColors.white.withOpacity(0.83), - letterSpacing: -0.4, - ), - ), - AutoSizeText( - AppState().isArabic(context) ? provider.marathonDetailModel.titleAr ?? "" : provider.marathonDetailModel.titleEn ?? "", - style: TextStyle( - fontStyle: FontStyle.italic, - fontSize: 19, - fontWeight: FontWeight.bold, - color: MyColors.white.withOpacity(0.83), - height: 32 / 22, - ), - ), - 3.height, - BuildCountdownTimer( - timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch, - provider: provider, - screenFlag: 0, - ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AppState().isArabic(context) ? 0.height : 5.height, + Text( + LocaleKeys.getReadyForContest.tr(), + style: TextStyle( + fontSize: isTablet ? 20 : 11, + fontStyle: FontStyle.italic, + fontWeight: FontWeight.w600, + color: MyColors.white.withOpacity(0.83), + letterSpacing: -0.4, + ), + ), + Text( + AppState().isArabic(context) ? provider.marathonDetailModel.titleAr ?? "" : provider.marathonDetailModel.titleEn ?? "", + style: TextStyle( + fontStyle: FontStyle.italic, + fontSize: isTablet ? 30 : 19, + fontWeight: FontWeight.bold, + color: MyColors.white.withOpacity(0.83), + height: 32 / 22, + ), + ), + isTablet ? 10.height : 3.height, + BuildCountdownTimer( + timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch, + provider: provider, + screenFlag: 0, + ), + ], + ).paddingOnly( + left: AppState().isArabic(context) ? 12 : 3, + right: AppState().isArabic(context) ? 3 : 12, + ) ], - ).paddingOnly( - left: AppState().isArabic(context) ? 12 : 3, - right: AppState().isArabic(context) ? 3 : 12, - ) - ], + ), + ), ), - ), + ], ), - ], - ), - ), - AppState().isArabic(context) - ? Align( - alignment: Alignment.topRight, - child: SizedBox( - height: 20, - width: 35, - child: Transform.rotate( - angle: math.pi / 4.5, - child: Text( - LocaleKeys.brainMarathon.tr(), - textAlign: TextAlign.center, - maxLines: 2, - style: const TextStyle( - color: MyColors.white, - fontWeight: FontWeight.bold, - fontSize: 6, - height: 1.2, + ), + AppState().isArabic(context) + ? Align( + alignment: Alignment.topRight, + child: SizedBox( + height: isTablet ? 30 : 20, + width: isTablet ? 45 : 35, + child: Transform.rotate( + angle: math.pi / 4.5, + child: Text( + LocaleKeys.brainMarathon.tr(), + textAlign: TextAlign.center, + maxLines: 2, + style: TextStyle( + color: MyColors.white, + fontWeight: FontWeight.bold, + fontSize: isTablet ? 8 : 6, + height: 1.2, + ), + ), ), ), - ), - ), - ).paddingOnly(top: 5) - : Align( - alignment: Alignment.topLeft, - child: SizedBox( - height: 20, - width: 35, - child: Transform.rotate( - angle: -math.pi / 4.5, - child: Text( - LocaleKeys.brainMarathon.tr(), - textAlign: TextAlign.center, - maxLines: 2, - style: const TextStyle( - color: MyColors.kWhiteColor, - fontWeight: FontWeight.bold, - fontSize: 6, - height: 1.2, + ).paddingOnly(top: 5) + : Align( + alignment: Alignment.topLeft, + child: SizedBox( + height: isTablet ? 30 : 20, + width: isTablet ? 45 : 35, + child: Transform.rotate( + angle: -math.pi / 4.5, + child: Text( + LocaleKeys.brainMarathon.tr(), + textAlign: TextAlign.center, + maxLines: 2, + style: TextStyle( + color: MyColors.kWhiteColor, + fontWeight: FontWeight.bold, + fontSize: isTablet ? 8 : 6, + height: 1.2, + ), + ), ), ), + ).paddingOnly(top: 5), + !AppState().isArabic(context) + ? Positioned( + right: 0, + bottom: 0, + child: RotatedBox( + quarterTurns: 4, + child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), + ).paddingAll(isTablet ? 20 : 15), + ) + : Positioned( + bottom: 0, + left: 0, + child: RotatedBox( + quarterTurns: 2, + child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), + ).paddingAll(isTablet ? 20 : 15), ), - ), - ).paddingOnly(top: 5), - !AppState().isArabic(context) - ? Positioned( - right: 0, - bottom: 0, - child: RotatedBox( - quarterTurns: 4, - child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), - ).paddingAll(15), - ) - : Positioned( - bottom: 0, - left: 0, - child: RotatedBox( - quarterTurns: 2, - child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), - ).paddingAll(15), - ), - ], - ).onPress( - () => Navigator.pushNamed(context, AppRoutes.marathonIntroScreen), - ), - ) - : const SizedBox(); + + ], + ).onPress(() async { + int remainingTimeInMinutes = DateTime.parse(provider.marathonDetailModel.startTime!).difference(DateTime.now()).inMinutes; + + if (remainingTimeInMinutes > 2) { + Utils.showLoading(context); + try { + await provider.initializeVideoPlayer().then((_) { + Utils.hideLoading(context); + provider.startTimerForSponsorVideo(); + Navigator.pushNamed(context, AppRoutes.marathonSponsorVideoScreen); + }); + } catch (e) { + if (kDebugMode) { + print("Error in VideoPlayer: ${e.toString()}"); + } + Utils.hideLoading(context); + Navigator.pushNamed(context, AppRoutes.marathonIntroScreen); + } + } else { + Navigator.pushNamed(context, AppRoutes.marathonIntroScreen); + } + }), + ) + : const SizedBox(); } } diff --git a/lib/ui/marathon/widgets/marathon_qualifiers_container.dart b/lib/ui/marathon/widgets/marathon_qualifiers_container.dart index 0308027..dc9d7e4 100644 --- a/lib/ui/marathon/widgets/marathon_qualifiers_container.dart +++ b/lib/ui/marathon/widgets/marathon_qualifiers_container.dart @@ -20,6 +20,7 @@ class _QualifiersContainerState extends State { @override void initState() { widget.provider.startTimerForWinnerSelection(); + widget.provider.callGetSelectedWinnersApi(); super.initState(); } @@ -36,13 +37,14 @@ class _QualifiersContainerState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor), - "00:${widget.provider.totalSecondsToWaitForWinner < 10 ? "0${widget.provider.totalSecondsToWaitForWinner}" : widget.provider.totalSecondsToWaitForWinner}".toText18(color: MyColors.redColor), + "00:${widget.provider.totalSecondsToWaitForWinner < 10 ? "0${widget.provider.totalSecondsToWaitForWinner}" : widget.provider.totalSecondsToWaitForWinner}" + .toText18(color: MyColors.redColor), ], ), 10.height, Row( children: [ - widget.provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true), + widget.provider.totalQualifiers != null ? widget.provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true) : const SizedBox(), 2.width, LocaleKeys.qualifiers.tr().toText16(color: MyColors.greenColor), ], diff --git a/lib/ui/marathon/widgets/question_card.dart b/lib/ui/marathon/widgets/question_card.dart index 4009983..5f419c5 100644 --- a/lib/ui/marathon/widgets/question_card.dart +++ b/lib/ui/marathon/widgets/question_card.dart @@ -1,4 +1,5 @@ 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:lottie/lottie.dart'; @@ -6,38 +7,35 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.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/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:provider/provider.dart'; class QuestionCard extends StatelessWidget { - final MarathonProvider provider; - - const QuestionCard({Key? key, required this.provider}) : super(key: key); + const QuestionCard({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + MarathonProvider provider = context.read(); return CupertinoPageScaffold( child: provider.cardContentList.isEmpty ? Lottie.asset(MyLottieConsts.hourGlassLottie, height: 250).paddingOnly(top: 50) : SizedBox( height: 440, width: double.infinity, - child: Consumer( - builder: (BuildContext context, MarathonProvider provider, _) { - return AppinioSwiper( - duration: const Duration(milliseconds: 400), - padding: EdgeInsets.zero, - isDisabled: true, - controller: provider.swiperController, - unswipe: (int index, AppinioSwiperDirection direction) {}, - onSwipe: (int index, AppinioSwiperDirection direction) {}, - cards: provider.cardContentList, - ); - }, + child: AppinioSwiper( + duration: const Duration(milliseconds: 400), + padding: EdgeInsets.zero, + isDisabled: true, + controller: provider.swiperController, + unswipe: (int index, AppinioSwiperDirection direction) {}, + onSwipe: (int index, AppinioSwiperDirection direction) {}, + cards: provider.cardContentList, ), ), ); @@ -163,7 +161,7 @@ class AnswerTileForText extends StatelessWidget { MarathonProvider provider = context.watch(); return InkWell( onTap: () { - provider.isUserOutOfGame ? null : onAnswerTapped() ; + provider.isUserOutOfGame ? Utils.showToast(LocaleKeys.youAreOutOfContest.tr()) : onAnswerTapped(); }, child: Container( alignment: Alignment.centerLeft, diff --git a/lib/ui/misc/request_submit_screen.dart b/lib/ui/misc/request_submit_screen.dart index f05bacb..170e288 100644 --- a/lib/ui/misc/request_submit_screen.dart +++ b/lib/ui/misc/request_submit_screen.dart @@ -8,6 +8,7 @@ import 'package:mohem_flutter_app/api/leave_balance_api_client.dart'; import 'package:mohem_flutter_app/api/my_attendance_api_client.dart'; import 'package:mohem_flutter_app/api/profile_api_client.dart'; import 'package:mohem_flutter_app/api/termination_dff_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/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; @@ -35,7 +36,8 @@ class RequestSubmitScreenParams { } class RequestSubmitScreen extends StatefulWidget { - RequestSubmitScreen({Key? key}) : super(key: key); + final String selectedEmp; + RequestSubmitScreen({this.selectedEmp ='',Key? key}) : super(key: key); @override _RequestSubmitScreenState createState() { @@ -50,6 +52,8 @@ class _RequestSubmitScreenState extends State { List approverList = []; List attachmentFiles = []; List attachments = []; + dynamic dynamicParams; + String selectedEmp =""; @override void initState() { @@ -123,6 +127,7 @@ class _RequestSubmitScreenState extends State { params!.transactionId, comments.text, -999, + empID: widget.selectedEmp ); } else if (params!.approvalFlag == 'eit') { await MyAttendanceApiClient().startEitApprovalProcess( 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 e84a9a8..228f1d6 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart @@ -627,7 +627,12 @@ class _DynamicInputScreenState extends State { ).paddingOnly(bottom: 12), itemBuilder: (_) => >[ if (model.rEADONLY != "Y") - for (int i = 0; i < model.eSERVICESVS!.length; i++) PopupMenuItem(child: Text(model.eSERVICESVS![i].vALUECOLUMNNAME!), value: i), + for (int i = 0; i < model.eSERVICESVS!.length; i++) PopupMenuItem(value: i, child: Column( + children: [ + Text(model.eSERVICESVS![i].vALUECOLUMNNAME!), + const PopupMenuDivider(), + ], + )), ], onSelected: (int popipIndex) async { ESERVICESDV eservicesdv = ESERVICESDV( diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart index 5c4053c..d1c21e1 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart @@ -91,7 +91,7 @@ class _DynamicListViewScreenState extends State { padding: const EdgeInsets.all(21), children: [ // HMG_TKT_NEW_EIT_SS Id used for ticket balance dashboard - if (isTicketRequest) ...[const BalancesDashboardWidget("Current Ticket Balance", false), 12.height], + if (isTicketRequest) ...[const BalancesDashboardWidget("Current Ticket Balance", false, ""), 12.height], getEITTransactionList == null ? const SizedBox() : (getEITTransactionList!.isEmpty @@ -102,8 +102,8 @@ class _DynamicListViewScreenState extends State { itemBuilder: (cxt, int parentIndex) => getEITTransactionList![parentIndex].collectionTransaction!.isEmpty ? const SizedBox() : GridView( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 1.9 / 0.75), - padding: const EdgeInsets.all(12.0), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 1.0 / 0.75), + padding: const EdgeInsets.only(left: 12,right: 21,top: 12), shrinkWrap: true, primary: false, physics: const ScrollPhysics(), diff --git a/lib/ui/my_attendance/services_menu_list_screen.dart b/lib/ui/my_attendance/services_menu_list_screen.dart index cd76132..7a61029 100644 --- a/lib/ui/my_attendance/services_menu_list_screen.dart +++ b/lib/ui/my_attendance/services_menu_list_screen.dart @@ -54,7 +54,7 @@ class ServicesMenuListScreen extends StatelessWidget { } return; } else if (servicesMenuData.list[index].requestType == "ABSENCE") { - Navigator.pushNamed(context, AppRoutes.leaveBalance); + Navigator.pushNamed(context, AppRoutes.leaveBalance, arguments: servicesMenuData?.selectedEmp ??''); return; } if (servicesMenuData.list[index].requestType == "EIT") { diff --git a/lib/ui/my_team/create_request.dart b/lib/ui/my_team/create_request.dart index 53205c9..2b3e063 100644 --- a/lib/ui/my_team/create_request.dart +++ b/lib/ui/my_team/create_request.dart @@ -65,14 +65,14 @@ class _CreateRequestState extends State { return menus; } - void handleOnPress(context, Menus menu) { - + void handleOnPress(context, Menus menu) { if (menu.menuEntry.menuEntryType == "FUNCTION") { if (menu.menuEntry.requestType == "EIT") { - Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(menu.menuEntry.prompt!, menu.menuEntry.functionName!)); + Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(menu.menuEntry.prompt!, menu.menuEntry.functionName!, selectedEmp: getEmployeeSubordinates?.eMPLOYEENUMBER ?? '')); } else {} } else { - Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(menu.menuEntry.prompt!, menu.menuEntiesList, selectedEmp: getEmployeeSubordinates?.eMPLOYEENUMBER??'')); + Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, + arguments: ServicesMenuListScreenParams(menu.menuEntry.prompt!, menu.menuEntiesList, selectedEmp: getEmployeeSubordinates?.eMPLOYEENUMBER ?? '')); } return; } diff --git a/lib/ui/profile/contact_details.dart b/lib/ui/profile/contact_details.dart index 78afc1c..749978e 100644 --- a/lib/ui/profile/contact_details.dart +++ b/lib/ui/profile/contact_details.dart @@ -160,7 +160,7 @@ class _ContactDetailsState extends State { right: 1, child: const Icon(Icons.add_location_alt_outlined, size: 20).onPress(continueDynamicForms), ), - Utils.getNoDataWidget(context).expanded, + Utils.getNoDataWidget(context), ], ).objectContainerView() ], @@ -177,13 +177,13 @@ class _ContactDetailsState extends State { void addUpdateAddress() { Widget cancelButton = TextButton( - child: Text("Cancel"), + child: Text(LocaleKeys.cancel.tr()), onPressed: () { Navigator.pop(context); }, ); Widget continueButton = TextButton( - child: Text("Next"), + child: Text(LocaleKeys.next.tr()), onPressed: () { continueDynamicForms(); }, @@ -191,7 +191,7 @@ class _ContactDetailsState extends State { StatefulBuilder alert = StatefulBuilder(builder: (context, setState) { return AlertDialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10.0))), - title: Text("Confirm"), + title: Text(LocaleKeys.confirm.tr()), content: Builder(builder: (context) { // Get available height and width of the build area of this widget. Make a choice depending on the size. var height = MediaQuery.of(context).size.height * .5; @@ -199,14 +199,14 @@ class _ContactDetailsState extends State { height: height, child: Column(children: [ Text( - "Select the type of change you want to make.", + LocaleKeys.SelectChangeWantToMake.tr(), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), Divider(), Column( children: [ ListTile( - title: Text("Correct or amend this address"), + title: Text(LocaleKeys.CorrectAddress.tr()), leading: Radio( value: 1, groupValue: correctOrNew, @@ -219,7 +219,7 @@ class _ContactDetailsState extends State { ), ), ListTile( - title: Text("Enter a new address if you have moved"), + title: Text(LocaleKeys.EnterNewAddressMoved.tr()), leading: Radio( value: 2, groupValue: correctOrNew, diff --git a/lib/ui/screens/announcements/announcement_details.dart b/lib/ui/screens/announcements/announcement_details.dart index dbe1eb2..210a8f7 100644 --- a/lib/ui/screens/announcements/announcement_details.dart +++ b/lib/ui/screens/announcements/announcement_details.dart @@ -60,7 +60,7 @@ class _AnnouncementDetailsState extends State { ), ), Html( - data: getAnnouncementDetailsObj?.bodyEN, + data: AppState().isArabic(context) ? getAnnouncementDetailsObj?.bodyAR : getAnnouncementDetailsObj?.bodyEN, ).paddingOnly(top: 12), ], ).objectContainerView().paddingAll(21), diff --git a/lib/ui/work_list/worklist_detail_screen.dart b/lib/ui/work_list/worklist_detail_screen.dart index 8b22041..b5129a5 100644 --- a/lib/ui/work_list/worklist_detail_screen.dart +++ b/lib/ui/work_list/worklist_detail_screen.dart @@ -106,6 +106,19 @@ class _WorkListDetailScreenState extends State { void getData() async { // try { // Utils.showLoading(context); + + getEitCollectionNotificationBodyList!.clear(); + getAbsenceCollectionNotificationBodyList!.clear(); + getPrNotificationBody = null; + getStampMsNotifications.clear(); + getStampNsNotifications.clear(); + getPoNotificationBody?.pOHeader!.clear(); + getItemCreationNtfBody?.itemCreationHeader!.clear(); + getPhonesNotificationBodyList!.clear(); + getBasicDetNtfBodyList!.clear(); + getAbsenceCollectionNotificationBodyList!.clear(); + getContactNotificationBodyList = null; + if (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP") { getUserInformation(); } @@ -233,9 +246,9 @@ class _WorkListDetailScreenState extends State { ), child: Row( children: [ - (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP") ? myTab(LocaleKeys.details.tr(), 0) : myTab(LocaleKeys.request.tr(), 0), - myTab(LocaleKeys.actions.tr(), 1), - myTab(LocaleKeys.info.tr(), 2), + myTab(LocaleKeys.info.tr(), 0), + (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP") ? myTab(LocaleKeys.details.tr(), 1) : myTab(LocaleKeys.request.tr(), 1), + myTab(LocaleKeys.actions.tr(), 2), myTab(LocaleKeys.attachments.tr(), 3), ], ), @@ -249,6 +262,19 @@ class _WorkListDetailScreenState extends State { }); }, children: [ + InfoFragment( + poHeaderList: getPoNotificationBody?.pOHeader ?? [], + workListData: workListData, + itemCreationHeader: getItemCreationNtfBody?.itemCreationHeader ?? [], + getStampMsNotifications: getStampMsNotifications, + getStampNsNotifications: getStampNsNotifications, + getEitCollectionNotificationBodyList: getEitCollectionNotificationBodyList, + getPhonesNotificationBodyList: getPhonesNotificationBodyList, + getBasicDetNtfBodyList: getBasicDetNtfBodyList, + getAbsenceCollectionNotificationBodyList: getAbsenceCollectionNotificationBodyList, + getContactNotificationBodyList: getContactNotificationBodyList, + getPrNotificationBodyList: getPrNotificationBody, + ), (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP") ? DetailFragment(workListData, memberInformationListModel) : RequestFragment( @@ -266,19 +292,6 @@ class _WorkListDetailScreenState extends State { voidCallback: reloadWorkList, ) : showLoadingAnimation(), - InfoFragment( - poHeaderList: getPoNotificationBody?.pOHeader ?? [], - workListData: workListData, - itemCreationHeader: getItemCreationNtfBody?.itemCreationHeader ?? [], - getStampMsNotifications: getStampMsNotifications, - getStampNsNotifications: getStampNsNotifications, - getEitCollectionNotificationBodyList: getEitCollectionNotificationBodyList, - getPhonesNotificationBodyList: getPhonesNotificationBodyList, - getBasicDetNtfBodyList: getBasicDetNtfBodyList, - getAbsenceCollectionNotificationBodyList: getAbsenceCollectionNotificationBodyList, - getContactNotificationBodyList: getContactNotificationBodyList, - getPrNotificationBodyList: getPrNotificationBody, - ), isAttachmentLoaded ? getAttachmentList.isEmpty ? Utils.getNoDataWidget(context) @@ -688,7 +701,7 @@ class _WorkListDetailScreenState extends State { GenericResponseModel model = await WorkListApiClient().postNotificationActions(payload); Utils.hideLoading(context); Utils.showToast(LocaleKeys.yourChangeHasBeenSavedSuccessfully.tr()); - animationIndex=animationIndex+1; + animationIndex = animationIndex + 1; AppState().workList!.removeAt(AppState().workListIndex!); if (AppState().workList!.isEmpty) { Navigator.pop(context, "delegate_reload"); @@ -725,6 +738,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + getEitCollectionNotificationBodyList!.clear(); getEitCollectionNotificationBodyList = await WorkListApiClient().GetEitNotificationBody(workListData!.nOTIFICATIONID); apiCallCount--; if (apiCallCount == 0) { @@ -742,6 +756,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + memberInformationListModel = null; memberInformationListModel = await WorkListApiClient().getUserInformation(-999, workListData!.sELECTEDEMPLOYEENUMBER!); apiCallCount--; if (apiCallCount == 0) { @@ -759,6 +774,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + getPhonesNotificationBodyList!.clear(); getPhonesNotificationBodyList = await WorkListApiClient().getPhonesNotificationBodyList(workListData!.nOTIFICATIONID); apiCallCount--; if (apiCallCount == 0) { @@ -776,6 +792,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + getBasicDetNtfBodyList!.clear(); getBasicDetNtfBodyList = await WorkListApiClient().getBasicDetNtfBodyList(workListData!.nOTIFICATIONID); apiCallCount--; if (apiCallCount == 0) { @@ -793,6 +810,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + getAbsenceCollectionNotificationBodyList!.clear(); getAbsenceCollectionNotificationBodyList = await WorkListApiClient().getAbsenceNotificationBody(workListData!.nOTIFICATIONID); apiCallCount--; if (apiCallCount == 0) { @@ -916,6 +934,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + getNotificationRespondAttributes.clear(); getNotificationRespondAttributes = await WorkListApiClient().notificationGetRespondAttributes(workListData!.nOTIFICATIONID!); if (getNotificationRespondAttributes.isNotEmpty) { notificationNoteInput = getNotificationRespondAttributes.first; @@ -936,6 +955,7 @@ class _WorkListDetailScreenState extends State { try { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; + notificationButtonsList.clear(); notificationButtonsList = await WorkListApiClient().getNotificationButtons(workListData!.nOTIFICATIONID!); if (notificationButtonsList.isNotEmpty) { isCloseAvailable = notificationButtonsList.any((element) => element.bUTTONACTION == "CLOSE"); diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart index 199074a..8bca3f3 100644 --- a/lib/widgets/app_bar_widget.dart +++ b/lib/widgets/app_bar_widget.dart @@ -5,25 +5,20 @@ 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, bool showHomeButton = true, bool showNotificationButton = false, bool showMemberButton = false, - String? image, List? actions, void Function()? onHomeTapped, void Function()? onBackTapped}) { return AppBar( leadingWidth: 0, - // leading: GestureDetector( - // behavior: HitTestBehavior.opaque, - // onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context), - // child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), - // ), - //titleSpacing: -1.44, title: Row( children: [ GestureDetector( @@ -34,14 +29,6 @@ AppBar AppBarWidget(BuildContext context, child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), ), 4.width, - if (image != null) - CircularAvatar( - url: image, - height: 40, - width: 40, - isImageBase64: true, - ), - if (image != null) 14.width, title.toText24(color: MyColors.darkTextColor, isBold: true).expanded, ], ), diff --git a/lib/widgets/balances_dashboard_widget.dart b/lib/widgets/balances_dashboard_widget.dart index e546acf..9689de4 100644 --- a/lib/widgets/balances_dashboard_widget.dart +++ b/lib/widgets/balances_dashboard_widget.dart @@ -31,8 +31,9 @@ class BalancesDashboardWidget extends StatefulWidget { final String title; final List chartModelList; final bool isLeaveBalance; + final String selectedEmp; - const BalancesDashboardWidget(this.title, this.isLeaveBalance, {Key? key, this.chartModelList = const []}) : super(key: key); + const BalancesDashboardWidget(this.title, this.isLeaveBalance, this.selectedEmp, {Key? key, this.chartModelList = const []}) : super(key: key); @override _BalancesDashboardWidgetState createState() { @@ -45,11 +46,14 @@ class _BalancesDashboardWidgetState extends State { late DateTime accrualDateTime; GetAccrualBalancesList? leaveBalanceAccrual; List? ticketBalanceAccrualList; + dynamic dynamicParams; + String selectedEmp = ""; @override void initState() { super.initState(); accrualDateTime = DateTime.now(); + changeAccrualDate(); } @override @@ -59,8 +63,8 @@ class _BalancesDashboardWidgetState extends State { void changeAccrualDate() async { try { - Utils.showLoading(context); - List accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(accrualDateTime)); + // Utils.showLoading(context); + List accrualList = await DashboardApiClient().getAccrualBalances(DateFormat("MM/dd/yyyy").format(accrualDateTime), empID: widget.selectedEmp); if (accrualList.isNotEmpty) { if (widget.isLeaveBalance) { leaveBalanceAccrual = accrualList[0]; @@ -76,7 +80,7 @@ class _BalancesDashboardWidgetState extends State { ]; } } - Utils.hideLoading(context); + // Utils.hideLoading(context); setState(() {}); } catch (ex) { Utils.hideLoading(context); diff --git a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart index 475c4a5..77501bc 100644 --- a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart +++ b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart @@ -20,6 +20,7 @@ import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/get_action_history_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/replacement_list_model.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; 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'; @@ -238,7 +239,7 @@ class _SearchEmployeeBottomSheetState extends State { Navigator.pushNamed( context, AppRoutes.chatDetailed, - arguments: {"targetUser": chatUsersList![index], "isNewChat": true}, + arguments: ChatDetailedScreenParams(chatUsersList![index], true), ); }, ), diff --git a/lib/widgets/chat_app_bar_widge.dart b/lib/widgets/chat_app_bar_widge.dart new file mode 100644 index 0000000..0feac6c --- /dev/null +++ b/lib/widgets/chat_app_bar_widge.dart @@ -0,0 +1,84 @@ +import 'package:animated_text_kit/animated_text_kit.dart'; +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/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, + {required String title, + bool showHomeButton = true, + String? image, + ChatUser? chatUser, + bool showTyping = false, + List? actions, + void Function()? onHomeTapped, + void Function()? onBackTapped}) { + return AppBar( + leadingWidth: 0, + title: Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: Feedback.wrapForTap(() { + (onBackTapped == null ? Navigator.maybePop(context) : onBackTapped()); + }, context), + child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), + ), + 4.width, + if (image != null) + CircularAvatar( + url: image, + height: 40, + width: 40, + isImageBase64: true, + ), + if (image != null) 14.width, + SizedBox( + height: 40, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title.toText20(color: MyColors.darkTextColor, isBold: true).expanded, + if (showTyping) + Consumer( + builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { + if (chatUser!.isTyping!) { + // return ("Typing ...").toText10(color: MyColors.textMixColor); + return AnimatedTextKit( + animatedTexts: [ + ScaleAnimatedText('Typing...', textStyle: const TextStyle(color: MyColors.textMixColor, fontSize: 10, letterSpacing: -0.4, fontStyle: FontStyle.normal)), + ], + ); + } else { + return const SizedBox(); + } + }, + ), + ], + ), + ) + ], + ), + centerTitle: false, + elevation: 0, + backgroundColor: Colors.white, + actions: [ + if (showHomeButton) + IconButton( + onPressed: () { + onHomeTapped == null ? Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)) : onHomeTapped(); + }, + icon: const Icon(Icons.home, color: MyColors.darkIconColor), + ), + ...actions ?? [] + ], + ); +} diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 69bf7e4..75466fc 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -45,7 +45,7 @@ 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', 'xls'], + 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); diff --git a/pubspec.yaml b/pubspec.yaml index 4fc00c8..2e3c4c2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -92,12 +92,14 @@ dependencies: swipe_to: ^1.0.2 flutter_webrtc: ^0.9.16 camera: ^0.10.0+4 + animated_text_kit: ^4.2.2 #Encryption flutter_des: ^2.1.0 video_player: ^2.4.7 just_audio: ^0.9.30 + safe_device: ^1.1.2 dev_dependencies: flutter_test: