Merge branch 'master' into development_sikander

# Conflicts:
#	lib/app_state/app_state.dart
merge-requests/125/head
Sikander Saleem 3 years ago
commit e4ab7e96d1

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#F15642;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M88.368,384c-4.096-2.304-6.656-6.912-4.096-12.288l36.72-71.744c3.456-6.784,12.656-7.04,15.856,0
l36.08,71.744c5.248,9.984-10.24,17.904-14.848,7.936l-5.632-11.248h-47.2l-5.52,11.248C97.712,384,92.992,384.912,88.368,384z
M143.392,351.52l-14.464-31.616l-15.744,31.616H143.392z"/>
<path style="fill:#FFFFFF;" d="M189.184,384c-4.096-2.304-6.656-6.912-4.096-12.288l36.704-71.744
c3.456-6.784,12.672-7.04,15.872,0l36.064,71.744c5.248,9.984-10.24,17.904-14.832,7.936l-5.648-11.248h-47.2l-5.504,11.248
C198.512,384,193.776,384.912,189.184,384z M244.192,351.52l-14.448-31.616l-15.728,31.616H244.192z"/>
<path style="fill:#FFFFFF;" d="M282.416,339.088c0-24.688,15.488-45.904,44.912-45.904c11.136,0,19.952,3.312,29.296,11.376
c3.456,3.184,3.84,8.832,0.384,12.4c-3.456,3.056-8.704,2.688-11.76-0.368c-5.248-5.504-10.624-7.024-17.92-7.024
c-19.696,0-29.168,13.936-29.168,29.536c0,15.872,9.344,30.464,29.168,30.464c7.296,0,14.08-2.96,19.952-8.192
c3.968-3.072,9.472-1.552,11.776,1.536c2.048,2.816,3.056,7.536-1.408,12.016c-8.96,8.336-19.696,9.984-30.336,9.984
C296.368,384.912,282.416,363.792,282.416,339.088z"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.616,14.4,32,32,32h320c17.6,0,32-14.384,32-32V128L352,0H128z
"/>
<path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
<polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
<path style="fill:#50BEE8;" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16
V416z"/>
<g>
<path style="fill:#FFFFFF;" d="M117.184,327.84v47.344c0,5.632-4.592,8.832-9.216,8.832c-4.096,0-7.664-3.2-7.664-8.832v-72.032
c0-6.64,5.632-8.832,7.664-8.832c3.712,0,5.888,2.192,8.064,4.608l28.16,38l29.152-39.408c4.24-5.248,14.592-3.2,14.592,5.632
v72.032c0,5.632-3.6,8.832-7.68,8.832c-4.592,0-8.192-3.2-8.192-8.832V327.84l-21.232,26.88c-4.592,5.632-10.352,5.632-14.576,0
L117.184,327.84z"/>
<path style="fill:#FFFFFF;" d="M210.288,303.152c0-4.224,3.328-8.832,8.704-8.832h29.552c16.64,0,31.616,11.136,31.616,32.496
c0,20.224-14.976,31.472-31.616,31.472h-21.36v16.896c0,5.632-3.584,8.832-8.192,8.832c-4.224,0-8.704-3.2-8.704-8.832V303.152z
M227.168,310.448v31.856h21.36c8.576,0,15.36-7.552,15.36-15.488c0-8.96-6.784-16.368-15.36-16.368L227.168,310.448
L227.168,310.448z"/>
<path style="fill:#FFFFFF;" d="M322.064,311.472h-21.872c-10.736,0-10.096-15.984,0-15.984h39.152c7.792,0,11.376,8.96,5.632,14.72
l-21.232,19.824c15.616-1.152,27.888,10.48,27.888,24.816c0,15.728-11.136,29.168-34.544,29.168
c-10.24,0-20.336-4.224-26.224-13.44c-6.144-9.072,7.024-17.776,13.936-8.832c3.328,4.352,8.704,6.528,14.448,6.528
c7.808,0,15.488-3.328,15.488-13.44c0-13.296-16.256-11.248-25.072-10.352c-10.752,2.048-13.936-9.6-7.664-14.448L322.064,311.472z
"/>
</g>
<path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -514,5 +514,7 @@
"startingIn": "يبدأ في", "startingIn": "يبدأ في",
"youAreOutOfContest": "أنت خارج المسابقة.", "youAreOutOfContest": "أنت خارج المسابقة.",
"winners": "الفائزين!!!", "winners": "الفائزين!!!",
"fakeLocation": ".لقد تتبعنا أنك تحاول استخدام موقع مزيف! يعتبر هذا مخالفة وقد تم إخطار الموارد البشرية" "noUpcoming": "لا يوجد قادم",
"fakeLocation": ".لقد تتبعنا أنك تحاول استخدام موقع مزيف! يعتبر هذا مخالفة وقد تم إخطار الموارد البشرية",
"noWinner": "حزين! لم يفز أحد اليوم."
} }

@ -514,5 +514,7 @@
"startingIn": "Starting in", "startingIn": "Starting in",
"youAreOutOfContest": "You are out of the contest.", "youAreOutOfContest": "You are out of the contest.",
"winners": "WINNERS!!!", "winners": "WINNERS!!!",
"fakeLocation": "We traced out that you try to use a fake location! This is considered a violation, and HR has been notified." "noUpcoming": "There is no upcoming",
"fakeLocation": "We traced out that you try to use a fake location! This is considered a violation, and HR has been notified.",
"noWinner": "Sad! No one won today."
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

