# Conflicts:
#	lib/classes/consts.dart
sultan-dev
Sultan khan 2 years ago
commit a212963ad7

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

@ -502,7 +502,12 @@
"favorite": "مفضلتي",
"searchfromchat": "البحث من الدردشة",
"yourAnswerCorrect": "إجابتك صحيحة",
"youMissedTheQuestion": "فاتك !! أنت خارج اللعبة. لكن يمكنك المتابعة.",
"wrongAnswer": "إجابة خاطئة! أنت خارج اللعبة. لكن يمكنك المتابعة."
"youMissedTheQuestion": "نفد منك الوقت. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.",
"wrongAnswer": "إجابتك غير صحيحة. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.",
"oops": "أوه!!!",
"winner": "الفائز",
"youWantToLeaveMarathon": "هل أنت متأكد أنك تريد العودة؟ سوف تخرج من المسابقة.",
"ourSponsor": "راعينا:",
"startingIn": "يبدأ في",
"youAreOutOfContest": "أنت خارج المسابقة."
}

@ -499,10 +499,16 @@
"codeExpire": "The verification code has been expired",
"allQuestionsCorrect": "You have answered all questions correct",
"typeheretoreply": "Type here to reply",
"favorite" : "My Favorites",
"favorite": "My Favorites",
"searchfromchat": "Search from chat",
"yourAnswerCorrect": "Your answer is correct",
"youMissedTheQuestion": "You Missed !! You are out of the game. But you can follow up.",
"wrongAnswer": "Wrong Answer! You are out of the game. But you can follow up."
"youMissedTheQuestion": "You ran out of time. You are out of the game. But you can continue and as a viewer.",
"wrongAnswer": "Your answer is Incorrect. You are out of the game. But you can continue and as a viewer.",
"oops": "Ooopsss!!!!",
"winner": "WINNER",
"youWantToLeaveMarathon": "Are you sure you want to go back? You will be out of the contest.",
"ourSponsor": "Our Sponsor:",
"startingIn": "Starting in",
"youAreOutOfContest": "You are out of the contest."
}

File diff suppressed because one or more lines are too long

@ -1,22 +1,19 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:logger/logger.dart' as L;
import 'package:logging/logging.dart';
import 'package:mohem_flutter_app/api/api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_generic_model.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_model.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:provider/provider.dart';
import 'package:signalr_netcore/http_connection_options.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/hub_connection_builder.dart';
class MarathonApiClient {
late HubConnection hubConnection;
L.Logger logger = L.Logger();
Future<String> getMarathonToken() async {
String employeeUserName = AppState().getUserName ?? "";
String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? "";
@ -55,11 +52,9 @@ class MarathonApiClient {
AppState().setMarathonProjectId = marathonModel.data[0]["id"] ?? "";
return marathonModel.data[0]["id"] ?? "";
} else {
//TODO : DO ERROR HANDLING HERE
return "";
}
} else {
//TODO : DO ERROR HANDLING HERE
return "";
}
}
@ -70,9 +65,14 @@ class MarathonApiClient {
Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonUpcomingUrl + payrollString, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
logger.i("json in getMarathonDetails: $json");
MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json);
if (marathonGenericModel.data == null) {
return MarathonDetailModel();
}
MarathonDetailModel marathonDetailModel = MarathonDetailModel.fromJson(marathonGenericModel.data);
AppState().setMarathonProjectId = marathonDetailModel.id!;
@ -80,99 +80,165 @@ class MarathonApiClient {
return marathonDetailModel;
}
late HubConnection hubConnection;
L.Logger logger = L.Logger();
Future<bool> joinMarathonAsParticipant() async {
Map<String, String> jsonObject = <String, String>{
"employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "",
"employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "",
"marathonId": AppState().getMarathonProjectId!,
};
Future<void> buildHubConnection(BuildContext context) async {
HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
hubConnection = HubConnectionBuilder()
.withUrl(
ApiConsts.marathonHubConnectionUrl + "?employeeNumber=${AppState().memberInformationList!.eMPLOYEENUMBER ?? ""}&employeeName=${AppState().memberInformationList!.eMPLOYEENAME ?? ""}",
options: httpOptions,
)
.withAutomaticReconnect(
retryDelays: <int>[2000, 5000, 10000, 20000],
)
.configureLogging(
Logger("Logging"),
)
.build();
hubConnection.onclose(
({Exception? error}) {
logger.i("onclose");
},
);
hubConnection.onreconnecting(
({Exception? error}) {
logger.i("onreconnecting");
},
);
hubConnection.onreconnected(
({String? connectionId}) {
logger.i("onreconnected");
},
);
if (hubConnection.state != HubConnectionState.Connected) {
await hubConnection.start();
logger.i("Started HubConnection");
await hubConnection.invoke(
"AddParticipant",
args: <Object>[
<String, dynamic>{
"employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "",
"employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "",
"marathonId": AppState().getMarathonProjectId,
}
],
).catchError((e) {
logger.i("Error in AddParticipant: $e");
});
context.read<MarathonProvider>().addItemToList(ApiConsts.dummyQuestion);
await hubConnection.invoke(
"SendQuestionToParticipant",
args: <Object>[
<String, dynamic>{
"marathonId": "${AppState().getMarathonProjectId}",
}
],
).catchError((e) {
logger.i("Error in SendQuestionToParticipant: $e");
});
try {
hubConnection.on("OnSendQuestionToParticipant", (List<Object?>? arguments) {
onSendQuestionToParticipant(arguments, context);
});
} catch (e, s) {
logger.i("Error in OnSendQuestionToParticipant");
}
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonJoinParticipantUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 208) {
// means participant is already in the marathon i.e already joined
return true;
}
try {
hubConnection.on("OnParticipantJoin", (List<Object?>? arguments) {
onParticipantJoin(arguments, context);
});
} catch (e, s) {
logger.i("Error in OnParticipantJoin");
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
logger.i("joinMarathonAsParticipant: ${marathonModel.data}");
return true;
} else {
return false;
}
} else {
return false;
}
}
Future<void> onSendQuestionToParticipant(List<Object?>? arguments, BuildContext context) async {
logger.i("onSendQuestionToParticipant arguments: $arguments");
Future<QuestionModel?> getNextQuestion({required String? questionId, required String marathonId}) async {
Map<String, String?> jsonObject = <String, String?>{
"questionId": questionId,
"marathonId": marathonId,
};
if (arguments != null) {
Map<dynamic, dynamic> data = arguments.first! as Map<dynamic, dynamic>;
var json = data["data"];
QuestionModel newQuestion = QuestionModel.fromJson(json);
context.read<MarathonProvider>().onNewQuestionReceived(newQuestion);
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonNextQuestionUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
var data = json["data"];
if (data != null) {
QuestionModel newQuestion = QuestionModel.fromJson(data);
return newQuestion;
} else {
return null;
}
}
Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
logger.i("OnParticipantJoin arguments: $arguments");
context.watch<MarathonProvider>().totalMarathoners++;
Future<bool> submitSelectedOption({required String? selectedAnswerId}) async {
Map<String, String?> jsonObject = <String, String?>{"selectedOptionId": selectedAnswerId};
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonSubmitAnswerUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
logger.i("json: $json");
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.isSuccessful == null) {
return false;
}
return marathonModel.isSuccessful!;
}
// Future<void> buildHubConnection(BuildContext context, String prizeId) async {
// HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
// hubConnection = HubConnectionBuilder()
// .withUrl(
// ApiConsts.marathonHubConnectionUrl + "?employeeNumber=${AppState().memberInformationList!.eMPLOYEENUMBER ?? ""}&employeeName=${AppState().memberInformationList!.eMPLOYEENAME ?? ""}",
// options: httpOptions,
// )
// .withAutomaticReconnect(
// retryDelays: <int>[2000, 5000, 10000, 20000],
// )
// .configureLogging(
// Logger("Logging"),
// )
// .build();
// hubConnection.onclose(
// ({Exception? error}) {
// logger.i("onclose");
// },
// );
// hubConnection.onreconnecting(
// ({Exception? error}) {
// logger.i("onreconnecting");
// },
// );
// hubConnection.onreconnected(
// ({String? connectionId}) {
// logger.i("onreconnected");
// },
// );
// if (hubConnection.state != HubConnectionState.Connected) {
// await hubConnection.start();
// logger.i("Started HubConnection");
//
// await hubConnection.invoke(
// "AddParticipant",
// args: <Object>[
// <String, dynamic>{
// "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "",
// "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "",
// "marathonId": AppState().getMarathonProjectId,
// "prizeId": "8577B2E8-5DD7-43F0-10DD-08DACB0AC064",
// }
// ],
// ).catchError((e) {
// logger.i("Error in AddParticipant: $e");
// });
//
// context.read<MarathonProvider>().addItemToList(ApiConsts.dummyQuestion);
//
// await hubConnection.invoke(
// "SendQuestionToParticipant",
// args: <Object>[
// <String, dynamic>{
// "marathonId": "${AppState().getMarathonProjectId}",
// }
// ],
// ).catchError((e) {
// Utils.confirmDialog(context, e.toString());
// logger.i("Error in SendQuestionToParticipant: $e");
// });
//
// try {
// hubConnection.on("OnSendQuestionToParticipant", (List<Object?>? arguments) {
// onSendQuestionToParticipant(arguments, context);
// });
// } catch (e, s) {
// logger.i("Error in OnSendQuestionToParticipant");
// }
//
// try {
// hubConnection.on("OnParticipantJoin", (List<Object?>? arguments) {
// onParticipantJoin(arguments, context);
// });
// } catch (e, s) {
// logger.i("Error in OnParticipantJoin");
// }
// }
// }
//
// Future<void> onSendQuestionToParticipant(List<Object?>? arguments, BuildContext context) async {
// logger.i("onSendQuestionToParticipant arguments: $arguments");
//
// if (arguments != null) {
// Map<dynamic, dynamic> data = arguments.first! as Map<dynamic, dynamic>;
// var json = data["data"];
// QuestionModel newQuestion = QuestionModel.fromJson(json);
// AppRoutes.navigatorKey.currentContext!.read<MarathonProvider>().onNewQuestionReceived(newQuestion);
// }
// }
//
// Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
// logger.i("OnParticipantJoin arguments: $arguments");
// context.watch<MarathonProvider>().totalMarathoners++;
// }
}

