Committing before reverting from SignalR to Apis

faiz_marathon_signalR_critical
Faiz Hashmi 3 years ago
parent 49b57cae08
commit 96e52abbb9

@ -505,6 +505,8 @@
"youMissedTheQuestion": "نفد منك الوقت. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.",
"wrongAnswer": "إجابتك غير صحيحة. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.",
"oops": "أوه!!!",
"winner": "الفائز"
"winner": "الفائز",
"youWantToLeaveMarathon": "هل أنت متأكد أنك تريد العودة؟ سوف تخرج من المسابقة.",
"ourSponsor": "راعينا:",
"startingIn": "يبدأ في"
}

@ -499,13 +499,15 @@
"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 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"
"winner": "WINNER",
"youWantToLeaveMarathon": "Are you sure you want to go back? You will be out of the contest.",
"ourSponsor": "Our Sponsor:",
"startingIn": "Starting in"
}

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 "";
}
}
@ -80,99 +75,146 @@ 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);
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("message: ${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? selectedOptionId, required String? questionId, required String marathonId}) async {
Map<String, String?> jsonObject = <String, String?>{
"selectedOptionId": selectedOptionId,
"questionId": questionId,
"marathonId": marathonId,
};
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonNextQuestionUrl, jsonObject, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
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, context);
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
logger.i("message: ${marathonModel.data}");
return null;
} else {
return null;
}
} else {
return null;
}
}
Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
logger.i("OnParticipantJoin arguments: $arguments");
context.watch<MarathonProvider>().totalMarathoners++;
}
// 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++;
// }
}

@ -28,11 +28,17 @@ class ApiConsts {
static String chatUserImages = chatServerBaseUrl + "empservice/api/employee/";
//Brain Marathon Constants
static String marathonBaseUrl = "https://18.188.181.12/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://18.188.181.12/service/api/";
static String marathonHubConnectionUrl = "https://18.188.181.12/service/MarathonBroadCast";
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,7 +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";
}

@ -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';
@ -73,6 +74,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 +188,8 @@ 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 final Map<String, WidgetBuilder> routes = {
login: (BuildContext context) => LoginScreen(),
@ -293,5 +298,7 @@ class AppRoutes {
marathonScreen: (BuildContext context) => MarathonScreen(),
marathonWinnerSelection: (BuildContext context) => MarathonWinnerSelection(),
marathonWinnerScreen: (BuildContext context) => WinnerScreen(),
marathonSponsorVideoScreen: (BuildContext context) => const SponsorVideoScreen(),
marathonWaitingScreen: (BuildContext context) => const MarathonWaitingScreen(),
};
}

@ -492,5 +492,8 @@ abstract class LocaleKeys {
static const wrongAnswer = 'wrongAnswer';
static const oops = 'oops';
static const winner = 'winner';
static const youWantToLeaveMarathon = 'youWantToLeaveMarathon';
static const ourSponsor = 'ourSponsor';
static const startingIn = 'startingIn';
}

@ -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;

@ -25,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,
@ -35,4 +38,3 @@ class MarathonIntroScreen extends StatelessWidget {
);
}
}