@ -1,14 +1,19 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:logger/logger.dart' as L; import 'package:logger/logger.dart' as L;
import 'package:mohem_flutter_app/api/api_client.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/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.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/marathon_model.dart';
import 'package:mohem_flutter_app/models/marathon/question_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/marathon/winner_model.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:provider/provider.dart';
import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/hub_connection.dart';
class MarathonApiClient { class MarathonApiClient {
@ -18,52 +23,38 @@ class MarathonApiClient {
Future<String> getMarathonToken() async { Future<String> getMarathonToken() async {
String employeeUserName = AppState().getUserName ?? ""; String employeeUserName = AppState().getUserName ?? "";
String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? ""; String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? "";
Map<String, String> jsonObject = <String, String>{"userName": employeeUserName, "password": employeeSession}; Map<String, String> jsonObject = <String, String>{"userName": employeeUserName, "password": employeeSession};
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonParticipantLoginUrl, jsonObject); return await ApiClient().postJsonForObject(
(json) {
var json = jsonDecode(response.body); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
AppState().setMarathonToken = marathonModel.data["token"] ?? ""; AppState().setMarathonToken = marathonModel.data["token"] ?? "";
return marathonModel.data["token"] ?? ""; return marathonModel.data["token"] ?? "";
} else { },
//TODO : DO ERROR HANDLING HERE ApiConsts.marathonParticipantLoginUrl,
return ""; jsonObject,
} );
} else {
//TODO : DO ERROR HANDLING HERE
return "";
}
} }
Future<String> getProjectId() async { Future<String> getProjectId() async {
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonProjectGetUrl, <String, dynamic>{}, token: AppState().getMarathonToken ?? await getMarathonToken()); return await ApiClient().postJsonForObject(
(json) {
var json = jsonDecode(response.body); MarathonGenericModel responseData = MarathonGenericModel.fromJson(json);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json); return responseData.data[0]["id"] ?? "";
},
if (marathonModel.statusCode == 200) { ApiConsts.marathonProjectGetUrl,
if (marathonModel.data != null && marathonModel.isSuccessful == true) { <String, dynamic>{},
logger.i("message: ${marathonModel.data[0]["id"]}"); token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
AppState().setMarathonProjectId = marathonModel.data[0]["id"] ?? ""; );
return marathonModel.data[0]["id"] ?? "";
} else {
return "";
}
} else {
return "";
}
} }
Future<MarathonDetailModel> getMarathonDetails() async { Future<MarathonDetailModel> getMarathonDetails() async {
String payrollString = AppState().postParamsObject?.payrollCodeStr.toString() ?? "CS"; String payrollString = AppState().postParamsObject?.payrollCodeStr.toString() ?? "CS";
Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonUpcomingUrl + payrollString, token: AppState().getMarathonToken ?? await getMarathonToken()); Response response = await ApiClient().getJsonForResponse(
ApiConsts.marathonUpcomingUrl + payrollString,
token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
);
var json = jsonDecode(response.body); var json = jsonDecode(response.body);
logger.i("json in getMarathonDetails: $json"); logger.i("json in getMarathonDetails: $json");
@ -89,72 +80,73 @@ class MarathonApiClient {
"marathonId": AppState().getMarathonProjectId!, "marathonId": AppState().getMarathonProjectId!,
}; };
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonJoinParticipantUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken()); return await ApiClient().postJsonForObject(
(json) {
var json = jsonDecode(response.body); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 208) {
// means participant is already in the marathon i.e already joined
//TODO: NEED TO LOOK UPON THIS
return marathonModel.data["remainingTime"];
}
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
logger.i("joinMarathonAsParticipant: ${marathonModel.data}");
return marathonModel.data["remainingTime"]; return marathonModel.data["remainingTime"];
} else { },
return null; ApiConsts.marathonJoinParticipantUrl,
} jsonObject,
} else { token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
return null; );
}
} }
Future<QuestionModel?> getNextQuestion({required String? questionId, required String marathonId}) async { Future<QuestionModel> getNextQuestion({required String? questionId, required String marathonId}) async {
Map<String, String?> jsonObject = <String, String?>{ Map<String, String?> jsonObject = <String, String?>{
"previousQuestionId": questionId, "previousQuestionId": questionId,
"marathonId": marathonId, "marathonId": marathonId,
}; };
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonNextQuestionUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken()); return await ApiClient().postJsonForObject(
(json) {
var json = jsonDecode(response.body); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 404 || marathonModel.statusCode == 208 || marathonModel.statusCode == 204 || marathonModel.statusCode == 500) {
logger.i("json in NextQuestion: $json"); Utils.confirmDialog(
var data = json["data"]; AppRoutes.navigatorKey.currentContext,
marathonModel.message!,
if (data != null) { onTap: () {
QuestionModel newQuestion = QuestionModel.fromJson(data); AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().resetValues();
return newQuestion; Navigator.of(
} else { AppRoutes.navigatorKey.currentContext!,
return null; ).popUntil(ModalRoute.withName(AppRoutes.dashboard));
} },
);
}
QuestionModel newQuestion = QuestionModel.fromJson(marathonModel.data);
return newQuestion;
},
ApiConsts.marathonNextQuestionUrl,
jsonObject,
token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
);
} }
Future<bool> submitSelectedOption({required String marathonId, required String? questionId, required String? selectedAnswerId}) async { Future<bool> submitSelectedOption({required String marathonId, required String? questionId, required String? selectedAnswerId}) async {
Map<String, String?> jsonObject = <String, String?>{"marathonId": marathonId, "questionId": questionId, "selectedOptionId" : selectedAnswerId}; Map<String, String?> jsonObject = <String, String?>{"marathonId": marathonId, "questionId": questionId, "selectedOptionId": selectedAnswerId};
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonSubmitAnswerUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken()); return await ApiClient().postJsonForObject(
(json) {
var json = jsonDecode(response.body); MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
logger.i("json in submitSelectedOption : $json"); if (marathonModel.data != null) {
bool isOptionCorrect = marathonModel.data["isCorrect"];
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json); return isOptionCorrect;
}
if (marathonModel.isSuccessful == null) { return false;
return false; },
} ApiConsts.marathonSubmitAnswerUrl,
jsonObject,
return marathonModel.isSuccessful!; token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
);
} }
Future<int?> getQualifiers({required String marathonId}) async { Future<int?> getQualifiers({required String marathonId}) async {
Map<String, String> params = <String, String>{"marathonId": marathonId}; Map<String, String> params = <String, String>{"marathonId": marathonId};
Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonQualifiersUrl, queryParameters: params, token: AppState().getMarathonToken ?? await getMarathonToken()); Response response = await ApiClient().getJsonForResponse(
ApiConsts.marathonQualifiersUrl,
queryParameters: params,
token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
);
var json = jsonDecode(response.body); var json = jsonDecode(response.body);
logger.i("json in getQualifiers: $json"); logger.i("json in getQualifiers: $json");
@ -169,7 +161,11 @@ class MarathonApiClient {
Future<List<WinnerModel>?> getSelectedWinner({required String marathonId}) async { Future<List<WinnerModel>?> getSelectedWinner({required String marathonId}) async {
Map<String, String> params = <String, String>{"marathonId": marathonId}; Map<String, String> params = <String, String>{"marathonId": marathonId};
Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonSelectedWinner, queryParameters: params, token: AppState().getMarathonToken ?? await getMarathonToken()); Response response = await ApiClient().getJsonForResponse(
ApiConsts.marathonSelectedWinner,
queryParameters: params,
token: AppState().getMarathonToken == null || AppState().getMarathonToken == "" ? await getMarathonToken() : AppState().getMarathonToken,
);
var json = jsonDecode(response.body); var json = jsonDecode(response.body);
logger.i("json in getSelectedWinner: $json"); logger.i("json in getSelectedWinner: $json");

File diff suppressed because one or more lines are too long

@ -44,6 +44,7 @@ class ApiConsts {
static int tabletMinLength = 500; static int tabletMinLength = 500;
} }
class SharedPrefsConsts { class SharedPrefsConsts {
static String isRememberMe = "remember_me"; static String isRememberMe = "remember_me";
static String username = "username"; static String username = "username";

@ -459,7 +459,7 @@ class DateUtil {
/// get data formatted like 10:30 according to lang /// get data formatted like 10:30 according to lang
static String formatDateToTimeLang(DateTime date, bool isArabic) { static String formatDateToTimeLang(DateTime date, bool isArabic) {
return DateFormat('HH:mm a', isArabic ? "ar_SA" : "en_US").format(date); return DateFormat('hh:mm a', isArabic ? "ar_SA" : "en_US").format(date);
} }
/// get data formatted like 26/4/2020 10:30 /// get data formatted like 26/4/2020 10:30

@ -7,4 +7,6 @@ class MyLottieConsts {
static const String marathonWaiting = "assets/lottie/marathon_waiting.json"; static const String marathonWaiting = "assets/lottie/marathon_waiting.json";
static const String wrongAnswerGif = "assets/images/wrong_answer.gif"; static const String wrongAnswerGif = "assets/images/wrong_answer.gif";
static const String congratsGif = "assets/images/congrats.gif"; static const String congratsGif = "assets/images/congrats.gif";
static const String loadingLottie = "assets/lottie/loading_lottie.json";
static const String noWinnerLottie = "assets/lottie/no_winner.json";
} }

@ -134,11 +134,22 @@ class Utils {
} }
} }
static void confirmDialog(cxt, String message) { static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message}) async {
return showDialog(
context: context,
builder: (BuildContext context) => ConfirmDialog(
message: message,
onTap: onOkTapped,
),
);
}
static void confirmDialog(cxt, String message, {VoidCallback? onTap}) {
showDialog( showDialog(
context: cxt, context: cxt,
builder: (cxt) => ConfirmDialog( builder: (BuildContext cxt) => ConfirmDialog(
message: message, message: message,
onTap: onTap,
), ),
); );
} }
@ -338,13 +349,13 @@ class Utils {
if (!Platform.isIOS) { if (!Platform.isIOS) {
await showCupertinoModalPopup( await showCupertinoModalPopup(
context: context, context: context,
builder: (cxt) => Container( builder: (BuildContext cxt) => Container(
height: 250, height: 250,
color: Colors.white, color: Colors.white,
child: CupertinoDatePicker( child: CupertinoDatePicker(
backgroundColor: Colors.white, backgroundColor: Colors.white,
mode: CupertinoDatePickerMode.date, mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (value) { onDateTimeChanged: (DateTime value) {
if (value != null && value != selectedDate) { if (value != null && value != selectedDate) {
selectedDate = value; selectedDate = value;
} }
@ -364,7 +375,7 @@ class Utils {
static void readNFc({required Function(String) onRead}) { static void readNFc({required Function(String) onRead}) {
NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async {
var f; MifareUltralight f;
if (Platform.isAndroid) { if (Platform.isAndroid) {
f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22);
} else { } else {

@ -23,7 +23,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_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_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_waiting_screen.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/misc/request_submit_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart';
import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart';
@ -299,7 +298,6 @@ class AppRoutes {
// Marathon // Marathon
marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(), marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(),
marathonScreen: (BuildContext context) => MarathonScreen(), marathonScreen: (BuildContext context) => MarathonScreen(),
marathonWinnerScreen: (BuildContext context) => WinnerScreen(),
marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(), marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(),
marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(), marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(),

@ -131,8 +131,9 @@ extension EmailValidator on String {
style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14), style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14),
); );
Widget toText20({Color? color, bool isBold = false}) => Text( Widget toText20({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this, this,
textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
); );

File diff suppressed because it is too large Load Diff

@ -500,6 +500,8 @@ abstract class LocaleKeys {
static const startingIn = 'startingIn'; static const startingIn = 'startingIn';
static const youAreOutOfContest = 'youAreOutOfContest'; static const youAreOutOfContest = 'youAreOutOfContest';
static const winners = 'winners'; static const winners = 'winners';
static const noUpcoming = 'noUpcoming';
static const fakeLocation = 'fakeLocation'; static const fakeLocation = 'fakeLocation';
static const noWinner = 'noWinner';
} }

@ -27,6 +27,7 @@ Logger logger = Logger(
// output: null, // U // output: null, // U
); );
class MyHttpOverrides extends HttpOverrides { class MyHttpOverrides extends HttpOverrides {
@override @override
HttpClient createHttpClient(SecurityContext? context) { HttpClient createHttpClient(SecurityContext? context) {

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

@ -16,25 +16,28 @@ class MarathonDetailModel {
List<Sponsors>? sponsors; List<Sponsors>? sponsors;
List<Questions>? questions; List<Questions>? questions;
int? totalQuestions; int? totalQuestions;
int? marathonBufferTime;
MarathonDetailModel(
{id, MarathonDetailModel({
titleEn, id,
titleAr, titleEn,
descEn, titleAr,
descAr, descEn,
winDeciderTime, descAr,
winnersCount, winDeciderTime,
questGapTime, winnersCount,
startTime, questGapTime,
endTime, startTime,
marathoneStatusId, endTime,
scheduleTime, marathoneStatusId,
selectedLanguage, scheduleTime,
projects, selectedLanguage,
sponsors, projects,
questions, sponsors,
totalQuestions}); questions,
totalQuestions,
marathonBufferTime,
});
MarathonDetailModel.fromJson(Map<String, dynamic> json) { MarathonDetailModel.fromJson(Map<String, dynamic> json) {
id = json['id']; id = json['id'];
@ -50,22 +53,21 @@ class MarathonDetailModel {
marathoneStatusId = json['marathoneStatusId']; marathoneStatusId = json['marathoneStatusId'];
scheduleTime = json['scheduleTime']; scheduleTime = json['scheduleTime'];
selectedLanguage = json['selectedLanguage']; selectedLanguage = json['selectedLanguage'];
projects = json['projects'] != null projects = json['projects'] != null ? Projects.fromJson(json['projects']) : null;
? Projects.fromJson(json['projects'])
: null;
if (json['sponsors'] != null) { if (json['sponsors'] != null) {
sponsors = <Sponsors>[]; sponsors = <Sponsors>[];
json['sponsors'].forEach((v) { json['sponsors'].forEach((v) {
sponsors!.add( Sponsors.fromJson(v)); sponsors!.add(Sponsors.fromJson(v));
}); });
} }
if (json['questions'] != null) { if (json['questions'] != null) {
questions = <Questions>[]; questions = <Questions>[];
json['questions'].forEach((v) { json['questions'].forEach((v) {
questions!.add( Questions.fromJson(v)); questions!.add(Questions.fromJson(v));
}); });
} }
totalQuestions = json["totalQuestions"]; totalQuestions = json["totalQuestions"];
marathonBufferTime = json["marathonBufferTime"];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -93,6 +95,7 @@ class MarathonDetailModel {
data['questions'] = questions!.map((v) => v.toJson()).toList(); data['questions'] = questions!.map((v) => v.toJson()).toList();
} }
data['totalQuestions'] = totalQuestions; data['totalQuestions'] = totalQuestions;
data['marathonBufferTime'] = marathonBufferTime;
return data; return data;
} }
@ -132,14 +135,7 @@ class Sponsors {
String? logo; String? logo;
List<SponsorPrizes>? sponsorPrizes; List<SponsorPrizes>? sponsorPrizes;
Sponsors( Sponsors({id, nameEn, nameAr, image, video, logo, sponsorPrizes});
{id,
nameEn,
nameAr,
image,
video,
logo,
sponsorPrizes});
Sponsors.fromJson(Map<String, dynamic> json) { Sponsors.fromJson(Map<String, dynamic> json) {
id = json['id']; id = json['id'];
@ -151,13 +147,13 @@ class Sponsors {
if (json['sponsorPrizes'] != null) { if (json['sponsorPrizes'] != null) {
sponsorPrizes = <SponsorPrizes>[]; sponsorPrizes = <SponsorPrizes>[];
json['sponsorPrizes'].forEach((v) { json['sponsorPrizes'].forEach((v) {
sponsorPrizes!.add( SponsorPrizes.fromJson(v)); sponsorPrizes!.add(SponsorPrizes.fromJson(v));
}); });
} }
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{}; Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id; data['id'] = id;
data['nameEn'] = nameEn; data['nameEn'] = nameEn;
data['nameAr'] = nameAr; data['nameAr'] = nameAr;
@ -165,8 +161,7 @@ class Sponsors {
data['video'] = video; data['video'] = video;
data['logo'] = logo; data['logo'] = logo;
if (sponsorPrizes != null) { if (sponsorPrizes != null) {
data['sponsorPrizes'] = data['sponsorPrizes'] = sponsorPrizes!.map((v) => v.toJson()).toList();
sponsorPrizes!.map((v) => v.toJson()).toList();
} }
return data; return data;
} }
@ -186,7 +181,7 @@ class SponsorPrizes {
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{}; Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id; data['id'] = id;
data['marathonPrizeEn'] = marathonPrizeEn; data['marathonPrizeEn'] = marathonPrizeEn;
data['marathonPrizeAr'] = marathonPrizeAr; data['marathonPrizeAr'] = marathonPrizeAr;
@ -208,19 +203,7 @@ class Questions {
int? questOptionsLimit; int? questOptionsLimit;
List? questionOptions; List? questionOptions;
Questions( Questions({id, titleEn, titleAr, marathonId, questionTypeId, questionTime, nextQuestGap, gapType, gapValue, gapImage, questOptionsLimit, questionOptions});
{id,
titleEn,
titleAr,
marathonId,
questionTypeId,
questionTime,
nextQuestGap,
gapType,
gapValue,
gapImage,
questOptionsLimit,
questionOptions});
Questions.fromJson(Map<String, dynamic> json) { Questions.fromJson(Map<String, dynamic> json) {
id = json['id']; id = json['id'];

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

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

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

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

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

@ -0,0 +1,189 @@
import 'dart:math';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
class SeekBar extends StatefulWidget {
final Duration duration;
final Duration position;
final Duration bufferedPosition;
final ValueChanged<Duration>? onChanged;
final ValueChanged<Duration>? onChangeEnd;
const SeekBar({
Key? key,
required this.duration,
required this.position,
required this.bufferedPosition,
this.onChanged,
this.onChangeEnd,
}) : super(key: key);
@override
SeekBarState createState() => SeekBarState();
}
class SeekBarState extends State<SeekBar> {
double? _dragValue;
late SliderThemeData _sliderThemeData;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_sliderThemeData = SliderTheme.of(context).copyWith(
// trackHeight: 2.0,
thumbColor: MyColors.lightGreenColor,
activeTrackColor: MyColors.lightGreenColor,
inactiveTrackColor: MyColors.grey57Color.withOpacity(0.4),
);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SliderTheme(
data: _sliderThemeData.copyWith(
thumbShape: HiddenThumbComponentShape(),
),
child: ExcludeSemantics(
child: Slider(
min: 0.0,
max: widget.duration.inMilliseconds.toDouble(),
value: min(widget.bufferedPosition.inMilliseconds.toDouble(), widget.duration.inMilliseconds.toDouble()),
onChanged: (value) {
setState(() {
_dragValue = value;
});
if (widget.onChanged != null) {
widget.onChanged!(Duration(milliseconds: value.round()));
}
},
onChangeEnd: (value) {
if (widget.onChangeEnd != null) {
widget.onChangeEnd!(Duration(milliseconds: value.round()));
}
_dragValue = null;
},
),
),
),
SliderTheme(
data: _sliderThemeData.copyWith(
inactiveTrackColor: Colors.transparent,
),
child: Slider(
min: 0.0,
max: widget.duration.inMilliseconds.toDouble(),
value: min(_dragValue ?? widget.position.inMilliseconds.toDouble(), widget.duration.inMilliseconds.toDouble()),
onChanged: (value) {
setState(() {
_dragValue = value;
});
if (widget.onChanged != null) {
widget.onChanged!(Duration(milliseconds: value.round()));
}
},
onChangeEnd: (value) {
if (widget.onChangeEnd != null) {
widget.onChangeEnd!(Duration(milliseconds: value.round()));
}
_dragValue = null;
},
),
),
],
);
}
}
class PositionData {
final Duration position;
final Duration bufferedPosition;
final Duration duration;
PositionData(this.position, this.bufferedPosition, this.duration);
}
class HiddenThumbComponentShape extends SliderComponentShape {
@override
Size getPreferredSize(bool isEnabled, bool isDiscrete) => Size.zero;
@override
void paint(
PaintingContext context,
Offset center, {
required Animation<double> activationAnimation,
required Animation<double> enableAnimation,
required bool isDiscrete,
required TextPainter labelPainter,
required RenderBox parentBox,
required SliderThemeData sliderTheme,
required TextDirection textDirection,
required double value,
required double textScaleFactor,
required Size sizeWithOverflow,
}) {}
}
class WaveBubble extends StatelessWidget {
final PlayerController playerController;
final VoidCallback onTap;
final bool isPlaying;
const WaveBubble({
Key? key,
required this.playerController,
required this.onTap,
required this.isPlaying,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
gradient: const LinearGradient(
transform: GradientRotation(.83),
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: <Color>[
MyColors.gradiantEndColor,
MyColors.gradiantStartColor,
],
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: onTap,
icon: Icon(isPlaying ? Icons.stop : Icons.play_arrow),
color: Colors.white,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
AudioFileWaveforms(
size: Size(MediaQuery.of(context).size.width / 2, 10),
playerController: playerController,
padding: EdgeInsets.zero,
margin: EdgeInsets.zero,
enableSeekGesture: true,
density: 1,
playerWaveStyle: const PlayerWaveStyle(
fixedWaveColor: Colors.white,
liveWaveColor: MyColors.greenColor,
showTop: true,
showBottom: true,
waveCap: StrokeCap.round,
seekLineThickness: 2,
visualizerHeight: 4,
backgroundColor: Colors.transparent,
),
),
],
),
);
}
}

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

@ -32,6 +32,8 @@ import 'package:signalr_netcore/signalr_client.dart';
late HubConnection chatHubConnection; late HubConnection chatHubConnection;
class DashboardScreen extends StatefulWidget { class DashboardScreen extends StatefulWidget {
DashboardScreen({Key? key}) : super(key: key); DashboardScreen({Key? key}) : super(key: key);

@ -165,6 +165,9 @@ class _LoginScreenState extends State<LoginScreen> {
// username.text = "206535"; // Hashim User // username.text = "206535"; // Hashim User
// password.text = "Namira786"; // password.text = "Namira786";
// 13777
// Ab12345cd
} }
if (isAppOpenBySystem!) checkFirebaseToken(); if (isAppOpenBySystem!) checkFirebaseToken();
} }

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:appinio_swiper/appinio_swiper.dart'; import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/marathon/marathon_api_client.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/app_state/app_state.dart';
@ -31,12 +30,12 @@ class MarathonProvider extends ChangeNotifier {
String? selectedOptionId; String? selectedOptionId;
int? totalQualifiers; int? totalQualifiers;
//TODO: THIS BUG NEEDS TO BE FIXED. NOT DONE YET
String? gapTimeImage; String? gapTimeImage;
String? gapTimeText; String? gapTimeText;
int? gapTimeType; int? gapTimeType;
bool iAmWinner = false; bool iAmWinner = false;
bool isGettingQualifiers = false;
bool isPrivilegedWithMarathon = false; bool isPrivilegedWithMarathon = false;
bool _isLoading = false; bool _isLoading = false;
@ -66,6 +65,24 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
bool _isButtonEnabled = false;
bool get isButtonEnabled => _isButtonEnabled;
set isButtonEnabled(bool value) {
_isButtonEnabled = value;
notifyListeners();
}
bool _isUserWaiting = false;
bool get isUserWaiting => _isUserWaiting;
set isUserWaiting(bool value) {
_isUserWaiting = value;
notifyListeners();
}
bool _isMarathonCompleted = false; bool _isMarathonCompleted = false;
bool get isMarathonCompleted => _isMarathonCompleted; bool get isMarathonCompleted => _isMarathonCompleted;
@ -144,7 +161,13 @@ class MarathonProvider extends ChangeNotifier {
); );
} }
int totalSecondsToWaitForMarathon = 20; int totalSecondsToWaitForMarathon = 30;
set updateTotalSecondsToWaitForMarathon(int value) {
totalSecondsToWaitForMarathon = value;
notifyListeners();
}
Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimerToWaitForMarathon() { void startTimerToWaitForMarathon() {
@ -153,7 +176,13 @@ class MarathonProvider extends ChangeNotifier {
oneSec, oneSec,
(Timer timer) async { (Timer timer) async {
if (totalSecondsToWaitForMarathon == 0) { if (totalSecondsToWaitForMarathon == 0) {
callNextQuestionApi(); if (isUserWaiting) {
MarathonApiClient().joinMarathonAsParticipant().whenComplete(() async {
await callNextQuestionApi();
});
} else {
isButtonEnabled = false;
}
timer.cancel(); timer.cancel();
return; return;
} else { } else {
@ -173,7 +202,7 @@ class MarathonProvider extends ChangeNotifier {
timerForQuestion = Timer.periodic( timerForQuestion = Timer.periodic(
oneSec, oneSec,
(Timer timer) async { (Timer timer) async {
// This 2 is just to show the color of answer tile for 2 seconds and then update card status // This 2 is just to show the color of answer tile for 1 and then update card status
if (totalCurrentQuestionTime - currentGapTime == 1) { if (totalCurrentQuestionTime - currentGapTime == 1) {
getCorrectAnswerAndUpdateAnswerColor(); getCorrectAnswerAndUpdateAnswerColor();
} }
@ -184,11 +213,14 @@ class MarathonProvider extends ChangeNotifier {
updateCardStatusToAnswer(); updateCardStatusToAnswer();
scheduleMicrotask(() async { scheduleMicrotask(() async {
await callSubmitOptionApi().then((bool value) async { if (isUserOutOfGame) {
if (value) { await callNextQuestionApi();
} else {
await callSubmitOptionApi().then((bool value) async {
updateIsUserOutOfGame = !value;
await callNextQuestionApi(); await callNextQuestionApi();
} });
}); }
}); });
} }
@ -198,7 +230,7 @@ class MarathonProvider extends ChangeNotifier {
gapTimeType = currentQuestion.gapType; gapTimeType = currentQuestion.gapType;
updateCardData(); updateCardData();
if (currentQuestionNumber - 1 == marathonDetailModel.totalQuestions!) { if (currentQuestionNumber - 1 == marathonDetailModel.totalQuestions!) {
callGetQualifiersApi(); isGettingQualifiers = true;
updateQuestionCardStatus(QuestionCardStatus.findingWinner); updateQuestionCardStatus(QuestionCardStatus.findingWinner);
timer.cancel(); timer.cancel();
cancelTimer(); cancelTimer();
@ -224,10 +256,13 @@ class MarathonProvider extends ChangeNotifier {
timerForWinnerSelection = Timer.periodic( timerForWinnerSelection = Timer.periodic(
oneSec, oneSec,
(Timer timer) async { (Timer timer) async {
if (totalSecondsToWaitForWinner == 0) { if (totalSecondsToWaitForWinner == 1) {
await callGetSelectedWinnersApi().whenComplete(() => updateQuestionCardStatus(QuestionCardStatus.winnerFound));
timer.cancel(); timer.cancel();
updateQuestionCardStatus(QuestionCardStatus.winnerFound);
return; return;
} else if (totalSecondsToWaitForWinner == 15) {
totalSecondsToWaitForWinner--;
await callGetQualifiersApi();
} else { } else {
totalSecondsToWaitForWinner--; totalSecondsToWaitForWinner--;
} }
@ -245,6 +280,7 @@ class MarathonProvider extends ChangeNotifier {
Future<void> callGetQualifiersApi() async { Future<void> callGetQualifiersApi() async {
totalQualifiers = await MarathonApiClient().getQualifiers(marathonId: marathonDetailModel.id!); totalQualifiers = await MarathonApiClient().getQualifiers(marathonId: marathonDetailModel.id!);
isGettingQualifiers = false;
notifyListeners(); notifyListeners();
} }
@ -268,7 +304,7 @@ class MarathonProvider extends ChangeNotifier {
if (currentQuestionNumber < marathonDetailModel.totalQuestions!) { if (currentQuestionNumber < marathonDetailModel.totalQuestions!) {
if (currentQuestionNumber == 0) { if (currentQuestionNumber == 0) {
Utils.showLoading(AppRoutes.navigatorKey.currentContext!); Utils.showLoading(AppRoutes.navigatorKey.currentContext!);
currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!))!; currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!));
gapTimeImage = currentQuestion.gapImage; gapTimeImage = currentQuestion.gapImage;
gapTimeText = currentQuestion.gapText; gapTimeText = currentQuestion.gapText;
gapTimeType = currentQuestion.gapType; gapTimeType = currentQuestion.gapType;
@ -277,10 +313,9 @@ class MarathonProvider extends ChangeNotifier {
} }
startTimerForQuestion(); startTimerForQuestion();
updateCardData(); updateCardData();
Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen); Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen);
} else { } else {
currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: currentQuestion.id, marathonId: marathonDetailModel.id!))!; currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: currentQuestion.id, marathonId: marathonDetailModel.id!));
} }
notifyListeners(); notifyListeners();
} }
@ -325,14 +360,15 @@ class MarathonProvider extends ChangeNotifier {
currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected; currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected;
} }
currentQuestion.questionOptions![index].optionStatus = status; currentQuestion.questionOptions![index].optionStatus = status;
selectedOptionId = currentQuestion.questionOptions![index].id;
selectedOptionIndex = index; selectedOptionIndex = index;
notifyListeners(); notifyListeners();
} }
void updateQuestionCardStatus(QuestionCardStatus status) { void updateQuestionCardStatus(QuestionCardStatus status) {
if (status == QuestionCardStatus.wrongAnswer || status == QuestionCardStatus.skippedAnswer) { // if (status == QuestionCardStatus.wrongAnswer || status == QuestionCardStatus.skippedAnswer) {
updateIsUserOutOfGame = true; // updateIsUserOutOfGame = true;
} // }
questionCardStatus = status; questionCardStatus = status;
notifyListeners(); notifyListeners();
} }
@ -369,18 +405,27 @@ class MarathonProvider extends ChangeNotifier {
void resetValues() async { void resetValues() async {
_currentQuestionNumber = 0; _currentQuestionNumber = 0;
cardContentList.clear(); cardContentList.clear();
itsMarathonTime = false;
timerForWinnerSelection.cancel(); timerForWinnerSelection.cancel();
timerForSponsorVideo.cancel(); timerForSponsorVideo.cancel();
timerToWaitForMarathon.cancel(); timerToWaitForMarathon.cancel();
timerForQuestion.cancel(); timerForQuestion.cancel();
_isMarathonCompleted = false; _isMarathonCompleted = false;
isUserOutOfGame = false; isUserOutOfGame = false;
isButtonEnabled = false;
isUserWaiting = false;
totalCurrentQuestionTime = 0; totalCurrentQuestionTime = 0;
sponsorsSecondsCounter = 0; sponsorsSecondsCounter = 0;
totalSponsorVideoSeconds = 0; totalSponsorVideoSeconds = 0;
totalSecondsToWaitForMarathon = 20; totalSecondsToWaitForWinner = 30;
totalSecondsToWaitForMarathon = 30;
currentGapTime = 0; currentGapTime = 0;
currentQuestion = QuestionModel(); currentQuestion = QuestionModel();
if (answerStatusesList.isNotEmpty) {
for (int i = 0; i < answerStatusesList.length; i++) {
answerStatusesList[i] = QuestionCardStatus.question;
}
}
notifyListeners(); notifyListeners();
} }
@ -408,12 +453,16 @@ class MarathonProvider extends ChangeNotifier {
if (isPrivilegedWithMarathon) { if (isPrivilegedWithMarathon) {
await MarathonApiClient().getMarathonToken().whenComplete(() async { await MarathonApiClient().getMarathonToken().whenComplete(() async {
marathonDetailModel = await MarathonApiClient().getMarathonDetails(); marathonDetailModel = await MarathonApiClient().getMarathonDetails();
updateTotalSecondsToWaitForMarathon = marathonDetailModel.marathonBufferTime ?? 30;
if (marathonDetailModel.id == null) { if (marathonDetailModel.id == null) {
isUpComingMarathon = false; isUpComingMarathon = false;
isLoading = false; isLoading = false;
notifyListeners(); notifyListeners();
return; return;
} }
if (DateTime.parse(marathonDetailModel.startTime!).isAfter(DateTime.now())) {
itsMarathonTime = false;
}
populateQuestionStatusesList(); populateQuestionStatusesList();
}); });
} }
@ -423,17 +472,9 @@ class MarathonProvider extends ChangeNotifier {
Future<void> onJoinMarathonPressed(BuildContext context) async { Future<void> onJoinMarathonPressed(BuildContext context) async {
try { try {
Utils.showLoading(context); isUserWaiting = true;
resetValues(); Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen);
int? remainingTime = await MarathonApiClient().joinMarathonAsParticipant();
if (remainingTime != null) {
totalSecondsToWaitForMarathon = remainingTime;
notifyListeners();
startTimerToWaitForMarathon();
Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen);
}
} catch (e) { } catch (e) {
Utils.hideLoading(context);
Utils.confirmDialog(context, e.toString()); Utils.confirmDialog(context, e.toString());
} }
} }

@ -36,11 +36,11 @@ class MarathonScreen extends StatelessWidget {
} }
return gapText.toText18(color: MyColors.darkTextColor, isCentered: true); return gapText.toText18(color: MyColors.darkTextColor, isCentered: true);
} }
if (gapType == 2) { if (gapType == 0) {
if (gapImage == null) { if (gapImage == null) {
return Image.asset(MyLottieConsts.congratsGif, height: 200); return Image.asset(MyLottieConsts.congratsGif, height: 200);
} }
return Image.network(gapImage, height: 200); return Image.network(ApiConsts.marathonBaseUrlServices + gapImage, height: 200);
} }
return Image.asset(MyLottieConsts.congratsGif, height: 200); return Image.asset(MyLottieConsts.congratsGif, height: 200);
} }
@ -50,88 +50,101 @@ class MarathonScreen extends StatelessWidget {
width: double.infinity, width: double.infinity,
decoration: MyDecorations.shadowDecoration, decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Stack( child: provider.selectedWinners == null || (provider.selectedWinners!.isEmpty && !provider.iAmWinner)
children: <Widget>[ ? Column(
Column( children: <Widget>[
mainAxisAlignment: MainAxisAlignment.center, Lottie.asset(MyLottieConsts.noWinnerLottie),
children: <Widget>[ Center(
SizedBox( child: LocaleKeys.noWinner.tr().toText18(color: MyColors.grey3AColor, isCentered: true),
height: 50,
child: Stack(
children: <Widget>[
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: <Widget>[
(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) ...<Widget>[
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: <Widget>[
(AppState().isArabic(context) ? provider.selectedWinners![index].nameEn : provider.selectedWinners![index].nameEn)!.toText16(
color: MyColors.grey3AColor,
),
provider.selectedWinners!.first.employeeId!.toText16(color: MyColors.grey57Color),
],
);
},
), ),
], ],
60.height, )
if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...<Widget>[ : Stack(
Row( children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
"${LocaleKeys.sponsoredBy.tr()} ".toText14(color: MyColors.grey77Color), SizedBox(
(AppState().isArabic(context) ? provider.marathonDetailModel.sponsors!.first.nameAr ?? "" : provider.marathonDetailModel.sponsors!.first.nameEn ?? "").toText14( height: 50,
color: MyColors.darkTextColor, child: Stack(
isBold: true, children: <Widget>[
Align(
alignment: Alignment.center,
child: SvgPicture.asset("assets/images/winner_ribbon.svg", height: 50),
),
Align(
alignment: Alignment.center,
child: ((provider.selectedWinners!.length == 1 && !provider.iAmWinner) || (provider.selectedWinners!.isEmpty && provider.iAmWinner)
? LocaleKeys.winner.tr()
: LocaleKeys.winners.tr())
.toText32(color: MyColors.white, isBold: true, isCentered: true)
.paddingOnly(top: 07),
)
],
),
), ),
16.height,
provider.iAmWinner
? Column(
children: <Widget>[
(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) ...<Widget>[
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: <Widget>[
(AppState().isArabic(context) ? provider.selectedWinners![index].nameEn : provider.selectedWinners![index].nameEn)!.toText16(
color: MyColors.grey3AColor,
),
provider.selectedWinners!.first.employeeId!.toText16(color: MyColors.grey57Color),
],
);
},
),
],
60.height,
if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...<Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
"${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(
ApiConsts.marathonBaseUrlServices + provider.marathonDetailModel.sponsors!.first.logo!,
height: 50,
width: 150,
fit: BoxFit.contain,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return Image.asset("assets/images/logos/main_mohemm_logo.png", height: 50, width: 150);
},
)
],
], ],
), ),
5.height, Lottie.asset(MyLottieConsts.celebrate1Lottie),
Image.network(
ApiConsts.marathonBaseUrlServices + provider.marathonDetailModel.sponsors!.first.image!,
height: 50,
width: 150,
fit: BoxFit.contain,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center();
},
)
], ],
], ),
),
Lottie.asset(MyLottieConsts.celebrate1Lottie),
],
),
); );
} }
@ -166,6 +179,7 @@ class MarathonScreen extends StatelessWidget {
onHomeTapped: () { onHomeTapped: () {
if (provider.questionCardStatus == QuestionCardStatus.winnerFound) { if (provider.questionCardStatus == QuestionCardStatus.winnerFound) {
provider.resetValues(); provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
} else { } else {
showDialog( showDialog(
@ -174,6 +188,7 @@ class MarathonScreen extends StatelessWidget {
message: LocaleKeys.youWantToLeaveMarathon.tr(), message: LocaleKeys.youWantToLeaveMarathon.tr(),
onTap: () { onTap: () {
provider.resetValues(); provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
}, },
), ),
@ -183,6 +198,7 @@ class MarathonScreen extends StatelessWidget {
onBackTapped: () { onBackTapped: () {
if (provider.questionCardStatus == QuestionCardStatus.winnerFound) { if (provider.questionCardStatus == QuestionCardStatus.winnerFound) {
provider.resetValues(); provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
} else { } else {
showDialog( showDialog(
@ -201,8 +217,8 @@ class MarathonScreen extends StatelessWidget {
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
ListView( ListView(
children: [ children: <Widget>[
20.height, 10.height,
if (provider.questionCardStatus == QuestionCardStatus.findingWinner) ...<Widget>[ if (provider.questionCardStatus == QuestionCardStatus.findingWinner) ...<Widget>[
QualifiersContainer(provider: provider).paddingOnly(left: 21, right: 21), QualifiersContainer(provider: provider).paddingOnly(left: 21, right: 21),
] else if (provider.questionCardStatus == QuestionCardStatus.winnerFound) ] else if (provider.questionCardStatus == QuestionCardStatus.winnerFound)
@ -237,7 +253,7 @@ class MarathonScreen extends StatelessWidget {
subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor, isCentered: true), subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor, isCentered: true),
), ),
onFindingWinner: (BuildContext context) => CustomStatusWidget( onFindingWinner: (BuildContext context) => CustomStatusWidget(
asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168), asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168, reverse: false),
title: LocaleKeys.fingersCrossed.tr().toText22(color: MyColors.greenColor), title: LocaleKeys.fingersCrossed.tr().toText22(color: MyColors.greenColor),
subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor, isCentered: true), subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor, isCentered: true),
), ),
@ -248,6 +264,7 @@ class MarathonScreen extends StatelessWidget {
provider.questionCardStatus == QuestionCardStatus.winnerFound provider.questionCardStatus == QuestionCardStatus.winnerFound
? DefaultButton(LocaleKeys.ok.tr(), () { ? DefaultButton(LocaleKeys.ok.tr(), () {
provider.resetValues(); provider.resetValues();
provider.getMarathonDetailsFromApi();
Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard)); Navigator.of(context).popUntil(ModalRoute.withName(AppRoutes.dashboard));
}).insideContainer }).insideContainer
: const SizedBox() : const SizedBox()

@ -80,24 +80,24 @@ class MarathonWaitingScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
LocaleKeys.startingIn.tr().toText16(), LocaleKeys.startingIn.tr().toText16(),
provider.totalSecondsToWaitForMarathon.toString().toText18(color: provider.totalSecondsToWaitForMarathon < 5 ? MyColors.redColor : MyColors.black), (provider.totalSecondsToWaitForMarathon < 10 ? "0${provider.totalSecondsToWaitForMarathon.toString()}" : provider.totalSecondsToWaitForMarathon.toString()).toText18(color: provider.totalSecondsToWaitForMarathon < 5 ? MyColors.redColor : MyColors.black),
],
),
),
Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
InkWell(
onTap: () {
provider.callNextQuestionApi();
provider.timerToWaitForMarathon.cancel();
},
child: "Join Now".toText16(),
).paddingOnly(bottom: 20),
], ],
), ),
), ),
// Align(
// child: Column(
// mainAxisAlignment: MainAxisAlignment.end,
// children: <Widget>[
// InkWell(
// onTap: () {
// provider.callNextQuestionApi();
// provider.timerToWaitForMarathon.cancel();
// },
// child: "Join Now".toText16(),
// ).paddingOnly(bottom: 20),
// ],
// ),
// ),
], ],
), ),
).expanded, ).expanded,

@ -0,0 +1,202 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:auto_size_text/auto_size_text.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
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 CountdownTimerForDetailScreen extends StatelessWidget {
final int timeToMarathon;
final MarathonProvider provider;
CountdownTimerForDetailScreen({
Key? key,
required this.provider,
required this.timeToMarathon,
}) : super(key: key);
final TextStyle styleTextHome = TextStyle(
color: MyColors.white.withOpacity(0.45),
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w800,
letterSpacing: -0.4,
);
final TextStyle styleDigitHome = TextStyle(
height: 22 / 27,
color: MyColors.white,
fontSize: isTablet ? 30 : 15,
fontStyle: FontStyle.italic,
letterSpacing: -1.44,
fontWeight: FontWeight.bold,
);
final TextStyle styleTextMarathon = const TextStyle(
fontSize: 10,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w600,
color: MyColors.grey57Color,
letterSpacing: -0.4,
);
final TextStyle styleDigitMarathon = const TextStyle(
height: 23 / 24,
color: MyColors.darkTextColor,
fontSize: 34,
letterSpacing: -1.44,
fontWeight: FontWeight.bold,
);
Widget buildSeparator() {
return AutoSizeText(
" : ",
maxFontSize: 24,
minFontSize: 20,
style: styleDigitMarathon,
);
}
Widget getTimeDigit(String text) {
return AutoSizeText(
text,
maxFontSize: 24,
minFontSize: 20,
style: styleDigitMarathon,
);
}
Widget getTimeText(String text) {
return AutoSizeText(
text,
minFontSize: 7,
maxFontSize: 8,
style: styleTextMarathon,
);
}
Widget buildEmptyWidget() {
return Directionality(
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
children: <Widget>[
getTimeDigit("00"),
getTimeText(LocaleKeys.days.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
getTimeDigit("00"),
getTimeText(LocaleKeys.hours.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
getTimeDigit("00"),
getTimeText(LocaleKeys.minutes.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
getTimeDigit("00"),
getTimeText(LocaleKeys.seconds.tr()),
],
),
],
),
);
}
Widget buildCountdownTimer(CurrentRemainingTime? time) {
if (time == null) {
if (!provider.itsMarathonTime) {
scheduleMicrotask(() {
provider.itsMarathonTime = true;
provider.isButtonEnabled = true;
provider.startTimerToWaitForMarathon();
});
}
return provider.itsMarathonTime
? Directionality(
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
children: <Widget>[
getTimeDigit(provider.totalSecondsToWaitForMarathon.toString()),
getTimeText(LocaleKeys.seconds.tr()),
],
),
],
),
)
: buildEmptyWidget();
}
return Directionality(
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
children: <Widget>[
time.days == null ? getTimeDigit("00") : getTimeDigit(time.days! < 10 ? "0${time.days.toString()}" : time.days.toString()),
getTimeText(LocaleKeys.days.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
time.hours == null ? getTimeDigit("00") : getTimeDigit(time.hours! < 10 ? "0${time.hours.toString()}" : time.hours.toString()),
getTimeText(LocaleKeys.hours.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
time.min == null ? getTimeDigit("00") : getTimeDigit(time.min! < 10 ? "0${time.min.toString()}" : time.min.toString()),
getTimeText(LocaleKeys.minutes.tr()),
],
),
buildSeparator(),
Column(
children: <Widget>[
time.sec == null ? getTimeDigit("00") : getTimeDigit(time.sec! < 10 ? "0${time.sec.toString()}" : time.sec.toString()),
getTimeText(LocaleKeys.seconds.tr()),
],
),
],
),
);
}
@override
Widget build(BuildContext context) {
return CountdownTimer(
endTime: timeToMarathon,
onEnd: null,
widgetBuilder: (BuildContext context, CurrentRemainingTime? time) {
return buildCountdownTimer(time);
},
);
}
}

@ -1,4 +1,3 @@
import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
@ -11,16 +10,14 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.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/marathon_provider.dart';
class BuildCountdownTimer extends StatelessWidget { class CountdownTimerForMainScreen extends StatelessWidget {
final int timeToMarathon; final int timeToMarathon;
final MarathonProvider provider; final MarathonProvider provider;
final int screenFlag;
BuildCountdownTimer({ CountdownTimerForMainScreen({
Key? key, Key? key,
required this.provider, required this.provider,
required this.timeToMarathon, required this.timeToMarathon,
required this.screenFlag,
}) : super(key: key); }) : super(key: key);
final TextStyle styleTextHome = TextStyle( final TextStyle styleTextHome = TextStyle(
@ -60,7 +57,7 @@ class BuildCountdownTimer extends StatelessWidget {
" : ", " : ",
maxFontSize: 24, maxFontSize: 24,
minFontSize: 20, minFontSize: 20,
style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, style: styleDigitHome,
); );
} }
@ -69,7 +66,7 @@ class BuildCountdownTimer extends StatelessWidget {
text, text,
maxFontSize: 24, maxFontSize: 24,
minFontSize: 20, minFontSize: 20,
style: screenFlag == 0 ? styleDigitHome : styleDigitMarathon, style: styleDigitHome,
); );
} }
@ -78,7 +75,7 @@ class BuildCountdownTimer extends StatelessWidget {
text, text,
minFontSize: 7, minFontSize: 7,
maxFontSize: 8, maxFontSize: 8,
style: screenFlag == 0 ? styleTextHome : styleTextMarathon, style: styleTextHome,
); );
} }
@ -124,14 +121,9 @@ class BuildCountdownTimer extends StatelessWidget {
Widget buildCountdownTimer(CurrentRemainingTime? time) { Widget buildCountdownTimer(CurrentRemainingTime? time) {
if (time == null) { if (time == null) {
if (!provider.itsMarathonTime) {
scheduleMicrotask(() {
provider.itsMarathonTime = true;
});
}
return buildEmptyWidget(); return buildEmptyWidget();
} }
return Directionality( return Directionality(
textDirection: ui.TextDirection.ltr, textDirection: ui.TextDirection.ltr,
child: Row( child: Row(

@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -14,9 +15,13 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.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/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer_main_screen.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// It is used to pass a dummy time to test Marathon
int dummyTime = DateTime.now().millisecondsSinceEpoch + 8690;
class MarathonBanner extends StatelessWidget { class MarathonBanner extends StatelessWidget {
final bool isMarathonUpcoming; final bool isMarathonUpcoming;
@ -180,11 +185,160 @@ class MarathonBanner extends StatelessWidget {
); );
} }
Widget getNoUpcomingMarathonWidget(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: <Widget>[
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: <Widget>[
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: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
AppState().isArabic(context) ? 0.height : 5.height,
Text(
LocaleKeys.noUpcoming.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,
),
),
),
),
).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),
],
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MarathonProvider provider = context.read<MarathonProvider>(); MarathonProvider provider = context.read<MarathonProvider>();
return provider.isPrivilegedWithMarathon return !provider.isPrivilegedWithMarathon
? getUnPrivilegedMarathon(context) ? getUnPrivilegedMarathon(context)
: provider.marathonDetailModel.startTime != null : provider.marathonDetailModel.startTime != null
? Container( ? Container(
@ -276,10 +430,10 @@ class MarathonBanner extends StatelessWidget {
), ),
), ),
isTablet ? 10.height : 3.height, isTablet ? 10.height : 3.height,
BuildCountdownTimer( CountdownTimerForMainScreen(
timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch, timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch,
// timeToMarathon: dummyTime,
provider: provider, provider: provider,
screenFlag: 0,
), ),
], ],
).paddingOnly( ).paddingOnly(
@ -357,7 +511,7 @@ class MarathonBanner extends StatelessWidget {
).onPress(() async { ).onPress(() async {
int remainingTimeInMinutes = DateTime.parse(provider.marathonDetailModel.startTime!).difference(DateTime.now()).inMinutes; int remainingTimeInMinutes = DateTime.parse(provider.marathonDetailModel.startTime!).difference(DateTime.now()).inMinutes;
if (remainingTimeInMinutes > 2) { if (remainingTimeInMinutes > 2 && provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) {
Utils.showLoading(context); Utils.showLoading(context);
try { try {
await provider.initializeVideoPlayer().then((_) { await provider.initializeVideoPlayer().then((_) {
@ -367,7 +521,7 @@ class MarathonBanner extends StatelessWidget {
}); });
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print("Error in VideoPlayer: ${e.toString()}"); log("Error in VideoPlayer: ${e.toString()}");
} }
Utils.hideLoading(context); Utils.hideLoading(context);
Navigator.pushNamed(context, AppRoutes.marathonIntroScreen); Navigator.pushNamed(context, AppRoutes.marathonIntroScreen);
@ -376,8 +530,7 @@ class MarathonBanner extends StatelessWidget {
Navigator.pushNamed(context, AppRoutes.marathonIntroScreen); Navigator.pushNamed(context, AppRoutes.marathonIntroScreen);
} }
}), }),
// );
) )
: const SizedBox(); : getNoUpcomingMarathonWidget(context);
} }
} }

