You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
mohemm-flutter-app/lib/ui/marathon/marathon_provider.dart

394 lines
12 KiB
Dart

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;
String? selectedOptionId;
int totalQualifiers = 0;
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<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();
}
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 swipeCardLeft() {
swiperController.swipeLeft();
notifyListeners();
}
void resetValues() async {
_currentQuestionNumber = 0;
cardContentList.clear();
timerForWinnerSelection.cancel();
timerForQuestion.cancel();
_isMarathonCompleted = false;
totalCurrentQuestionTime = 0;
currentQuestion = QuestionModel();
notifyListeners();
}
void cancelTimer() {
timerForQuestion.cancel();
notifyListeners();
}
Future<void> getMarathonDetailsFromApi() async {
isLoading = true;
notifyListeners();
await MarathonApiClient().getMarathonToken().whenComplete(() async {
marathonDetailModel = await MarathonApiClient().getMarathonDetails();
if (marathonDetailModel.id == null) {
isUpComingMarathon = false;
notifyListeners();
return;
}
populateQuestionStatusesList();
isLoading = false;
notifyListeners();
});
}
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());
}
}
}
}