@ -2,6 +2,7 @@ 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';
@ -9,63 +10,25 @@ 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;
int totalSecondsToWaitForWinner = 30;
int totalSecondsToWaitForMarathon = 20;
int totalQualifiers = 0;
void onNewQuestionReceived(QuestionModel newQuestion, BuildContext context) {
if (currentQuestionNumber < marathonDetailModel.totalQuestions!) {
if (currentQuestionNumber == 0) {
if (Utils.isLoading) {
Utils.hideLoading(context);
}
startTimerForQuestion(context);
Navigator.pushNamed(context, AppRoutes.marathonScreen);
}
if (currentQuestionNumber > 0) {
swipeCardLeft();
}
print("I received a new question and time is $currentQuestionTime and number is $currentQuestionNumber");
selectedOptionIndex = null;
currentQuestionNumber++;
currentQuestion = newQuestion;
cardContentList.add(const CardContent());
currentQuestionTime = newQuestion.questionTime!;
questionCardStatus = QuestionCardStatus.question;
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) {
questionCardStatus = status;
notifyListeners();
}
bool _isLoading = false;
bool get isLoading => _isLoading;
@ -93,6 +56,13 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
}
bool isUserOutOfGame = false;
set updateIsUserOutOfGame(bool value) {
isUserOutOfGame = value;
notifyListeners();
}
int _currentQuestionNumber = 0;
int get currentQuestionNumber => _currentQuestionNumber;
@ -111,8 +81,120 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
}
void swipeCardLeft() {
swiperController.swipeLeft();
//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();
}
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();
},
);
}
// FUNCTIONS
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();
},
);
}
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 onNewQuestionReceived(QuestionModel newQuestion) {
if (currentQuestionNumber < marathonDetailModel.totalQuestions!) {
if (currentQuestionNumber == 0) {
if (Utils.isLoading) {
Utils.hideLoading(AppRoutes.navigatorKey.currentContext!);
}
startTimerForQuestion(AppRoutes.navigatorKey.currentContext!);
Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen);
}
if (currentQuestionNumber > 0) {
swipeCardLeft();
}
selectedOptionIndex = null;
currentQuestionNumber++;
currentQuestion = newQuestion;
cardContentList.add(const CardContent());
currentQuestionTime = newQuestion.questionTime!;
questionCardStatus = QuestionCardStatus.question;
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();
}
@ -134,11 +216,14 @@ 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);
}
}
@ -153,7 +238,10 @@ class MarathonProvider extends ChangeNotifier {
getCorrectAnswerAndUpdateAnswerColor();
}
if (currentQuestionTime == 0) {
// we can enable this check if we do not want to show the user QuestionGapImages
// if (!isUserOutOfGame) {
updateCardStatusToAnswer();
// }
//todo: we will need to remove this -2 when API is all set
if (currentQuestionNumber == marathonDetailModel.totalQuestions! - 1) {
updateQuestionCardStatus(QuestionCardStatus.findingWinner);
@ -171,23 +259,30 @@ class MarathonProvider extends ChangeNotifier {
}
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) {
cancelTimer();
timer.cancel();
updateQuestionCardStatus(QuestionCardStatus.winnerFound);
return;
} else {
totalSecondsToWaitForWinner--;
}
totalSecondsToWaitForWinner--;
notifyListeners();
},
);
}
void swipeCardLeft() {
swiperController.swipeLeft();
notifyListeners();
}
void resetValues() async {
_currentQuestionNumber = 0;
cardContentList.clear();
@ -201,7 +296,6 @@ class MarathonProvider extends ChangeNotifier {
}
void cancelTimer() {
timerForWinnerSelection.cancel();
timerForQuestion.cancel();
notifyListeners();
}
@ -211,20 +305,36 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners();
await MarathonApiClient().getMarathonToken().whenComplete(() async {
marathonDetailModel = await MarathonApiClient().getMarathonDetails();
populateQuestionStatusesList();
isLoading = false;
notifyListeners();
});
}
Future<void> onJoinMarathonPressed(BuildContext context) async {
Future<void> buildConnectionWithSignalR(BuildContext context) async {
Utils.showLoading(context);
try {
resetValues();
await MarathonApiClient().buildHubConnection(context);
} catch (e, s) {
// await MarathonApiClient().buildHubConnection(context, marathonDetailModel.sponsors!.first.sponsorPrizes!.first.id!);
} catch (e) {
if (kDebugMode) {
print("error in buildConnectionWithSignalR: ${e.toString()}");
}
Utils.hideLoading(context);
print("error in onJoinMarathonPressed: ${e.toString()}");
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 {
Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen);
}
}
}