@ -36,9 +36,9 @@ class MarathonDetailsCard extends StatelessWidget {
) )
], ],
), ),
if (provider.marathonDetailModel.sponsors != null) ...<Widget>[ if (provider.marathonDetailModel.sponsors != null && provider.marathonDetailModel.sponsors!.isNotEmpty) ...<Widget>[
5.height, 5.height,
provider.marathonDetailModel.sponsors?.first.sponsorPrizes != null provider.marathonDetailModel.sponsors!.first.sponsorPrizes != null
? Row( ? Row(
children: <Widget>[ children: <Widget>[
"${LocaleKeys.prize.tr()} ".toText16(color: MyColors.grey77Color, isBold: true), "${LocaleKeys.prize.tr()} ".toText16(color: MyColors.grey77Color, isBold: true),
@ -59,12 +59,12 @@ class MarathonDetailsCard extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Image.network( Image.network(
ApiConsts.marathonBaseUrlServices + provider.marathonDetailModel.sponsors!.first.image!, ApiConsts.marathonBaseUrlServices + provider.marathonDetailModel.sponsors!.first.logo!,
height: 50, height: 50,
width: 150, width: 150,
fit: BoxFit.contain, fit: BoxFit.contain,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center(); return Image.asset("assets/images/logos/main_mohemm_logo.png", height: 50, width: 150);
}, },
) )
], ],

@ -43,7 +43,7 @@ class MarathonFooter extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultButton( return DefaultButton(
LocaleKeys.joinMarathon.tr(), LocaleKeys.joinMarathon.tr(),
!provider.itsMarathonTime ? () => provider.onJoinMarathonPressed(context) : null, provider.isButtonEnabled ? () => provider.onJoinMarathonPressed(context) : null,
).insideContainer; ).insideContainer;
} }
} }