@ -26,11 +26,15 @@ class ApiConsts {
static String chatUserImages = chatServerBaseUrl + "empservice/api/employee/";
//Brain Marathon Constants
static String marathonBaseUrl = "https://marathoon.com/service/";
static String marathonParticipantLoginUrl = marathonBaseUrl + "api/auth/participantlogin";
static String marathonProjectGetUrl = marathonBaseUrl + "api/Project/Project_Get";
static String marathonUpcomingUrl = marathonBaseUrl + "api/marathon/upcoming/";
static String marathonHubConnectionUrl = marathonBaseUrl + "MarathonBroadCast";
static String marathonBaseUrl = "https://marathoon.com/service/api/";
static String marathonParticipantLoginUrl = marathonBaseUrl + "auth/participantlogin";
static String marathonProjectGetUrl = marathonBaseUrl + "Project/Project_Get";
static String marathonUpcomingUrl = marathonBaseUrl + "marathon/upcoming/";
static String marathonJoinParticipantUrl = marathonBaseUrl + "participant/participant_join";
static String marathonNextQuestionUrl = marathonBaseUrl + "question/next";
static String marathonSubmitAnswerUrl = marathonBaseUrl + "question/submit";
static String marathonQualifiersUrl = marathonBaseUrl + "winner/getWinner/";
static String marathonSelectedWinner = marathonBaseUrl + "winner/getSelectedWinner/";
//DummyCards for the UI

@ -4,6 +4,7 @@ class MyLottieConsts {
static const String celebrate2Lottie = "assets/lottie/celebrate2.json";
static const String winnerLottie = "assets/lottie/winner3.json";
static const String allQuestions = "assets/lottie/all_questions.json";
static const String marathonWaiting = "assets/lottie/marathon_waiting.json";
static const String wrongAnswerGif = "assets/images/wrong_answer.gif";
static const String congratsGif = "assets/images/congrats.gif";
}

@ -86,6 +86,11 @@ class Utils {
return prefs.getString(key) ?? "";
}
static Future<bool> removeStringFromPrefs(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
static Future<bool> saveStringFromPrefs(String key, String value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return await prefs.setString(key, value);

@ -9,7 +9,6 @@ import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart';
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart';
import 'package:mohem_flutter_app/ui/landing/itg/survey_screen.dart';
import 'package:mohem_flutter_app/ui/landing/today_attendance_screen.dart';
import 'package:mohem_flutter_app/ui/landing/today_attendance_screen2.dart';
import 'package:mohem_flutter_app/ui/leave_balance/add_leave_balance_screen.dart';
import 'package:mohem_flutter_app/ui/leave_balance/leave_balance_screen.dart';
@ -21,6 +20,8 @@ import 'package:mohem_flutter_app/ui/login/verify_last_login_screen.dart';
import 'package:mohem_flutter_app/ui/login/verify_login_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_intro_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_sponsor_video_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_waiting_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_winner_selection.dart';
import 'package:mohem_flutter_app/ui/marathon/winner_screen.dart';
import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart';
@ -66,6 +67,7 @@ import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transa
import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transactions_details.dart';
import 'package:mohem_flutter_app/ui/screens/submenu_screen.dart';
import 'package:mohem_flutter_app/ui/termination/end_employement.dart';
import 'package:mohem_flutter_app/ui/unsafe_device_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/item_history_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/itg_detail_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart';
@ -73,6 +75,8 @@ import 'package:mohem_flutter_app/ui/work_list/worklist_detail_screen.dart';
import 'package:mohem_flutter_app/ui/work_list/worklist_settings.dart';
class AppRoutes {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
static const String splash = "/splash";
static const String registerSelection = "/registerSelection";
static const String loginVerifyAccount = "/loginVerifyAccount";
@ -185,6 +189,10 @@ class AppRoutes {
static const String marathonScreen = "/marathonScreen";
static const String marathonWinnerSelection = "/marathonWinnerSelection";
static const String marathonWinnerScreen = "/marathonWinnerScreen";
static const String marathonSponsorVideoScreen = "/marathonSponsorVideoScreen";
static const String marathonWaitingScreen = "/marathonWaitingScreen";
static const String unsafeDeviceScreen = "/unsafeDeviceScreen";
static final Map<String, WidgetBuilder> routes = {
login: (BuildContext context) => LoginScreen(),
@ -293,5 +301,9 @@ class AppRoutes {
marathonScreen: (BuildContext context) => MarathonScreen(),
marathonWinnerSelection: (BuildContext context) => MarathonWinnerSelection(),
marathonWinnerScreen: (BuildContext context) => WinnerScreen(),
marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(),
marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(),
unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(),
};
}

@ -90,8 +90,9 @@ extension EmailValidator on String {
style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.52, decoration: isUnderLine ? TextDecoration.underline : null),
);
Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : TextAlign.left,
maxLines: maxlines,
style: TextStyle(
color: color ?? MyColors.darkTextColor,
@ -118,8 +119,9 @@ extension EmailValidator on String {
style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
);
Widget toText18({Color? color, bool isBold = false}) => Text(
Widget toText18({Color? color, bool isBold = false, bool isCentered = false}) => Text(
this,
textAlign: isCentered ? TextAlign.center : null,
style: TextStyle(fontSize: 18, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
);

@ -490,5 +490,11 @@ abstract class LocaleKeys {
static const yourAnswerCorrect = 'yourAnswerCorrect';
static const youMissedTheQuestion = 'youMissedTheQuestion';
static const wrongAnswer = 'wrongAnswer';
static const oops = 'oops';
static const winner = 'winner';
static const youWantToLeaveMarathon = 'youWantToLeaveMarathon';
static const ourSponsor = 'ourSponsor';
static const startingIn = 'startingIn';
static const youAreOutOfContest = 'youAreOutOfContest';
}

@ -3,11 +3,11 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:logger/logger.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/config/routes.dart';
import 'package:mohem_flutter_app/generated/codegen_loader.g.dart';
import 'package:mohem_flutter_app/models/post_params_model.dart';
import 'package:mohem_flutter_app/provider/chat_provider_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/provider/eit_provider_model.dart';
import 'package:mohem_flutter_app/theme/app_theme.dart';
@ -92,6 +92,7 @@ class MyApp extends StatelessWidget {
MonthYearPickerLocalizations.delegate,
);
return MaterialApp(
navigatorKey: AppRoutes.navigatorKey,
builder: (BuildContext context, Widget? child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
@ -249,3 +250,4 @@ class MyApp extends StatelessWidget {
// });
// }
// }

@ -126,10 +126,10 @@ class Projects {
class Sponsors {
String? id;
String? nameEn;
Null? nameAr;
String? nameAr;
String? image;
Null? video;
Null? logo;
String? video;
String? logo;
List<SponsorPrizes>? sponsorPrizes;
Sponsors(
@ -186,7 +186,7 @@ class SponsorPrizes {
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = new Map<String, dynamic>();
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['marathonPrizeEn'] = marathonPrizeEn;
data['marathonPrizeAr'] = marathonPrizeAr;

@ -14,6 +14,7 @@ class QuestionModel {
String? gapText;
String? gapImage;
int? questOptionsLimit;
int? remainingParticipantCount;
List<QuestionOptions>? questionOptions;
QuestionModel({
@ -28,6 +29,7 @@ class QuestionModel {
String? gapText,
String? gapImage,
int? questOptionsLimit,
int? remainingParticipantCount,
List<QuestionOptions>? questionOptions,
});
@ -43,6 +45,7 @@ class QuestionModel {
gapText = json['gapText'];
gapImage = json['gapImage'];
questOptionsLimit = json['questOptionsLimit'];
remainingParticipantCount = json['remainingParticipantCount'];
if (json['questionOptions'] != null) {
questionOptions = <QuestionOptions>[];
json['questionOptions'].forEach((v) {
@ -64,6 +67,7 @@ class QuestionModel {
data['gapText'] = gapText;
data['gapImage'] = gapImage;
data['questOptionsLimit'] = questOptionsLimit;
data['remainingParticipantCount'] = remainingParticipantCount;
if (questionOptions != null) {
data['questionOptions'] = questionOptions!.map((v) => v.toJson()).toList();
}

@ -6,6 +6,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:just_audio/just_audio.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
@ -54,6 +55,8 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Future<void> buildHubConnection() async {
chatHubConnection = await getHubConnection();
await chatHubConnection.start();
print("Startedddddddd");
chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
}
Future<HubConnection> getHubConnection() async {
@ -75,7 +78,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void registerEvents() {
chatHubConnection.on("OnUpdateUserStatusAsync", changeStatus);
chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
// chatHubConnection.on("OnDeliveredChatUserAsync", onMsgReceived);
// hubConnection.on("OnSeenChatUserAsync", onChatSeen);
//hubConnection.on("OnUserTypingAsync", onUserTyping);
chatHubConnection.on("OnUserCountAsync", userCountAsync);
@ -122,7 +125,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
),
);
notifyListeners();
getUserImages();
if (searchedChats!.isNotEmpty) {
getUserImages();
}
}
Future invokeUserChatHistoryNotDeliveredAsync({required int userId}) async {
@ -135,6 +140,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
if (isNewChat) userChatHistory = [];
if (!loadMore) paginationVal = 0;
isChatScreenActive = true;
// if (chatHubConnection.state != HubConnectionState.Connected) {
// getUserAutoLoginToken().whenComplete(() async {
// await buildHubConnection();
// getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore);
// });
// return;
// }
Response response = await ChatApiClient().getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
if (response.statusCode == 204) {
if (isNewChat) {
@ -314,8 +326,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
Future<void> onMsgReceived(List<Object?>? parameters) async {
List<SingleUserChatModel> data = [];
List<SingleUserChatModel> temp = [];
List<SingleUserChatModel> data = [], temp = [];
for (dynamic msg in parameters!) {
data = getSingleUserChatModel(jsonEncode(msg));
temp = getSingleUserChatModel(jsonEncode(msg));
@ -336,16 +347,22 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
}
}
dynamic contain = searchedChats!.where((ChatUser element) => element.id == data.first.currentUserId);
if (contain.isEmpty) {
searchedChats!.add(ChatUser(id: data.first.currentUserId, userName: data.first.currentUserName, unreadMessageCount: 0, isImageLoading: false, image: "", isImageLoaded: true, userStatus: 1));
}
setMsgTune();
userChatHistory.insert(0, data.first);
if (searchedChats != null && !isChatScreenActive) {
for (ChatUser user in searchedChats!) {
if (user.id == data.first.currentUserId) {
var tempCount = user.unreadMessageCount ?? 0;
int tempCount = user.unreadMessageCount ?? 0;
user.unreadMessageCount = tempCount + 1;
}
}
sort();
}
List list = [
@ -355,6 +372,14 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners();
}
void sort() {
searchedChats!.sort(
(ChatUser a, ChatUser b) => b.unreadMessageCount!.compareTo(
a.unreadMessageCount!,
),
);
}
void onUserTyping(List<Object?>? parameters) {
for (ChatUser user in searchedChats!) {
if (user.id == parameters![1] && parameters[0] == true) {
@ -448,34 +473,35 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Uint8List? image,
required bool isImageLoaded}) async {
Uuid uuid = const Uuid();
var contentNo = uuid.v4();
String contentNo = uuid.v4();
var msg = message.text;
String msg = message.text;
SingleUserChatModel data = SingleUserChatModel(
chatEventId: chatEventId,
chatSource: 1,
contant: msg,
contantNo: contentNo,
conversationId: chatCID,
createdDate: DateTime.now(),
currentUserId: AppState().chatDetails!.response!.id,
currentUserName: AppState().chatDetails!.response!.userName,
targetUserId: targetUserId,
targetUserName: targetUserName,
isReplied: false,
fileTypeId: fileTypeId,
userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null,
fileTypeResponse: isAttachment
? FileTypeResponse(
fileTypeId: fileTypeId,
fileTypeName: getFileType(getFileExtension(selectedFile.path).toString()),
fileKind: getFileExtension(selectedFile.path),
fileName: selectedFile.path.split("/").last,
fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
)
: null,
image: image,
isImageLoaded: isImageLoaded);
chatEventId: chatEventId,
chatSource: 1,
contant: msg,
contantNo: contentNo,
conversationId: chatCID,
createdDate: DateTime.now(),
currentUserId: AppState().chatDetails!.response!.id,
currentUserName: AppState().chatDetails!.response!.userName,
targetUserId: targetUserId,
targetUserName: targetUserName,
isReplied: false,
fileTypeId: fileTypeId,
userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null,
fileTypeResponse: isAttachment
? FileTypeResponse(
fileTypeId: fileTypeId,
fileTypeName: getFileType(getFileExtension(selectedFile.path).toString()),
fileKind: getFileExtension(selectedFile.path),
fileName: selectedFile.path.split("/").last,
fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()),
)
: null,
image: image,
isImageLoaded: isImageLoaded,
);
userChatHistory.insert(0, data);
isFileSelected = false;
isMsgReply = false;
@ -492,7 +518,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) {
searchedChats!.add(
ChatUser(id: targetUserId, userName: targetUserName, unreadMessageCount: 0),
ChatUser(id: targetUserId, userName: targetUserName, unreadMessageCount: 0, isImageLoading: false, image: "", isImageLoaded: true),
);
notifyListeners();
}
@ -729,11 +755,25 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
}
}
}
notifyListeners();
}
///getUserAutoLoginToken().whenComplete(() {
// buildHubConnection();
// print("After Reconnect State: " + hubConnection.state.toString());
// });
void setMsgTune() async {
AudioPlayer player = AudioPlayer();
await player.setVolume(1.0);
String audioAsset = "";
if (Platform.isAndroid) {
audioAsset = "assets/audio/pulse_tone_android.mp3";
} else {
audioAsset = "assets/audio/pulse_tune_ios.caf";
}
try {
await player.setAsset(audioAsset);
await player.load();
player.play();
} catch (e) {
print("Error: $e");
}
}
}

@ -239,8 +239,7 @@ class ChatBubble extends StatelessWidget {
return SizedBox(
height: isReplyPreview ? 32 : 140,
width: isReplyPreview ? 32 : 227,
child: const Center(child: CircularProgressIndicator()),
);
).toShimmer();
}
},
);

@ -16,13 +16,11 @@ 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/ui/chat/call/chat_outgoing_call_screen.dart';
import 'package:mohem_flutter_app/ui/chat/chat_bubble.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/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:sizer/sizer.dart';
import 'package:swipe_to/swipe_to.dart';
class ChatDetailScreen extends StatefulWidget {
@ -34,9 +32,9 @@ class ChatDetailScreen extends StatefulWidget {
}
class _ChatDetailScreenState extends State<ChatDetailScreen> {
final RefreshController _rc = RefreshController(initialRefresh: false);
dynamic userDetails;
late ChatProviderModel data;
final RefreshController _rc = RefreshController(initialRefresh: false);
void getMoreChat() async {
if (userDetails != null) {
@ -165,6 +163,10 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
),
if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg")
SizedBox(height: 200, width: double.infinity, child: Image.file(m.selectedFile, fit: BoxFit.cover)).paddingOnly(left: 21, right: 21, top: 21),
const Divider(
height: 1,
color: MyColors.lightGreyEFColor,
),
TextField(
controller: m.message,
decoration: InputDecoration(

@ -85,6 +85,7 @@ class _ChatHomeState extends State<ChatHome> {
onPageChanged: (int pageIndex) {
setState(() {
tabIndex = pageIndex;
});
},
children: <Widget>[

@ -19,6 +19,7 @@ import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_s
import 'package:mohem_flutter_app/widgets/circular_avatar.dart';
import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart';
import 'package:provider/provider.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
class ChatHomeScreen extends StatefulWidget {
const ChatHomeScreen({Key? key}) : super(key: key);
@ -31,6 +32,8 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
TextEditingController search = TextEditingController();
late ChatProviderModel data;
final RefreshController _rc = RefreshController(initialRefresh: false);
@override
void initState() {
super.initState();
@ -86,7 +89,7 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
itemCount: m.searchedChats!.length,
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(bottom: 80.0),
padding: const EdgeInsets.only(bottom: 80.0),
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 55,
@ -99,7 +102,7 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
height: 48,
width: 48,
).toShimmer().circle(30),
if (m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image != null && m.searchedChats![index].image.isNotEmpty)
if (m.searchedChats![index].isImageLoaded! && m.searchedChats![index].image.isNotEmpty)
CircularAvatar(
radius: 20,
height: 48,
@ -125,7 +128,9 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
).circle(10),
)
],
),
).onPress(() {
print(jsonEncode(m.searchedChats![index]));
}),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,

@ -23,13 +23,16 @@ class ChatFavoriteUsersScreen extends StatelessWidget {
body: Consumer<ChatProviderModel>(
builder: (BuildContext context, ChatProviderModel m, Widget? child) {
if (m.isLoading) {
return ChatHomeShimmer(isDetailedScreen: false,);
return ChatHomeShimmer(
isDetailedScreen: false,
);
} else {
return m.favUsersList != null && m.favUsersList.isNotEmpty
? ListView.separated(
itemCount: m.favUsersList!.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
physics: const ClampingScrollPhysics(),
padding: const EdgeInsets.only(bottom: 80.0),
itemBuilder: (BuildContext context, int index) {
return SizedBox(
height: 55,

@ -6,7 +6,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
@ -322,9 +321,11 @@ class _DashboardScreenState extends State<DashboardScreen> {
),
],
).paddingOnly(left: 21, right: 21, top: 7),
context.watch<MarathonProvider>().isLoading ? MarathonBannerShimmer().paddingAll(20) : MarathonBanner().paddingAll(20),
context.watch<MarathonProvider>().isLoading
? const MarathonBannerShimmer().paddingAll(20)
: MarathonBanner(isMarathonUpcoming: context.watch<MarathonProvider>().isUpComingMarathon).paddingAll(20),
ServicesWidget(),
// 8.height,
8.height,
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 31),

@ -25,6 +25,7 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:mohem_flutter_app/widgets/input_widget.dart';
import 'package:safe_device/safe_device.dart';
class LoginScreen extends StatefulWidget {
LoginScreen({Key? key}) : super(key: key);
@ -48,10 +49,34 @@ class _LoginScreenState extends State<LoginScreen> {
bool? isAppOpenBySystem;
bool isJailBroken = false;
bool isRealDevice = false;
bool isOnExternalStorage = false;
bool isDevelopmentModeEnable = false;
@override
void initState() {
super.initState();
// checkFirebaseToken();
// checkFirebaseToken();
if (kReleaseMode) {
checkDeviceSafety();
}
}
void checkDeviceSafety() async {
try {
isJailBroken = await SafeDevice.isJailBroken;
isRealDevice = await SafeDevice.isRealDevice;
if (Platform.isAndroid) {
isOnExternalStorage = await SafeDevice.isOnExternalStorage;
isDevelopmentModeEnable = await SafeDevice.isDevelopmentModeEnable;
}
if (isJailBroken || !isRealDevice || isOnExternalStorage || isDevelopmentModeEnable) {
Navigator.pushNamedAndRemoveUntil(context, AppRoutes.unsafeDeviceScreen, (_) => false);
}
} catch (error) {
print(error);
}
}
@override
@ -68,8 +93,7 @@ class _LoginScreenState extends State<LoginScreen> {
await Firebase.initializeApp();
_firebaseMessaging = FirebaseMessaging.instance;
firebaseToken = await _firebaseMessaging.getToken();
loginInfo = await LoginApiClient().getMobileLoginInfoNEW(
firebaseToken ?? "", Platform.isAndroid ? "android" : "ios");
loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios");
if (loginInfo == null) {
Utils.hideLoading(context);
return;
@ -86,11 +110,9 @@ class _LoginScreenState extends State<LoginScreen> {
}
Future<void> checkPrefs() async {
String username =
await Utils.getStringFromPrefs(SharedPrefsConsts.username);
String username = await Utils.getStringFromPrefs(SharedPrefsConsts.username);
if (username.isNotEmpty) {
String password =
await Utils.getStringFromPrefs(SharedPrefsConsts.password);
String password = await Utils.getStringFromPrefs(SharedPrefsConsts.password);
// String firebaseToken = await Utils.getStringFromPrefs(SharedPrefsConsts.firebaseToken);
// print("firebaseToken:$firebaseToken");
this.username.text = username;
@ -103,30 +125,23 @@ class _LoginScreenState extends State<LoginScreen> {
Utils.showLoading(context);
try {
_checkMobileAppVersion = await LoginApiClient().checkMobileAppVersion();
_memberLoginList =
await LoginApiClient().memberLogin(username.text, password.text);
_memberLoginList = await LoginApiClient().memberLogin(username.text, password.text);
AppState().setMemberLoginListModel = _memberLoginList;
AppState().setUserName = username.text;
AppState().password = password.text;
if (_autoLogin) {
AppState().setMemberInformationListModel =
(await MemberInformationListModel.getFromPrefs()).first;
AppState().setPrivilegeListModel =
await PrivilegeListModel.getFromPrefs();
String mohemmWifiSSID =
await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiSSID);
String mohemmWifiPassword = await Utils.getStringFromPrefs(
SharedPrefsConsts.mohemmWifiPassword);
AppState().setMemberInformationListModel = (await MemberInformationListModel.getFromPrefs()).first;
AppState().setPrivilegeListModel = await PrivilegeListModel.getFromPrefs();
String mohemmWifiSSID = await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiSSID);
String mohemmWifiPassword = await Utils.getStringFromPrefs(SharedPrefsConsts.mohemmWifiPassword);
AppState().setMohemmWifiSSID = mohemmWifiSSID;
AppState().setMohemmWifiPassword = mohemmWifiPassword;
}
Utils.hideLoading(context);
if (_autoLogin) {
Navigator.pushReplacementNamed(context, AppRoutes.verifyLastLogin,
arguments: loginInfo);
Navigator.pushReplacementNamed(context, AppRoutes.verifyLastLogin, arguments: loginInfo);
} else {
Navigator.pushNamed(context, AppRoutes.verifyLogin,
arguments: "$firebaseToken");
Navigator.pushNamed(context, AppRoutes.verifyLogin, arguments: "$firebaseToken");
}
Utils.saveStringFromPrefs(SharedPrefsConsts.password, password.text);
} catch (ex) {
@ -169,13 +184,7 @@ class _LoginScreenState extends State<LoginScreen> {
Expanded(child: SizedBox()),
Row(
children: [
LocaleKeys.english
.tr()
.toText14(
color: AppState().isArabic(context)
? null
: MyColors.textMixColor)
.onPress(() {
LocaleKeys.english.tr().toText14(color: AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() {
context.setLocale(const Locale("en", "US"));
}),
Container(
@ -184,13 +193,7 @@ class _LoginScreenState extends State<LoginScreen> {
height: 16,
margin: const EdgeInsets.only(left: 10, right: 10),
),
LocaleKeys.arabic
.tr()
.toText14(
color: !AppState().isArabic(context)
? null
: MyColors.textMixColor)
.onPress(() {
LocaleKeys.arabic.tr().toText14(color: !AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() {
context.setLocale(const Locale("ar", "SA"));
}),
],
@ -206,23 +209,14 @@ class _LoginScreenState extends State<LoginScreen> {
LocaleKeys.login.tr().toText24(isBold: true),
LocaleKeys.pleaseEnterLoginDetails.tr().toText16(),
16.height,
InputWidget(
LocaleKeys.username.tr(), "123456", username),
InputWidget(LocaleKeys.username.tr(), "123456", username),
12.height,
InputWidget(
LocaleKeys.password.tr(), "xxxxxx", password,
isTextIsPassword: true),
InputWidget(LocaleKeys.password.tr(), "xxxxxx", password, isTextIsPassword: true),
9.height,
Align(
alignment: Alignment.centerRight,
child: LocaleKeys.forgotPassword
.tr()
.toText12(
isUnderLine: true,
color: MyColors.textMixColor)
.onPress(() {
Navigator.pushNamed(
context, AppRoutes.forgotPassword);
child: LocaleKeys.forgotPassword.tr().toText12(isUnderLine: true, color: MyColors.textMixColor).onPress(() {
Navigator.pushNamed(context, AppRoutes.forgotPassword);
}),
),
],

@ -1,21 +1,13 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.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/date_uitl.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_details_card.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_footer.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_timer_card.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:provider/provider.dart';
class MarathonIntroScreen extends StatelessWidget {
@ -33,7 +25,10 @@ class MarathonIntroScreen extends StatelessWidget {
children: <Widget>[
MarathonDetailsCard(provider: provider),
10.height,
MarathonTimerCard(provider: provider, timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch,),
MarathonTimerCard(
provider: provider,
timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch,
),
],
).expanded,
1.divider,
@ -43,180 +38,3 @@ class MarathonIntroScreen extends StatelessWidget {
);
}
}
class MarathonDetailsCard extends StatelessWidget {
final MarathonProvider provider;
const MarathonDetailsCard({Key? key, required this.provider}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LocaleKeys.contestTopicAbout.tr().toText16(color: MyColors.grey77Color),
"${AppState().isArabic(context) ? provider.marathonDetailModel.titleAr : provider.marathonDetailModel.titleEn}".toText20(color: MyColors.textMixColor, isBold: true),
Row(
children: <Widget>[
Flexible(
child: "${AppState().isArabic(context) ? provider.marathonDetailModel.descAr : provider.marathonDetailModel.descEn}".toText14(color: MyColors.grey77Color),
)
],
),
if (provider.itsMarathonTime && provider.marathonDetailModel.sponsors != null) ...<Widget>[
5.height,
provider.marathonDetailModel.sponsors?.first.sponsorPrizes != null
? Row(
children: <Widget>[
"${LocaleKeys.prize.tr()} ".toText16(color: MyColors.grey77Color, isBold: true),
"${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr : provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr}"
.toText16(color: MyColors.greenColor, isBold: true),
],
)
: const SizedBox(),
Row(
children: <Widget>[
"${LocaleKeys.sponsoredBy.tr()} ".toText16(color: MyColors.grey77Color),
"${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.nameAr : provider.marathonDetailModel.sponsors?.first.nameEn}"
.toText16(color: MyColors.darkTextColor, isBold: true),
],
),
10.height,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
provider.marathonDetailModel.sponsors!.first.image!,
height: 40,
width: 150,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center();
},
)
],
),
]
],
),
],
),
);
}
}
class MarathonTimerCard extends StatelessWidget {
final int timeToMarathon;
final MarathonProvider provider;
const MarathonTimerCard({
Key? key,
required this.provider,
required this.timeToMarathon,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 14),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
"${LocaleKeys.gameDate.tr()} ".toText16(color: MyColors.grey77Color),
DateUtil.getMonthDayYearDateFormatted(DateTime.parse(provider.marathonDetailModel.startTime!)).toText16(color: MyColors.darkTextColor, isBold: true),
],
),
Row(
children: <Widget>[
"${LocaleKeys.gameTime.tr()} ".toText16(color: MyColors.grey77Color),
DateUtil.formatDateToTimeLang(DateTime.parse(provider.marathonDetailModel.startTime!), AppState().isArabic(context)).toText16(color: MyColors.darkTextColor, isBold: true),
],
),
Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200),
BuildCountdownTimer(timeToMarathon: timeToMarathon, provider: provider, screenFlag: 1),
],
),
);
}
}
class MarathonFooter extends StatelessWidget {
final MarathonProvider provider;
const MarathonFooter({
Key? key,
required this.provider,
}) : super(key: key);
Widget buildNoteForDemo() {
return RichText(
text: TextSpan(
children: <InlineSpan>[
TextSpan(
text: LocaleKeys.note.tr(),
style: const TextStyle(color: MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.bold),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP1.tr(),
style: const TextStyle(color: MyColors.grey77Color, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.w500),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP2.tr(),
style: const TextStyle(color: MyColors.darkTextColor, fontSize: 17, fontWeight: FontWeight.bold),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP3.tr(),
style: const TextStyle(color: MyColors.grey77Color, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.w500),
)
],
),
).paddingOnly(right: 21, left: 21, top: 11, bottom: 0);
}
@override
Widget build(BuildContext context) {
return !provider.itsMarathonTime
? DefaultButton(
LocaleKeys.joinMarathon.tr(),
() async {
Utils.showLoading(context);
try {
provider.resetValues();
await provider.connectSignalrAndJoinMarathon(context);
} catch (e, s) {
Utils.confirmDialog(context, e.toString());
print(s);
}
Utils.hideLoading(context);
Navigator.pushNamed(context, AppRoutes.marathonScreen);
},
).insideContainer
: Container(
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
buildNoteForDemo(),
DefaultButton(
LocaleKeys.joinDemoMarathon.tr(),
() {
provider.connectSignalrAndJoinMarathon(context);
},
color: MyColors.yellowColorII,
).insideContainer,
],
),
);
}
}

@ -2,62 +2,46 @@ import 'dart:async';
import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/marathon/marathon_api_client.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_model.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
import 'package:video_player/video_player.dart';
class MarathonProvider extends ChangeNotifier {
//****************VARIABLES**********
final AppinioSwiperController swiperController = AppinioSwiperController();
MarathonDetailModel marathonDetailModel = MarathonDetailModel();
List<CardContent> cardContentList = <CardContent>[];
QuestionModel currentQuestion = QuestionModel();
List<QuestionCardStatus> answerStatusesList = <QuestionCardStatus>[];
QuestionCardStatus questionCardStatus = QuestionCardStatus.question;
int? selectedOptionIndex;
int currentQuestionTime = 0;
void onNewQuestionReceived(QuestionModel newQuestion) {
if (currentQuestionNumber > 0) {
swipeCardLeft();
}
selectedOptionIndex = null;
currentQuestionNumber++;
currentQuestion = newQuestion;
cardContentList.add(const CardContent());
currentQuestionTime = newQuestion.questionTime!;
questionCardStatus = QuestionCardStatus.question;
notifyListeners();
}
String? selectedOptionId;
int totalQualifiers = 0;
void addItemToList(CardContent value) {
cardContentList.add(value);
notifyListeners();
}
bool _isLoading = false;
void updateCurrentQuestionOptionStatus(QuestionsOptionStatus status, int index) {
for (int i = 0; i < currentQuestion.questionOptions!.length; i++) {
currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected;
}
currentQuestion.questionOptions![index].optionStatus = status;
selectedOptionIndex = index;
notifyListeners();
}
bool get isLoading => _isLoading;
void updateQuestionCardStatus(QuestionCardStatus status) {
questionCardStatus = status;
set isLoading(bool value) {
_isLoading = value;
notifyListeners();
}
bool _isLoading = false;
bool _isUpComingMarathon = true;
bool get isLoading => _isLoading;
bool get isUpComingMarathon => _isUpComingMarathon;
set isLoading(bool value) {
_isLoading = value;
set isUpComingMarathon(bool value) {
_isUpComingMarathon = value;
notifyListeners();
}
@ -79,6 +63,13 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
}
bool isUserOutOfGame = false;
set updateIsUserOutOfGame(bool value) {
isUserOutOfGame = value;
notifyListeners();
}
int _currentQuestionNumber = 0;
int get currentQuestionNumber => _currentQuestionNumber;
@ -88,7 +79,7 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
}
int _totalMarathoners = 23;
int _totalMarathoners = 0;
int get totalMarathoners => _totalMarathoners;
@ -97,8 +88,202 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
}
void swipeCardLeft() {
swiperController.swipeLeft();
//****************SPONSOR VIDEO PLAYER**********
late VideoPlayerController videoController;
Future<void> initializeVideoPlayer() async {
// videoController = VideoPlayerController.network(marathonDetailModel.sponsors!.first.video!)..initialize();
videoController = VideoPlayerController.network("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4");
await videoController.initialize();
await videoController.play();
await videoController.setVolume(1.0);
await videoController.setLooping(false);
totalSponsorVideoSeconds = videoController.value.duration.inSeconds;
notifyListeners();
}
void disposeVideoPlayer() {
videoController.dispose();
notifyListeners();
}
//****************TIMERS**********
int totalSponsorVideoSeconds = 0;
Timer timerForSponsorVideo = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimerForSponsorVideo() {
const Duration oneSec = Duration(seconds: 1);
timerForSponsorVideo = Timer.periodic(
oneSec,
(Timer timer) async {
if (totalSponsorVideoSeconds == 0) {
timer.cancel();
notifyListeners();
return;
} else {
totalSponsorVideoSeconds--;
}
notifyListeners();
},
);
}
int totalSecondsToWaitForMarathon = 20;
Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimerToMarathon(BuildContext context) {
const Duration oneSec = Duration(seconds: 1);
timerToWaitForMarathon = Timer.periodic(
oneSec,
(Timer timer) async {
if (totalSecondsToWaitForMarathon == 0) {
} else {
totalSecondsToWaitForMarathon--;
}
notifyListeners();
},
);
}
int totalCurrentQuestionTime = 0;
int currentGapTime = 0;
Timer timerForQuestion = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimerForQuestion() {
const Duration oneSec = Duration(seconds: 1);
timerForQuestion = Timer.periodic(
oneSec,
(Timer timer) async {
// This 2 is just to show the color of answer tile for 2 seconds and then update card status
if (totalCurrentQuestionTime - currentGapTime == currentQuestion.questionTime! - 2) {
getCorrectAnswerAndUpdateAnswerColor();
}
if (totalCurrentQuestionTime == currentGapTime) {
updateCardStatusToAnswer();
await callSubmitOptionApi().then((bool value) async {
if (value) {
await callNextQuestionApi();
}
});
}
if (totalCurrentQuestionTime == 0) {
updateCardData();
if (currentQuestionNumber == marathonDetailModel.totalQuestions! - 1) {
updateQuestionCardStatus(QuestionCardStatus.findingWinner);
timer.cancel();
cancelTimer();
notifyListeners();
}
return;
} else {
totalCurrentQuestionTime--;
}
notifyListeners();
},
);
}
int totalSecondsToWaitForWinner = 30;
Timer timerForWinnerSelection = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimerForWinnerSelection() {
const Duration oneSec = Duration(seconds: 1);
timerForWinnerSelection = Timer.periodic(
oneSec,
(Timer timer) async {
if (totalSecondsToWaitForWinner == 0) {
timer.cancel();
updateQuestionCardStatus(QuestionCardStatus.winnerFound);
return;
} else {
totalSecondsToWaitForWinner--;
}
notifyListeners();
},
);
}
//****************FUNCTIONS*********
Future<bool> callSubmitOptionApi() async {
return await MarathonApiClient().submitSelectedOption(selectedAnswerId: selectedOptionId);
}
// TODO: here I need to add a logic where I should call this function for Api but for the 1st question it should behave differently
// TODO: Verify the callings!!!
Future<void> callNextQuestionApi() async {
if (currentQuestionNumber < marathonDetailModel.totalQuestions!) {
if (currentQuestionNumber == 0) {
currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!))!;
if (Utils.isLoading) {
Utils.hideLoading(AppRoutes.navigatorKey.currentContext!);
}
startTimerForQuestion();
updateCardData();
Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen);
} else {
currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: currentQuestion.id, marathonId: marathonDetailModel.id!))!;
}
notifyListeners();
}
}
void updateCardData() {
if (currentQuestionNumber > 0) {
print("swiped it away!!");
swipeCardLeft();
}
selectedOptionIndex = null;
currentQuestionNumber++;
cardContentList.add(const CardContent());
totalCurrentQuestionTime = currentQuestion.questionTime! + currentQuestion.nextQuestGap!;
currentGapTime = currentQuestion.nextQuestGap!;
totalMarathoners = currentQuestion.remainingParticipantCount!;
questionCardStatus = QuestionCardStatus.question;
}
void populateQuestionStatusesList() {
if (marathonDetailModel.totalQuestions != null) {
for (int i = 0; i < marathonDetailModel.totalQuestions! - 1; i++) {
answerStatusesList.add(QuestionCardStatus.question);
}
notifyListeners();
}
}
void updateAnswerStatusesList(QuestionCardStatus status) {
answerStatusesList[currentQuestionNumber - 1] = status;
notifyListeners();
}
void addItemToList(CardContent value) {
cardContentList.add(value);
notifyListeners();
}
void updateCurrentQuestionOptionStatus(QuestionsOptionStatus status, int index) {
for (int i = 0; i < currentQuestion.questionOptions!.length; i++) {
currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected;
}
currentQuestion.questionOptions![index].optionStatus = status;
selectedOptionIndex = index;
notifyListeners();
}
void updateQuestionCardStatus(QuestionCardStatus status) {
if (status == QuestionCardStatus.wrongAnswer || status == QuestionCardStatus.skippedAnswer) {
updateIsUserOutOfGame = true;
}
questionCardStatus = status;
notifyListeners();
}
@ -120,59 +305,36 @@ class MarathonProvider extends ChangeNotifier {
if (selectedOptionIndex != null) {
if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) {
updateQuestionCardStatus(QuestionCardStatus.correctAnswer);
updateAnswerStatusesList(QuestionCardStatus.correctAnswer);
} else {
updateQuestionCardStatus(QuestionCardStatus.wrongAnswer);
updateAnswerStatusesList(QuestionCardStatus.wrongAnswer);
}
} else {
updateQuestionCardStatus(QuestionCardStatus.skippedAnswer);
updateAnswerStatusesList(QuestionCardStatus.skippedAnswer);
}
}
Timer timerU = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
void startTimer(BuildContext context) {
const Duration oneSec = Duration(seconds: 1);
timerU = Timer.periodic(
oneSec,
(Timer timer) async {
if (currentQuestionTime == 2) {
getCorrectAnswerAndUpdateAnswerColor();
}
if (currentQuestionTime == 0) {
updateCardStatusToAnswer();
// if (currentQuestionNumber == 9) {
// timer.cancel();
// cancelTimer();
// isMarathonCompleted = true;
// await Future<dynamic>.delayed(const Duration(seconds: 2)).whenComplete(
// () => Navigator.pushReplacementNamed(context, AppRoutes.marathonWinnerSelection),
// );
//
// resetValues();
//
// return;
// }
// timer.cancel();
} else {
currentQuestionTime--;
}
notifyListeners();
},
);
void swipeCardLeft() {
swiperController.swipeLeft();
notifyListeners();
}
void resetValues() {
void resetValues() async {
_currentQuestionNumber = 0;
cardContentList.clear();
timerU.cancel();
timerForWinnerSelection.cancel();
timerForQuestion.cancel();
_isMarathonCompleted = false;
currentQuestionTime = 0;
totalCurrentQuestionTime = 0;
currentQuestion = QuestionModel();
notifyListeners();
}
void cancelTimer() {
timerU.cancel();
timerForQuestion.cancel();
notifyListeners();
}
@ -181,12 +343,51 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
await MarathonApiClient().getMarathonToken().whenComplete(() async {
marathonDetailModel = await MarathonApiClient().getMarathonDetails();
if (marathonDetailModel.id == null) {
isUpComingMarathon = false;
notifyListeners();
return;
}
populateQuestionStatusesList();
isLoading = false;
notifyListeners();
});
}
Future<void> connectSignalrAndJoinMarathon(BuildContext context) async {
await MarathonApiClient().buildHubConnection(context);
Future<void> buildConnectionWithSignalR(BuildContext context) async {
Utils.showLoading(context);
try {
resetValues();
// await MarathonApiClient().buildHubConnection(context, marathonDetailModel.sponsors!.first.sponsorPrizes!.first.id!);
} catch (e) {
if (kDebugMode) {
print("error in buildConnectionWithSignalR: ${e.toString()}");
}
Utils.hideLoading(context);
Utils.confirmDialog(context, e.toString());
}
}
Future<void> onJoinMarathonPressed(BuildContext context) async {
//TODO: here we need to put a check to make sure we should not display sponsor when remaining time to marathon is less than 30 seconds plus video duration e.g. 30 seconds + video duration time
// if (marathonDetailModel.sponsors!.first.video != null && marathonDetailModel.sponsors!.first.video != "") {
if (false) {
await initializeVideoPlayer().then((_) {
startTimerForSponsorVideo();
Navigator.pushNamed(context, AppRoutes.marathonSponsorVideoScreen);
});
} else {
try {
Utils.showLoading(context);
bool isJoined = await MarathonApiClient().joinMarathonAsParticipant();
if (isJoined) {
print("joined");
callNextQuestionApi();
}
} catch (e, s) {
Utils.hideLoading(context);
Utils.confirmDialog(context, e.toString());
}
}
}
}

@ -2,16 +2,22 @@ import 'dart:async';
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/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/custom_status_widget.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_progress_container.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_qualifiers_container.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card_builder.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
@ -20,46 +26,164 @@ import 'package:provider/provider.dart';
class MarathonScreen extends StatelessWidget {
const MarathonScreen({Key? key}) : super(key: key);
Widget getSuccessWidget({required int? gapType, required String? gapImage, required String? gapText}) {
if (gapType == 1) {
if (gapText == null) {
return Image.asset(MyLottieConsts.congratsGif, height: 200);
}
return gapText.toText18(color: MyColors.darkTextColor, isCentered: true);
}
if (gapType == 2) {
if (gapImage == null) {
return Image.asset(MyLottieConsts.congratsGif, height: 200);
}
return Image.network(gapImage, height: 200);
}
return Image.asset(MyLottieConsts.congratsGif, height: 200);
}
Widget getWinnerWidget(BuildContext context, {required MarathonProvider provider}) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <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: LocaleKeys.winner.tr().toText32(color: MyColors.white, isBold: true).paddingOnly(top: 07),
)
],
),
),
12.height,
"Muhammad Shrouff".toText22(color: MyColors.grey3AColor),
"837436".toText22(color: MyColors.grey57Color),
80.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(
provider.marathonDetailModel.sponsors!.first.image!,
height: 40,
width: 150,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center();
},
)
],
],
),
);
}
Widget getNameContainer(BuildContext context) {
return Container(
height: 50,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
color: MyColors.greenColor,
borderRadius: BorderRadius.circular(15),
boxShadow: <BoxShadow>[BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 26, offset: const Offset(0, -3))],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
(AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr!)
.toText17(isBold: true, color: MyColors.white),
AppState().memberInformationList!.eMPLOYEENUMBER!.toText17(isBold: true, color: MyColors.white),
],
),
).paddingOnly(left: 20, right: 20, top: 12, bottom: 20);
}
@override
Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>();
return WillPopScope(
child: Scaffold(
appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()),
appBar: AppBarWidget(
context,
title: LocaleKeys.brainMarathon.tr(),
onHomeTapped: () {
Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr());
},
onBackTapped: () {
Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr());
},
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
20.height,
MarathonProgressContainer(provider: provider).paddingOnly(left: 21, right: 21),
if (provider.questionCardStatus == QuestionCardStatus.findingWinner) ...<Widget>[
QualifiersContainer(provider: provider).paddingOnly(left: 21, right: 21),
] else if (provider.questionCardStatus == QuestionCardStatus.winnerFound)
...<Widget>[]
else ...<Widget>[
MarathonProgressContainer(provider: provider).paddingOnly(left: 21, right: 21),
],
if (provider.questionCardStatus == QuestionCardStatus.findingWinner) ...<Widget>[
getNameContainer(context),
],
QuestionCardBuilder(
onQuestion: (BuildContext context) => QuestionCard(provider: provider),
onCompleted: (BuildContext context) => CustomStatusWidget(
asset: Lottie.asset(MyLottieConsts.allQuestions, height: 200),
title: LocaleKeys.congrats.tr().toText22(color: MyColors.greenColor),
subTitle: LocaleKeys.allQuestionsCorrect.toText18(color: MyColors.darkTextColor),
subTitle: LocaleKeys.allQuestionsCorrect.toText18(color: MyColors.darkTextColor, isCentered: true),
),
onCorrectAnswer: (BuildContext context) => CustomStatusWidget(
asset: Lottie.asset(MyLottieConsts.allQuestions, height: 200),
asset: getSuccessWidget(gapType: provider.currentQuestion.gapType, gapImage: provider.currentQuestion.gapImage, gapText: provider.currentQuestion.gapText),
title: LocaleKeys.congrats.tr().toText22(color: MyColors.greenColor),
subTitle: LocaleKeys.yourAnswerCorrect.toText18(color: MyColors.darkTextColor),
subTitle: LocaleKeys.yourAnswerCorrect.toText18(color: MyColors.darkTextColor, isCentered: true),
),
onWinner: (BuildContext context) => QuestionCard(provider: provider),
onWinner: (BuildContext context) => getWinnerWidget(context, provider: provider),
onWrongAnswer: (BuildContext context) => CustomStatusWidget(
asset: Image.asset(MyLottieConsts.wrongAnswerGif, height: 200),
title: const Text(""),
subTitle: LocaleKeys.wrongAnswer.tr().toText18(color: MyColors.darkTextColor),
title: LocaleKeys.oops.tr().toText22(color: MyColors.redColor),
subTitle: LocaleKeys.wrongAnswer.tr().toText18(color: MyColors.darkTextColor, isCentered: true),
),
onSkippedAnswer: (BuildContext context) => CustomStatusWidget(
asset: Image.asset(MyLottieConsts.wrongAnswerGif, height: 200),
title: const Text(""),
subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor),
title: LocaleKeys.oops.tr().toText22(color: MyColors.redColor),
subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor, isCentered: true),
),
questionCardStatus: provider.questionCardStatus,
onFindingWinner: (BuildContext context) => CustomStatusWidget(
asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168),
title: LocaleKeys.fingersCrossed.tr().toText22(color: MyColors.greenColor),
subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor),
subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor, isCentered: true),
),
questionCardStatus: provider.questionCardStatus,
).paddingOnly(top: 12, left: 21, right: 21),
],
),
@ -72,127 +196,3 @@ class MarathonScreen extends StatelessWidget {
);
}
}
class MarathonProgressContainer extends StatefulWidget {
final MarathonProvider provider;
const MarathonProgressContainer({Key? key, required this.provider}) : super(key: key);
@override
State<MarathonProgressContainer> createState() => _MarathonProgressContainerState();
}
class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
@override
void initState() {
scheduleMicrotask(() {
widget.provider.startTimer(context);
});
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 13),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
decoration: BoxDecoration(color: MyColors.greenColor, borderRadius: BorderRadius.circular(5)),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8),
child: "${widget.provider.currentQuestionNumber.toString()} / ${widget.provider.marathonDetailModel.totalQuestions.toString()} ${LocaleKeys.question.tr()}"
.toText12(color: MyColors.white),
),
"${widget.provider.totalMarathoners} ${LocaleKeys.marathoners.tr()}".toText14(),
"00:${widget.provider.currentQuestionTime < 10 ? "0${widget.provider.currentQuestionTime}" : widget.provider.currentQuestionTime}"
.toText18(color: widget.provider.currentQuestionTime < 5 ? MyColors.redColor : MyColors.black),
],
),
12.height,
stepper(widget.provider.currentQuestionNumber),
8.height,
Row(
children: <Widget>[
"${((widget.provider.currentQuestionNumber / widget.provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}".toText14(),
],
),
],
),
);
}
Widget stepper(int value) {
return SizedBox(
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (int i = 0; i < 10; i++)
if (value <= i) roundContainer(MyColors.lightGreyDeColor, i != 0) else roundContainer(MyColors.greenColor, i != 0)
],
),
);
}
Widget roundContainer(Color color, bool isNeedLeftBorder) {
if (isNeedLeftBorder) {
return Row(
children: [
Divider(thickness: 6, color: color).expanded,
Container(
width: 10,
height: 10,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
),
],
).expanded;
}
return Container(
width: 10,
height: 10,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
);
}
}
// InkWell(
// onTap: () {
// Navigator.pushReplacementNamed(
// context,
// AppRoutes.marathonWinnerSelection,
// );
// },
// child: CustomStatusWidget(
// asset: Lottie.asset(
// MyLottieConsts.allQuestions,
// height: 200,
// ),
// title: Text(
// LocaleKeys.congrats.tr(),
// style: const TextStyle(
// height: 23 / 24,
// color: MyColors.greenColor,
// fontSize: 27,
// letterSpacing: -1,
// fontWeight: FontWeight.w600,
// ),
// ),
// subTitle: Text(
// LocaleKeys.allQuestionsCorrect.tr(),
// textAlign: TextAlign.center,
// style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -1.08),
// ),
// ).paddingOnly(top: 12, left: 21, right: 21),
// )

