import 'dart:async'; import 'package:appinio_swiper/appinio_swiper.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/marathon/marathon_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/models/marathon/marathon_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart'; import 'package:mohem_flutter_app/models/marathon/winner_model.dart'; import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; import 'package:video_player/video_player.dart'; class MarathonProvider extends ChangeNotifier { //****************VARIABLES********** final AppinioSwiperController swiperController = AppinioSwiperController(); MarathonDetailModel marathonDetailModel = MarathonDetailModel(); List cardContentList = []; QuestionModel currentQuestion = QuestionModel(); List answerStatusesList = []; QuestionCardStatus questionCardStatus = QuestionCardStatus.question; List? selectedWinners; int? selectedOptionIndex; String? selectedOptionId; int? totalQualifiers; String? gapTimeImage; String? gapTimeText; int? gapTimeType; bool iAmWinner = false; bool isGettingQualifiers = false; bool isPrivilegedWithMarathon = false; bool _isLoading = false; bool get isLoading => _isLoading; set isLoading(bool value) { _isLoading = value; notifyListeners(); } bool _isUpComingMarathon = true; bool get isUpComingMarathon => _isUpComingMarathon; set isUpComingMarathon(bool value) { _isUpComingMarathon = value; notifyListeners(); } bool _itsMarathonTime = false; bool get itsMarathonTime => _itsMarathonTime; set itsMarathonTime(bool value) { _itsMarathonTime = value; notifyListeners(); } bool _isMarathonCompleted = false; bool get isMarathonCompleted => _isMarathonCompleted; set isMarathonCompleted(bool value) { _isMarathonCompleted = value; notifyListeners(); } bool isUserOutOfGame = false; set updateIsUserOutOfGame(bool value) { isUserOutOfGame = value; notifyListeners(); } int _currentQuestionNumber = 0; int get currentQuestionNumber => _currentQuestionNumber; set currentQuestionNumber(int value) { _currentQuestionNumber = value; notifyListeners(); } int _totalMarathoners = 0; int get totalMarathoners => _totalMarathoners; set totalMarathoners(int value) { _totalMarathoners = value; notifyListeners(); } //****************SPONSOR VIDEO PLAYER********** late VideoPlayerController videoController; Future initializeVideoPlayer() async { videoController = VideoPlayerController.network(ApiConsts.marathonBaseUrlServices + marathonDetailModel.sponsors!.first.video!); 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 sponsorsSecondsCounter = 0; 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 { sponsorsSecondsCounter++; if (totalSponsorVideoSeconds == 0) { timer.cancel(); notifyListeners(); return; } else { totalSponsorVideoSeconds--; } notifyListeners(); }, ); } int totalSecondsToWaitForMarathon = 20; Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); void startTimerToWaitForMarathon() { const Duration oneSec = Duration(seconds: 1); timerToWaitForMarathon = Timer.periodic( oneSec, (Timer timer) async { if (totalSecondsToWaitForMarathon == 0) { callNextQuestionApi(); timer.cancel(); return; } 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 { print("here is the timer: $totalCurrentQuestionTime"); // This 2 is just to show the color of answer tile for 1 and then update card status if (totalCurrentQuestionTime - currentGapTime == 1) { getCorrectAnswerAndUpdateAnswerColor(); } if (totalCurrentQuestionTime == currentGapTime) { totalCurrentQuestionTime--; updateCardStatusToAnswer(); scheduleMicrotask(() async { if (isUserOutOfGame) { await callNextQuestionApi(); } else { await callSubmitOptionApi().then((bool value) async { if (value) { await callNextQuestionApi(); } }); } }); } if (totalCurrentQuestionTime == 0) { gapTimeImage = currentQuestion.gapImage; gapTimeText = currentQuestion.gapText; gapTimeType = currentQuestion.gapType; updateCardData(); if (currentQuestionNumber - 1 == marathonDetailModel.totalQuestions!) { isGettingQualifiers = true; updateQuestionCardStatus(QuestionCardStatus.findingWinner); timer.cancel(); cancelTimer(); notifyListeners(); } return; } else { if (totalCurrentQuestionTime != currentGapTime) { 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 == 1) { timer.cancel(); callGetSelectedWinnersApi(); updateQuestionCardStatus(QuestionCardStatus.winnerFound); return; } else if (totalSecondsToWaitForWinner == 15) { totalSecondsToWaitForWinner--; await callGetQualifiersApi(); } else { totalSecondsToWaitForWinner--; } notifyListeners(); }, ); } //****************FUNCTIONS********* Future callSubmitOptionApi() async { return await MarathonApiClient().submitSelectedOption(marathonId: marathonDetailModel.id!, questionId: currentQuestion.id, selectedAnswerId: selectedOptionId); } Future callGetQualifiersApi() async { totalQualifiers = await MarathonApiClient().getQualifiers(marathonId: marathonDetailModel.id!); isGettingQualifiers = false; notifyListeners(); } Future callGetSelectedWinnersApi() async { selectedWinners = await MarathonApiClient().getSelectedWinner(marathonId: marathonDetailModel.id!); if (selectedWinners != null) { selectedWinners!.removeWhere((WinnerModel element) { if (element.employeeId == AppState().memberInformationList!.eMPLOYEENUMBER) { iAmWinner = true; return true; } else { return false; } }); } notifyListeners(); } Future callNextQuestionApi() async { print("Called callNextQuestionApi!!"); if (currentQuestionNumber < marathonDetailModel.totalQuestions!) { if (currentQuestionNumber == 0) { Utils.showLoading(AppRoutes.navigatorKey.currentContext!); currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!)); gapTimeImage = currentQuestion.gapImage; gapTimeText = currentQuestion.gapText; gapTimeType = currentQuestion.gapType; 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() { print("Called updateCardData!!"); if (currentQuestionNumber > 0) { swiperController.swipeLeft(); } selectedOptionIndex = null; currentQuestionNumber++; cardContentList.add(const CardContent()); totalCurrentQuestionTime = currentQuestion.questionTime! + currentQuestion.nextQuestGap!; currentGapTime = currentQuestion.nextQuestGap!; totalMarathoners = currentQuestion.remainingParticipantCount!; questionCardStatus = QuestionCardStatus.question; notifyListeners(); } void populateQuestionStatusesList() { if (marathonDetailModel.totalQuestions != null) { for (int i = 0; i < marathonDetailModel.totalQuestions!; 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(); } void getCorrectAnswerAndUpdateAnswerColor() { if (selectedOptionIndex != null) { if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) { updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!); } else { updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!); } } } void updateCardStatusToAnswer() { if (currentQuestionNumber == 0) { return; } 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); } } void resetProgressColorValues() {} void resetValues() async { _currentQuestionNumber = 0; cardContentList.clear(); timerForWinnerSelection.cancel(); timerForSponsorVideo.cancel(); timerToWaitForMarathon.cancel(); timerForQuestion.cancel(); _isMarathonCompleted = false; isUserOutOfGame = false; totalCurrentQuestionTime = 0; sponsorsSecondsCounter = 0; totalSponsorVideoSeconds = 0; totalSecondsToWaitForMarathon = 0; currentGapTime = 0; currentQuestion = QuestionModel(); if (answerStatusesList.isNotEmpty) { for (int i = 0; i < answerStatusesList.length; i++) { answerStatusesList[i] = QuestionCardStatus.question; } } notifyListeners(); } void cancelTimer() { timerForQuestion.cancel(); notifyListeners(); } bool checkIfPrivilegedForMarathon() { for (PrivilegeListModel element in AppState().privilegeListModel!) { if (element.serviceName == "Marathon") { if (element.previlege != null) { return element.previlege!; } } } return false; } Future getMarathonDetailsFromApi() async { isLoading = true; notifyListeners(); isPrivilegedWithMarathon = checkIfPrivilegedForMarathon(); if (isPrivilegedWithMarathon) { await MarathonApiClient().getMarathonToken().whenComplete(() async { marathonDetailModel = await MarathonApiClient().getMarathonDetails(); if (marathonDetailModel.id == null) { isUpComingMarathon = false; isLoading = false; notifyListeners(); return; } populateQuestionStatusesList(); }); } isLoading = false; notifyListeners(); } Future onJoinMarathonPressed(BuildContext context) async { try { Utils.showLoading(context); resetValues(); int? remainingTime = await MarathonApiClient().joinMarathonAsParticipant(); if (remainingTime != null) { totalSecondsToWaitForMarathon = remainingTime; notifyListeners(); startTimerToWaitForMarathon(); Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen); } } catch (e) { Utils.hideLoading(context); Utils.confirmDialog(context, e.toString()); } } }