@ -42,9 +42,13 @@ class MarathonProgressContainer extends StatelessWidget {
stepper(provider.currentQuestionNumber, provider.answerStatusesList, provider.marathonDetailModel.totalQuestions!, provider.isUserOutOfGame), stepper(provider.currentQuestionNumber, provider.answerStatusesList, provider.marathonDetailModel.totalQuestions!, provider.isUserOutOfGame),
8.height, 8.height,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
"${provider.currentQuestionNumber == 1 ? 0 : (((provider.currentQuestionNumber - 1) / provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}" "${provider.currentQuestionNumber == 1 ? 0 : (((provider.currentQuestionNumber - 1) / provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}"
.toText14(), .toText14(),
provider.isUserOutOfGame
? LocaleKeys.youAreOutOfContest.tr().toText14(color: MyColors.redColor)
: const SizedBox(),
], ],
), ),
], ],

@ -1,7 +1,9 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
@ -20,7 +22,6 @@ class _QualifiersContainerState extends State<QualifiersContainer> {
@override @override
void initState() { void initState() {
widget.provider.startTimerForWinnerSelection(); widget.provider.startTimerForWinnerSelection();
widget.provider.callGetSelectedWinnersApi();
super.initState(); super.initState();
} }
@ -42,14 +43,23 @@ class _QualifiersContainerState extends State<QualifiersContainer> {
], ],
), ),
10.height, 10.height,
Row( widget.provider.isGettingQualifiers
crossAxisAlignment: CrossAxisAlignment.end, ? Container(
children: <Widget>[ alignment: Alignment.centerLeft,
widget.provider.totalQualifiers != null ? widget.provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true) : const SizedBox(), child: Lottie.asset(
2.width, MyLottieConsts.loadingLottie,
LocaleKeys.qualifiers.tr().toText16(color: MyColors.greenColor), width: 60,
], fit: BoxFit.fill,
), ),
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
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),
],
),
], ],
), ),
); );