@ -0,0 +1,91 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:provider/provider.dart';
import 'package:video_player/video_player.dart';
class SponsorVideoScreen extends StatelessWidget {
const SponsorVideoScreen({Key? key}) : super(key: key);
Future<void> onSponsorVideoClosed(BuildContext context) async {
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>();
return WillPopScope(
onWillPop: () {
provider.videoController.dispose();
return Future<bool>.value(true);
},
child: Scaffold(
backgroundColor: MyColors.black,
body: SafeArea(
child: Stack(
children: <Widget>[
Align(
child: provider.videoController.value.isInitialized
? AspectRatio(
aspectRatio: provider.videoController.value.aspectRatio,
child: VideoPlayer(provider.videoController),
)
: Container(color: Colors.white),
),
Align(
alignment: Alignment.topRight,
child: Container(
decoration: BoxDecoration(
color: MyColors.white,
shape: provider.totalSponsorVideoSeconds == 0 ? BoxShape.circle : BoxShape.rectangle,
borderRadius: provider.totalSponsorVideoSeconds == 0 ? null : BorderRadius.circular(15),
),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8),
child: provider.totalSponsorVideoSeconds == 0
? InkWell(
onTap: () {
Navigator.pop(context);
provider.videoController.dispose();
provider.buildConnectionWithSignalR(AppRoutes.navigatorKey.currentState!.overlay!.context);
},
child: const Icon(Icons.close, size: 12),
)
: Directionality(
textDirection: ui.TextDirection.ltr,
child: ("${LocaleKeys.ourSponsor.tr()} ${provider.totalSponsorVideoSeconds < 10 ? "0" : ""}${provider.totalSponsorVideoSeconds}").toText12(color: MyColors.darkTextColor),
),
),
).paddingOnly(top: 20, right: 18),
Align(
alignment: Alignment.topLeft,
child: InkWell(
onTap: () {
Navigator.pop(context);
provider.videoController.dispose();
provider.buildConnectionWithSignalR(AppRoutes.navigatorKey.currentState!.overlay!.context);
},
child: Container(
decoration: BoxDecoration(color: MyColors.white, borderRadius: BorderRadius.circular(15)),
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 6),
child: Directionality(
textDirection: ui.TextDirection.ltr,
child: LocaleKeys.skip.tr().toText11(color: MyColors.darkTextColor),
),
),
),
).paddingOnly(top: 20, left: 18),
],
),
),
),
);
}
}