@ -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.currentQuestionTime < 10 ? "0${provider.currentQuestionTime}" : provider.currentQuestionTime}"
.toText18(color: provider.currentQuestionTime < 5 ? MyColors.redColor : MyColors.black),
],
),
),
],
),
).expanded,
],
),
);
}
}

@ -26,7 +26,7 @@ class MarathonWinnerSelection extends StatelessWidget {
child: Column(
children: [
20.height,
const QualifiersContainer().paddingOnly(left: 21, right: 21),
QualifiersContainer(provider: provider,).paddingOnly(left: 21, right: 21),
12.height,
InkWell(
onTap: () {

@ -9,22 +9,11 @@ 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 StatefulWidget {
class MarathonProgressContainer extends StatelessWidget {
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() {
widget.provider.startTimerForQuestion(context);
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
@ -40,20 +29,19 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
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),
child: "${provider.currentQuestionNumber.toString()} / ${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),
"${provider.totalMarathoners} ${LocaleKeys.marathoners.tr()}".toText14(),
"00:${provider.currentQuestionTime < 10 ? "0${provider.currentQuestionTime}" : provider.currentQuestionTime}"
.toText18(color: provider.currentQuestionTime < 5 ? MyColors.redColor : MyColors.black),
],
),
12.height,
stepper(widget.provider.currentQuestionNumber, widget.provider.questionCardStatus),
stepper(provider.currentQuestionNumber, provider.answerStatusesList, provider.marathonDetailModel.totalQuestions!, provider.isUserOutOfGame),
8.height,
Row(
children: <Widget>[
"${((widget.provider.currentQuestionNumber / widget.provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}".toText14(),
"${((provider.currentQuestionNumber / provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}".toText14(),
],
),
],
@ -61,15 +49,42 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
);
}
Widget stepper(int value, QuestionCardStatus status) {
print("Here Status: $status");
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: [
for (int i = 0; i < 10; i++)
if (value <= i) roundContainer(MyColors.lightGreyDeColor, i != 0) else roundContainer(status == QuestionCardStatus.correctAnswer ? MyColors.greenColor : MyColors.redColor, i != 0)
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,
)
],
),
);

@ -1,5 +1,3 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
@ -8,15 +6,25 @@ 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';
import 'package:provider/provider.dart';
class QualifiersContainer extends StatelessWidget {
class QualifiersContainer extends StatefulWidget {
final MarathonProvider provider;
const QualifiersContainer({Key? key, required this.provider}) : super(key: key);
const QualifiersContainer({Key? key}) : 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) {
MarathonProvider provider = context.watch<MarathonProvider>();
return Container(
width: double.infinity,
decoration: MyDecorations.shadowDecoration,
@ -28,14 +36,13 @@ class QualifiersContainer extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor),
"00:${provider.totalSecondsToWaitForWinner < 10 ? "0${provider.totalSecondsToWaitForWinner}" : provider.totalSecondsToWaitForWinner}"
.toText18(color: MyColors.redColor),
"00:${widget.provider.totalSecondsToWaitForWinner < 10 ? "0${widget.provider.totalSecondsToWaitForWinner}" : widget.provider.totalSecondsToWaitForWinner}".toText18(color: MyColors.redColor),
],
),
10.height,
Row(
children: <Widget>[
provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true),
widget.provider.totalQualifiers.toString().toText30(color: MyColors.greenColor, isBold: true),
2.width,
LocaleKeys.qualifiers.tr().toText16(color: MyColors.greenColor),
],

@ -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),
),
),

@ -7,7 +7,14 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.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(
@ -20,7 +27,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,
@ -41,7 +50,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),
),
@ -59,7 +68,7 @@ AppBar AppBarWidget(BuildContext context,
},
icon: const Icon(Icons.people, color: MyColors.textMixColor),
),
...actions??[]
...actions ?? []
],
);
}

Loading…
Cancel
Save