@ -9,7 +9,8 @@ import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer_detail_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_banner.dart';
class MarathonTimerCard extends StatelessWidget { class MarathonTimerCard extends StatelessWidget {
@ -43,7 +44,11 @@ class MarathonTimerCard extends StatelessWidget {
], ],
), ),
Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200), Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200),
BuildCountdownTimer(timeToMarathon: timeToMarathon, provider: provider, screenFlag: 1), CountdownTimerForDetailScreen(
// timeToMarathon: dummyTime,
timeToMarathon: timeToMarathon,
provider: provider,
),
], ],
), ),
); );

@ -65,7 +65,6 @@ class CardContent extends StatelessWidget {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Container( Container(
height: 78,
width: double.infinity, width: double.infinity,
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -84,9 +83,9 @@ class CardContent extends StatelessWidget {
), ),
child: Center( child: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 13), padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 15),
child: Text( child: Text(
AppState().isArabic(context) ? provider.currentQuestion.titleAr ?? "" : provider.currentQuestion.titleEn ?? "", AppState().isArabic(context) ? "${provider.currentQuestion.titleAr}" ?? "" : provider.currentQuestion.titleEn ?? "",
style: const TextStyle( style: const TextStyle(
color: MyColors.white, color: MyColors.white,
fontSize: 16, fontSize: 16,
@ -110,7 +109,7 @@ class AnswerContent extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>(); MarathonProvider provider = context.watch<MarathonProvider>();
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 31, horizontal: 13), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 13),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: MyColors.kWhiteColor, color: MyColors.kWhiteColor,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
@ -126,7 +125,11 @@ class AnswerContent extends StatelessWidget {
return AnswerTileForText( return AnswerTileForText(
index: index, index: index,
onAnswerTapped: () { onAnswerTapped: () {
provider.updateCurrentQuestionOptionStatus(QuestionsOptionStatus.selected, index); if (provider.totalCurrentQuestionTime - provider.currentGapTime <= 1) {
null;
} else {
provider.updateCurrentQuestionOptionStatus(QuestionsOptionStatus.selected, index);
}
}, },
); );
}, },
@ -169,7 +172,7 @@ class AnswerTileForText extends StatelessWidget {
child: Center( child: Center(
child: (AppState().isArabic(context) ? provider.currentQuestion.questionOptions![index].titleAr! : provider.currentQuestion.questionOptions![index].titleEn!) child: (AppState().isArabic(context) ? provider.currentQuestion.questionOptions![index].titleAr! : provider.currentQuestion.questionOptions![index].titleEn!)
.toText16(color: provider.isUserOutOfGame ? MyColors.darkTextColor : getAnswerTextColor(provider.currentQuestion.questionOptions![index].optionStatus!)) .toText16(color: provider.isUserOutOfGame ? MyColors.darkTextColor : getAnswerTextColor(provider.currentQuestion.questionOptions![index].optionStatus!))
.paddingOnly(top: 17, bottom: 17), .paddingOnly(top: 13, bottom: 13),
), ),
), ),
); );