@ -0,0 +1,62 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:provider/provider.dart';
class MarathonWaitingScreen extends StatelessWidget {
const MarathonWaitingScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>();
return Scaffold(
appBar: AppBarWidget(
context,
title: LocaleKeys.brainMarathon.tr(),
onHomeTapped: () {
Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr());
},
onBackTapped: () {
Utils.confirmDialog(context, LocaleKeys.youWantToLeaveMarathon.tr());
},
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: double.infinity,
margin: const EdgeInsets.all(21),
decoration: MyDecorations.shadowDecoration,
child: Stack(
children: <Widget>[
Align(
child: Lottie.asset(MyLottieConsts.marathonWaiting, height: 200),
),
Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
LocaleKeys.startingIn.tr().toText16(),
"00:${provider.totalSecondsToWaitForMarathon < 10 ? "0${provider.totalSecondsToWaitForMarathon}" : provider.totalSecondsToWaitForMarathon}"
.toText18(color: provider.totalSecondsToWaitForMarathon < 5 ? MyColors.redColor : MyColors.black),
],
),
),
],
),
).expanded,
],
),
);
}
}

@ -1,10 +1,7 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
@ -13,6 +10,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/custom_status_widget.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_qualifiers_container.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:provider/provider.dart';
@ -28,7 +26,7 @@ class MarathonWinnerSelection extends StatelessWidget {
child: Column(
children: [
20.height,
QualifiersContainer(provider: provider).paddingOnly(left: 21, right: 21),
QualifiersContainer(provider: provider,).paddingOnly(left: 21, right: 21),
12.height,
InkWell(
onTap: () {
@ -90,60 +88,3 @@ class MarathonWinnerSelection extends StatelessWidget {
);
}
}
class QualifiersContainer extends StatefulWidget {
final MarathonProvider provider;
const QualifiersContainer({Key? key, required this.provider}) : super(key: key);
@override
State<QualifiersContainer> createState() => _QualifiersContainerState();
}
class _QualifiersContainerState extends State<QualifiersContainer> {
final int totalQuestions = 10;
final int currentQuestion = 04;
@override
void initState() {
scheduleMicrotask(() {
// widget.provider.startTimer(context);
});
super.initState();
}
@override
void dispose() {
// widget.provider.cancelTimer();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.only(top: 14,left: 18,right: 14,bottom: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor),
// "00:${widget.provider.start < 10 ? "0${widget.provider.start}" : widget.provider.start}".toText18(color: MyColors.redColor),
],
),
10.height,
Row(
children: [
"18".toText30(color: MyColors.greenColor, isBold: true),2.width,
LocaleKeys.qualifiers.tr().toText16(color: MyColors.greenColor),
],
),
],
),
);
}
}

