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 cardContentList = []; QuestionModel currentQuestion = QuestionModel(); List answerStatusesList = []; QuestionCardStatus questionCardStatus = QuestionCardStatus.question; int? selectedOptionIndex; int currentQuestionTime = 0; int totalSecondsToWaitForWinner = 30; int totalSecondsToWaitForMarathon = 20; int totalQualifiers = 0; bool _isLoading = false; bool get isLoading => _isLoading; set isLoading(bool value) { _isLoading = 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 = 23; int get totalMarathoners => _totalMarathoners; set totalMarathoners(int value) { _totalMarathoners = value; notifyListeners(); } //VIDEO PLAYER late VideoPlayerController videoController; Future 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(); } Future callForNewQuestion(QuestionModel newQuestion) async { 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 = await MarathonApiClient().getNextQuestion(selectedOptionId: null, questionId: null, marathonId: marathonDetailModel.id!); 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(); } 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); } } Timer timerForQuestion = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); void startTimerForQuestion(BuildContext context) { const Duration oneSec = Duration(seconds: 1); timerForQuestion = Timer.periodic( oneSec, (Timer timer) async { if (currentQuestionTime == 2) { 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); timer.cancel(); cancelTimer(); notifyListeners(); return; } } else { currentQuestionTime--; } notifyListeners(); }, ); } 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(); }, ); } void swipeCardLeft() { swiperController.swipeLeft(); notifyListeners(); } void resetValues() async { _currentQuestionNumber = 0; cardContentList.clear(); timerForWinnerSelection.cancel(); timerForQuestion.cancel(); _isMarathonCompleted = false; currentQuestionTime = 0; currentQuestion = QuestionModel(); notifyListeners(); } void cancelTimer() { timerForQuestion.cancel(); notifyListeners(); } Future getMarathonDetailsFromApi() async { isLoading = true; notifyListeners(); await MarathonApiClient().getMarathonToken().whenComplete(() async { marathonDetailModel = await MarathonApiClient().getMarathonDetails(); populateQuestionStatusesList(); isLoading = false; notifyListeners(); }); } Future 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 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 { // Utils.showLoading(context); // await MarathonApiClient().getNextQuestion(selectedOptionId: null, questionId: null, marathonId: marathonDetailModel.id!); // if (isJoined) { // // if (Utils.isLoading) { // Utils.hideLoading(AppRoutes.navigatorKey.currentContext!); // } // } // Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen); } } }