@ -1,93 +0,0 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
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/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/widgets/app_bar_widget.dart';
class WinnerScreen extends StatelessWidget {
const WinnerScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: MyColors.backgroundColor,
appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
60.height,
Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
children: <Widget>[
SizedBox(
height: 200,
child: Stack(
children: <Widget>[
Lottie.asset(
MyLottieConsts.celebrate1Lottie,
height: 200,
),
Lottie.asset(
MyLottieConsts.celebrate2Lottie,
height: 200,
),
],
),
),
26.height,
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: "WINNER".toText32(color: MyColors.white, isBold: true).paddingOnly(top: 07),
)
],
),
),
12.height,
"Muhammad Shrouff".toText22(color: MyColors.grey3AColor),
"837436".toText22(color: MyColors.grey57Color),
80.height,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
LocaleKeys.sponsoredBy.tr().toText14(color: MyColors.grey77Color),
" Extra".toText14(color: MyColors.darkTextColor, isBold: true),
],
),
5.height,
Image.asset(
"assets/images/logos/main_mohemm_logo.png",
height: 40,
fit: BoxFit.fill,
width: 150,
)
],
),
).paddingOnly(left: 21, right: 21),
10.height,
],
),
),
);
}
}

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

@ -132,7 +132,7 @@ class ServicesHeaderShimmer extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
"Other".tr().toText10().toShimmer(), LocaleKeys.otherCharges.tr().toText10().toShimmer(),
6.height, 6.height,
LocaleKeys.services.tr().toText12(isBold: true).toShimmer(), LocaleKeys.services.tr().toText12(isBold: true).toShimmer(),
], ],

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

Loading…
Cancel
Save