@ -22,6 +22,7 @@ class CustomStatusWidget extends StatelessWidget {
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
asset,
50.height,

@ -16,7 +16,9 @@ import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart';
import 'package:provider/provider.dart';
class MarathonBanner extends StatelessWidget {
const MarathonBanner({Key? key}) : super(key: key);
final bool isMarathonUpcoming;
const MarathonBanner({Key? key, required this.isMarathonUpcoming}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -76,7 +78,7 @@ class MarathonBanner extends StatelessWidget {
height: double.infinity,
),
),
Expanded(
Expanded(
flex: AppState().isArabic(context) ? 4 : 5,
child: SizedBox(
width: double.infinity,

@ -0,0 +1,82 @@
import 'package:easy_localization/easy_localization.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/decorations_helper.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
class MarathonDetailsCard extends StatelessWidget {
final MarathonProvider provider;
const MarathonDetailsCard({Key? key, required this.provider}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 14),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
LocaleKeys.contestTopicAbout.tr().toText16(color: MyColors.grey77Color),
"${AppState().isArabic(context) ? provider.marathonDetailModel.titleAr : provider.marathonDetailModel.titleEn}".toText20(color: MyColors.textMixColor, isBold: true),
Row(
children: <Widget>[
Flexible(
child: "${AppState().isArabic(context) ? provider.marathonDetailModel.descAr : provider.marathonDetailModel.descEn}".toText14(color: MyColors.grey77Color),
)
],
),
if (provider.itsMarathonTime && provider.marathonDetailModel.sponsors != null) ...<Widget>[
5.height,
provider.marathonDetailModel.sponsors?.first.sponsorPrizes != null
? Row(
children: <Widget>[
"${LocaleKeys.prize.tr()} ".toText16(color: MyColors.grey77Color, isBold: true),
"${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr : provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr}"
.toText16(color: MyColors.greenColor, isBold: true),
],
)
: const SizedBox(),
Row(
children: <Widget>[
"${LocaleKeys.sponsoredBy.tr()} ".toText16(color: MyColors.grey77Color),
"${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.nameAr : provider.marathonDetailModel.sponsors?.first.nameEn}"
.toText16(color: MyColors.darkTextColor, isBold: true),
],
),
10.height,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.network(
provider.marathonDetailModel.sponsors!.first.image!,
height: 40,
width: 150,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center();
},
)
],
),
]
],
),
],
),
);
}
}

@ -0,0 +1,66 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
class MarathonFooter extends StatelessWidget {
final MarathonProvider provider;
const MarathonFooter({
Key? key,
required this.provider,
}) : super(key: key);
Widget buildNoteForDemo() {
return RichText(
text: TextSpan(
children: <InlineSpan>[
TextSpan(
text: LocaleKeys.note.tr(),
style: const TextStyle(color: MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.bold),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP1.tr(),
style: const TextStyle(color: MyColors.grey77Color, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.w500),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP2.tr(),
style: const TextStyle(color: MyColors.darkTextColor, fontSize: 17, fontWeight: FontWeight.bold),
),
TextSpan(
text: " " + LocaleKeys.demoMarathonNoteP3.tr(),
style: const TextStyle(color: MyColors.grey77Color, fontSize: 17, letterSpacing: -0.64, fontWeight: FontWeight.w500),
)
],
),
).paddingOnly(right: 21, left: 21, top: 11, bottom: 0);
}
@override
Widget build(BuildContext context) {
return DefaultButton(
LocaleKeys.joinMarathon.tr(),
!provider.itsMarathonTime ? () => provider.onJoinMarathonPressed(context) : null,
).insideContainer;
}
}
//Container(
// color: Colors.white,
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// buildNoteForDemo(),
// DefaultButton(
// LocaleKeys.joinDemoMarathon.tr(),
// () {
// provider.connectSignalrAndJoinMarathon(context);
// },
// color: MyColors.yellowColorII,
// ).insideContainer,
// ],
// ),
// );

@ -0,0 +1,115 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
class MarathonProgressContainer extends StatelessWidget {
final MarathonProvider provider;
const MarathonProgressContainer({Key? key, required this.provider}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 13),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
decoration: BoxDecoration(color: MyColors.greenColor, borderRadius: BorderRadius.circular(5)),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8),
child: "${provider.currentQuestionNumber.toString()} / ${provider.marathonDetailModel.totalQuestions.toString()} ${LocaleKeys.question.tr()}".toText12(color: MyColors.white),
),
"${provider.totalMarathoners} ${LocaleKeys.marathoners.tr()}".toText14(),
provider.questionCardStatus == QuestionCardStatus.question
? "00:${(provider.totalCurrentQuestionTime - provider.currentGapTime) < 10 ? "0${provider.totalCurrentQuestionTime - provider.currentGapTime}" : provider.totalCurrentQuestionTime - provider.currentGapTime}"
.toText18(color: provider.totalCurrentQuestionTime - provider.currentGapTime < 5 ? MyColors.redColor : MyColors.black)
: const SizedBox(),
],
),
12.height,
stepper(provider.currentQuestionNumber, provider.answerStatusesList, provider.marathonDetailModel.totalQuestions!, provider.isUserOutOfGame),
8.height,
Row(
children: <Widget>[
"${((provider.currentQuestionNumber / provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}".toText14(),
],
),
],
),
);
}
Color getStepColor(QuestionCardStatus status, bool isOutOfGame) {
if (isOutOfGame) {
return MyColors.redColor;
}
switch (status) {
case QuestionCardStatus.question:
return MyColors.yellowColorII;
case QuestionCardStatus.wrongAnswer:
return MyColors.redColor;
case QuestionCardStatus.correctAnswer:
return MyColors.greenColor;
case QuestionCardStatus.skippedAnswer:
return MyColors.redColor;
case QuestionCardStatus.completed:
return MyColors.lightGreyDeColor;
case QuestionCardStatus.findingWinner:
return MyColors.lightGreyDeColor;
case QuestionCardStatus.winnerFound:
return MyColors.lightGreyDeColor;
}
}
Widget stepper(int value, List<QuestionCardStatus> statusesList, int totalQuestions, bool isOutOfGame) {
return SizedBox(
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
for (int i = 0; i < totalQuestions - 1; i++)
if (value <= i)
roundContainer(MyColors.lightGreyDeColor, i != 0)
else
roundContainer(
getStepColor(statusesList[i], isOutOfGame),
i != 0,
)
],
),
);
}
Widget roundContainer(Color color, bool isNeedLeftBorder) {
if (isNeedLeftBorder) {
return Row(
children: [
Divider(thickness: 6, color: color).expanded,
Container(
width: 10,
height: 10,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
),
],
).expanded;
}
return Container(
width: 10,
height: 10,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
);
}
}

@ -0,0 +1,54 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
class QualifiersContainer extends StatefulWidget {
final MarathonProvider provider;
const QualifiersContainer({Key? key, required this.provider}) : super(key: key);
@override
State<QualifiersContainer> createState() => _QualifiersContainerState();
}
class _QualifiersContainerState extends State<QualifiersContainer> {
@override
void initState() {
widget.provider.startTimerForWinnerSelection();
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.only(top: 14, left: 18, right: 14, bottom: 18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor),
"00:${widget.provider.totalSecondsToWaitForWinner < 10 ? "0${widget.provider.totalSecondsToWaitForWinner}" : widget.provider.totalSecondsToWaitForWinner}".toText18(color: MyColors.redColor),
],
),
10.height,
Row(
children: <Widget>[
widget.provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true),
2.width,
LocaleKeys.qualifiers.tr().toText16(color: MyColors.greenColor),
],
),
],
),
);
}
}

@ -0,0 +1,51 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.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/date_uitl.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/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart';
class MarathonTimerCard extends StatelessWidget {
final int timeToMarathon;
final MarathonProvider provider;
const MarathonTimerCard({
Key? key,
required this.provider,
required this.timeToMarathon,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 14),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
"${LocaleKeys.gameDate.tr()} ".toText16(color: MyColors.grey77Color),
DateUtil.getMonthDayYearDateFormatted(DateTime.parse(provider.marathonDetailModel.startTime!)).toText16(color: MyColors.darkTextColor, isBold: true),
],
),
Row(
children: <Widget>[
"${LocaleKeys.gameTime.tr()} ".toText16(color: MyColors.grey77Color),
DateUtil.formatDateToTimeLang(DateTime.parse(provider.marathonDetailModel.startTime!), AppState().isArabic(context)).toText16(color: MyColors.darkTextColor, isBold: true),
],
),
Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200),
BuildCountdownTimer(timeToMarathon: timeToMarathon, provider: provider, screenFlag: 1),
],
),
);
}
}

@ -145,21 +145,32 @@ class AnswerTileForText extends StatelessWidget {
const AnswerTileForText({Key? key, required this.index, required this.onAnswerTapped}) : super(key: key);
Color getAnswerTextColor(QuestionsOptionStatus status) {
switch (status) {
case QuestionsOptionStatus.correct:
return MyColors.white;
case QuestionsOptionStatus.wrong:
return MyColors.white;
case QuestionsOptionStatus.selected:
return MyColors.white;
case QuestionsOptionStatus.unSelected:
return MyColors.darkTextColor;
}
}
@override
Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>();
return InkWell(
onTap: () {
onAnswerTapped();
provider.isUserOutOfGame ? null : onAnswerTapped() ;
},
child: Container(
alignment: Alignment.centerLeft,
decoration: MyDecorations.getAnswersContainerColor(provider.currentQuestion.questionOptions![index].optionStatus!),
child: Center(
child: (AppState().isArabic(context) ? provider.currentQuestion.questionOptions![index].titleAr! : provider.currentQuestion.questionOptions![index].titleEn!)
.toText16(
color: provider.currentQuestion.questionOptions![index].optionStatus == QuestionsOptionStatus.unSelected ? MyColors.darkTextColor : MyColors.white,
)
.toText16(color: provider.isUserOutOfGame ? MyColors.darkTextColor : getAnswerTextColor(provider.currentQuestion.questionOptions![index].optionStatus!))
.paddingOnly(top: 17, bottom: 17),
),
),

@ -127,6 +127,7 @@ class _AddItemDetailsFragmentState extends State<AddItemDetailsFragment> {
isPopup: false,
lines: 1,
isInputTypeNum: true,
isInputTypeNumSigned: false,
isReadOnly: false,
onChange: (String value) {
itemPrice = num.parse(value);

@ -1,6 +1,8 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.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/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
@ -74,6 +76,7 @@ class _ItemsForSaleState extends State<ItemsForSale> {
child: const Icon(Icons.add, color: Colors.white, size: 30),
).onPress(
() {
Utils.removeStringFromPrefs(SharedPrefsConsts.editItemForSale);
Navigator.pushNamed(context, AppRoutes.addNewItemForSale);
},
),

@ -143,7 +143,7 @@ class _OffersAndDiscountsDetailsState extends State<OffersAndDiscountsDetails> {
List<Widget> getItemsForSaleWidgets() {
List<Widget> itemsList = [];
for (int i = 1; i < 5; i++) {
for (int i = 1; i < getOffersList.length; i++) {
itemsList.add(getItemCard(getOffersList[i]));
}
return itemsList;

@ -80,39 +80,26 @@ class _OffersAndDiscountsHomeState extends State<OffersAndDiscountsHome> {
// getItemsForSale(currentPageNo, currentCategoryID);
});
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.string(
getCategoriesList[index].content!,
fit: BoxFit.contain,
width: 25,
height: 25,
),
currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(),
],
).expanded,
AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10(maxlines: 1) : getCategoriesList[index].categoryNameEn!.toText10(maxlines: 1)
],
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.string(
getCategoriesList[index].content!,
fit: BoxFit.contain,
width: 25,
height: 25,
),
currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(),
],
).expanded,
AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10() : getCategoriesList[index].categoryNameEn!.toText10()
],
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12).expanded.objectContainerView(disablePadding: true),
),
);
},

@ -0,0 +1,72 @@
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mohem_flutter_app/classes/colors.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/button/default_button.dart';
class UnsafeDeviceScreen extends StatefulWidget {
const UnsafeDeviceScreen({Key? key}) : super(key: key);
@override
State<UnsafeDeviceScreen> createState() => _UnsafeDeviceScreenState();
}
class _UnsafeDeviceScreenState extends State<UnsafeDeviceScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
21.height,
Center(child: Image.asset("assets/images/logos/main_mohemm_logo.png", width: 200, height: 50)),
50.height,
"Sorry".toText24(isBold: true),
21.height,
"You are using Mohemm app on an unsafe device. To be able to use the app with all it's features, Please make sure that the below points are considered: "
.toText14(isCenter: true)
.paddingOnly(left: 20.0, right: 20.0),
48.height,
passwordConstraintsUI("The device is not jailbroken or rooted.", true).paddingOnly(left: 24.0, right: 5.0),
8.height,
passwordConstraintsUI("The app is not installed on external storage.", true).paddingOnly(left: 24.0, right: 5.0),
8.height,
passwordConstraintsUI("Development mode is disabled.", true).paddingOnly(left: 24.0, right: 5.0),
21.height,
DefaultButton(LocaleKeys.ok.tr(), () async {
if (Platform.isAndroid) {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
} else {
// MinimizeApp.minimizeApp();
}
}).paddingAll(24)
],
),
),
);
}
Widget passwordConstraintsUI(String description, bool check) {
return Row(
children: [
4.width,
SizedBox(
width: 12,
height: 12,
child: Checkbox(fillColor: MaterialStateProperty.all(MyColors.gradiantEndColor), shape: const CircleBorder(), value: check, onChanged: null),
),
8.width,
description.toText14()
],
);
}
}

@ -8,7 +8,14 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/widgets/circular_avatar.dart';
AppBar AppBarWidget(BuildContext context,
{required String title, bool showHomeButton = true, bool showNotificationButton = false, bool showMemberButton = false, String? image, List<Widget>? actions}) {
{required String title,
bool showHomeButton = true,
bool showNotificationButton = false,
bool showMemberButton = false,
String? image,
List<Widget>? actions,
void Function()? onHomeTapped,
void Function()? onBackTapped}) {
return AppBar(
leadingWidth: 0,
// leading: GestureDetector(
@ -21,7 +28,9 @@ AppBar AppBarWidget(BuildContext context,
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context),
onTap: Feedback.wrapForTap(() {
(onBackTapped == null ? Navigator.maybePop(context) : onBackTapped());
}, context),
child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor),
),
4.width,
@ -43,7 +52,7 @@ AppBar AppBarWidget(BuildContext context,
if (showHomeButton)
IconButton(
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard));
onHomeTapped == null ? Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)) : onHomeTapped();
},
icon: const Icon(Icons.home, color: MyColors.darkIconColor),
),

@ -15,6 +15,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
import 'package:mohem_flutter_app/models/get_action_history_list_model.dart';
import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart';
@ -90,10 +91,9 @@ class _SearchEmployeeBottomSheetState extends State<SearchEmployeeBottomSheet> {
Utils.showLoading(context);
chatUsersList = await ChatApiClient().getChatMemberFromSearch(
searchText,
int.parse(
AppState().chatDetails!.response!.id.toString(),
),
int.parse(AppState().chatDetails!.response!.id.toString()),
);
chatUsersList!.removeWhere((element) => element.id == AppState().chatDetails!.response!.id);
Utils.hideLoading(context);
setState(() {});
} catch (e) {

@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/classes/colors.dart';
class DynamicTextFieldWidget extends StatelessWidget {
final String labelText;
final String hintText;
// final TextEditingController controller;
final VoidCallback? onTap;
final IconData? suffixIconData;
@ -13,6 +14,7 @@ class DynamicTextFieldWidget extends StatelessWidget {
final bool isPopup;
final int? lines;
final bool isInputTypeNum;
final bool isInputTypeNumSigned;
final bool isObscureText;
final bool isBackgroundEnable;
final void Function(String)? onChange;
@ -28,6 +30,7 @@ class DynamicTextFieldWidget extends StatelessWidget {
this.inputAction,
this.onChange,
this.isInputTypeNum = false,
this.isInputTypeNumSigned = true,
this.isBackgroundEnable = false});
@override
@ -63,8 +66,13 @@ class DynamicTextFieldWidget extends StatelessWidget {
),
TextField(
enabled: isEnable,
scrollPadding: EdgeInsets.zero, readOnly: isReadOnly,
keyboardType: isInputTypeNum ? const TextInputType.numberWithOptions(signed: true) : TextInputType.text,
scrollPadding: EdgeInsets.zero,
readOnly: isReadOnly,
keyboardType: (isInputTypeNum)
? isInputTypeNumSigned
? const TextInputType.numberWithOptions(signed: true)
: TextInputType.number
: TextInputType.text,
textInputAction: TextInputAction.done,
//controller: controller,
maxLines: lines,

@ -37,9 +37,9 @@ class ItemDetailViewCol extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"$title:".toText12(isBold: true, color: const Color(0xff2BB8A6)),
"$title:".toText12(isBold: true, color: const Color(0xff2BB8A6), maxLine: 2),
4.width,
(value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor),
(value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor, maxLine: 2),
],
);
}

Loading…
Cancel
Save