From 96e52abbb9f40aaa4525ecdd358c2a5b7f95d459 Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Mon, 5 Dec 2022 15:57:02 +0300 Subject: [PATCH] Committing before reverting from SignalR to Apis --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 8 +- assets/lottie/marathon_waiting.json | 1 + lib/api/marathon/marathon_api_client.dart | 228 +++++++++++------- lib/classes/consts.dart | 16 +- lib/classes/lottie_consts.dart | 2 +- lib/config/routes.dart | 9 +- lib/generated/locale_keys.g.dart | 3 + lib/main.dart | 4 +- lib/models/marathon/marathon_model.dart | 8 +- lib/ui/marathon/marathon_intro_screen.dart | 6 +- lib/ui/marathon/marathon_provider.dart | 216 +++++++++++++---- .../marathon_sponsor_video_screen.dart | 91 +++++++ lib/ui/marathon/marathon_waiting_screen.dart | 62 +++++ .../marathon/marathon_winner_selection.dart | 2 +- .../widgets/marathon_progress_container.dart | 63 +++-- .../marathon_qualifiers_container.dart | 25 +- lib/ui/marathon/widgets/question_card.dart | 19 +- lib/widgets/app_bar_widget.dart | 17 +- 19 files changed, 579 insertions(+), 207 deletions(-) create mode 100644 assets/lottie/marathon_waiting.json create mode 100644 lib/ui/marathon/marathon_sponsor_video_screen.dart create mode 100644 lib/ui/marathon/marathon_waiting_screen.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index b5fd9ce..4e1d930 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -505,6 +505,8 @@ "youMissedTheQuestion": "نفد منك الوقت. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.", "wrongAnswer": "إجابتك غير صحيحة. أنت خارج اللعبة. لكن يمكنك الاستمرار وكمشاهد.", "oops": "أوه!!!", - "winner": "الفائز" - + "winner": "الفائز", + "youWantToLeaveMarathon": "هل أنت متأكد أنك تريد العودة؟ سوف تخرج من المسابقة.", + "ourSponsor": "راعينا:", + "startingIn": "يبدأ في" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3ddc9f7..1b0d468 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -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" } \ No newline at end of file diff --git a/assets/lottie/marathon_waiting.json b/assets/lottie/marathon_waiting.json new file mode 100644 index 0000000..83e4756 --- /dev/null +++ b/assets/lottie/marathon_waiting.json @@ -0,0 +1 @@ +{"nm":"Loading_003","mn":"","layers":[{"ty":0,"nm":"2","mn":"","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"w":500,"h":500,"refId":"comp_0","ind":0},{"ty":0,"nm":"1","mn":"","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[250,250,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[250,250,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":60,"ix":11},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-31],"t":4},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[-31],"t":207.000008431283}],"ix":10}},"ef":[],"w":500,"h":500,"refId":"comp_1","ind":1}],"ddd":0,"h":500,"w":500,"meta":{"a":"","k":"","d":"","g":"@lottiefiles/toolkit-js 0.17.4","tc":"#ffffff"},"v":"5.2.1","fr":29.9700012207031,"op":202.000008227629,"ip":9.00000036657752,"assets":[{"nm":"","mn":"","layers":[{"ty":4,"nm":"Layer 3 Outlines 3","mn":"","sr":1,"st":5.00000020365417,"op":605.000024642155,"ip":-19.0000007738859,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[186.018,192.618,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[97.292,100.917,100],"t":4},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[104.224,99.892,100],"t":108},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[97.292,100.917,100],"t":209.000008512745}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[252.209,249.449,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[310.866],"t":4},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[-49],"t":209.000008512745}],"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[51.471,-19.6],[51.29,75.368],[-11.48,42.136],[-85.813,28.535],[-16.737,-1.394],[-18.031,-37.988],[22.567,-60.623]],"o":[[-20.195,7.689],[-24.566,-36.096],[23.791,-87.319],[15.938,-5.3],[41.874,3.489],[28.123,59.252],[-20.321,54.592]],"v":[[18.613,169.26],[-146.062,126.343],[-172.423,1.333],[1.405,-192.793],[62.244,-200.318],[155.78,-124.529],[159.191,64.737]]},"ix":2}},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":2,"cix":2,"np":0,"it":[{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.098,0.1137,0.1451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.261,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[184.153,201.962],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":0},{"ty":4,"nm":"Layer 5 Outlines 3","mn":"","sr":1,"st":5.00000020365417,"op":605.000024642155,"ip":-19.0000007738859,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[216.251,216.449,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[252.209,249.449,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":1,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 4","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[119.432,0],[0,-119.541],[-119.432,0],[0,119.542]],"o":[[-119.432,0],[0,119.542],[119.432,0],[0,-119.541]],"v":[[0,-216.449],[-216.25,0],[0,216.449],[216.251,0]]},"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gf","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[65.201,-74.966],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.16862745098039217,0.7215686274509804,0.6588235294117647,0.455,0.13725490196078433,0.615686274509804,0.5607843137254902,0.999,0.11372549019607843,0.5725490196078431,0.6666666666666666],"ix":9}},"t":1,"a":{"a":0,"k":0},"s":{"a":0,"k":[-130.068,76.804],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[216.251,216.449],"ix":2},"r":{"a":0,"k":-149.651,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}],"id":"comp_0","fr":30},{"nm":"","mn":"","layers":[{"ty":4,"nm":"Layer 3 Outlines 3","mn":"","sr":1,"st":5.00000020365417,"op":605.000024642155,"ip":-19.0000007738859,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[186.018,192.618,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[97.292,100.917,100],"t":4},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[104.224,99.892,100],"t":108},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[97.292,100.917,100],"t":209.000008512745}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[252.209,249.449,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-49],"t":4},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[310.866],"t":209.000008512745}],"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[51.471,-19.6],[51.29,75.368],[-11.48,42.136],[-85.813,28.535],[-16.737,-1.394],[-18.031,-37.988],[22.567,-60.623]],"o":[[-20.195,7.689],[-24.566,-36.096],[23.791,-87.319],[15.938,-5.3],[41.874,3.489],[28.123,59.252],[-20.321,54.592]],"v":[[18.613,169.26],[-146.062,126.343],[-172.423,1.333],[1.405,-192.793],[62.244,-200.318],[155.78,-124.529],[159.191,64.737]]},"ix":2}},{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":2,"cix":2,"np":0,"it":[{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"fl","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.098,0.1137,0.1451],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100.261,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[184.153,201.962],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":0},{"ty":4,"nm":"Layer 5 Outlines 3","mn":"","sr":1,"st":5.00000020365417,"op":605.000024642155,"ip":-19.0000007738859,"hd":false,"cl":"","ln":"","ddd":0,"bm":0,"tt":2,"hasMask":false,"td":0,"ao":0,"ks":{"a":{"a":0,"k":[216.251,216.449,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[252.209,249.449,0],"ix":2},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10}},"ef":[],"shapes":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"gr","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":1,"it":[{"ty":"sh","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 4","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[119.432,0],[0,-119.541],[-119.432,0],[0,119.542]],"o":[[-119.432,0],[0,119.542],[119.432,0],[0,-119.541]],"v":[[0,-216.449],[-216.25,0],[0,216.449],[216.251,0]]},"ix":2}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gf","bm":0,"cl":"","ln":"","hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[65.201,-74.966],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.07058823529411765,0.3411764705882353,0.396078431372549,0.424,0.07058823529411765,0.3411764705882353,0.396078431372549,1,0.07058823529411765,0.3411764705882353,0.396078431372549],"ix":9}},"t":1,"a":{"a":0,"k":0},"s":{"a":0,"k":[-130.068,76.804],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[216.251,216.449],"ix":2},"r":{"a":0,"k":-149.651,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}],"id":"comp_1","fr":30}]} \ No newline at end of file diff --git a/lib/api/marathon/marathon_api_client.dart b/lib/api/marathon/marathon_api_client.dart index b17683a..1d9eb81 100644 --- a/lib/api/marathon/marathon_api_client.dart +++ b/lib/api/marathon/marathon_api_client.dart @@ -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 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 joinMarathonAsParticipant() async { + Map jsonObject = { + "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "", + "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "", + "marathonId": AppState().getMarathonProjectId!, + }; - Future 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: [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: [ - { - "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "", - "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "", - "marathonId": AppState().getMarathonProjectId, - } - ], - ).catchError((e) { - logger.i("Error in AddParticipant: $e"); - }); - - context.read().addItemToList(ApiConsts.dummyQuestion); - - await hubConnection.invoke( - "SendQuestionToParticipant", - args: [ - { - "marathonId": "${AppState().getMarathonProjectId}", - } - ], - ).catchError((e) { - logger.i("Error in SendQuestionToParticipant: $e"); - }); - - try { - hubConnection.on("OnSendQuestionToParticipant", (List? 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? 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 onSendQuestionToParticipant(List? arguments, BuildContext context) async { - logger.i("onSendQuestionToParticipant arguments: $arguments"); + Future getNextQuestion({required String? selectedOptionId, required String? questionId, required String marathonId}) async { + Map jsonObject = { + "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 data = arguments.first! as Map; - var json = data["data"]; - QuestionModel newQuestion = QuestionModel.fromJson(json); - context.read().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 onParticipantJoin(List? arguments, BuildContext context) async { - logger.i("OnParticipantJoin arguments: $arguments"); - context.watch().totalMarathoners++; - } +// Future 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: [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: [ +// { +// "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().addItemToList(ApiConsts.dummyQuestion); +// +// await hubConnection.invoke( +// "SendQuestionToParticipant", +// args: [ +// { +// "marathonId": "${AppState().getMarathonProjectId}", +// } +// ], +// ).catchError((e) { +// Utils.confirmDialog(context, e.toString()); +// logger.i("Error in SendQuestionToParticipant: $e"); +// }); +// +// try { +// hubConnection.on("OnSendQuestionToParticipant", (List? arguments) { +// onSendQuestionToParticipant(arguments, context); +// }); +// } catch (e, s) { +// logger.i("Error in OnSendQuestionToParticipant"); +// } +// +// try { +// hubConnection.on("OnParticipantJoin", (List? arguments) { +// onParticipantJoin(arguments, context); +// }); +// } catch (e, s) { +// logger.i("Error in OnParticipantJoin"); +// } +// } +// } +// +// Future onSendQuestionToParticipant(List? arguments, BuildContext context) async { +// logger.i("onSendQuestionToParticipant arguments: $arguments"); +// +// if (arguments != null) { +// Map data = arguments.first! as Map; +// var json = data["data"]; +// QuestionModel newQuestion = QuestionModel.fromJson(json); +// AppRoutes.navigatorKey.currentContext!.read().onNewQuestionReceived(newQuestion); +// } +// } +// +// Future onParticipantJoin(List? arguments, BuildContext context) async { +// logger.i("OnParticipantJoin arguments: $arguments"); +// context.watch().totalMarathoners++; +// } } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index fb7079f..f53c02f 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -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 diff --git a/lib/classes/lottie_consts.dart b/lib/classes/lottie_consts.dart index d7d38bc..7846f6c 100644 --- a/lib/classes/lottie_consts.dart +++ b/lib/classes/lottie_consts.dart @@ -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"; - } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 7cf8a35..d1b6b08 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -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 navigatorKey = GlobalKey(); + 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 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(), }; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 08f8ad4..2229b3b 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -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'; } diff --git a/lib/main.dart b/lib/main.dart index 4d686b8..4709be1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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 { // }); // } // } + diff --git a/lib/models/marathon/marathon_model.dart b/lib/models/marathon/marathon_model.dart index b32530c..6fd1f5c 100644 --- a/lib/models/marathon/marathon_model.dart +++ b/lib/models/marathon/marathon_model.dart @@ -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; Sponsors( @@ -186,7 +186,7 @@ class SponsorPrizes { } Map toJson() { - Map data = new Map(); + Map data = {}; data['id'] = id; data['marathonPrizeEn'] = marathonPrizeEn; data['marathonPrizeAr'] = marathonPrizeAr; diff --git a/lib/ui/marathon/marathon_intro_screen.dart b/lib/ui/marathon/marathon_intro_screen.dart index ef29809..f47b47e 100644 --- a/lib/ui/marathon/marathon_intro_screen.dart +++ b/lib/ui/marathon/marathon_intro_screen.dart @@ -25,7 +25,10 @@ class MarathonIntroScreen extends StatelessWidget { children: [ 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 { ); } } - diff --git a/lib/ui/marathon/marathon_provider.dart b/lib/ui/marathon/marathon_provider.dart index a178710..46149a4 100644 --- a/lib/ui/marathon/marathon_provider.dart +++ b/lib/ui/marathon/marathon_provider.dart @@ -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 cardContentList = []; QuestionModel currentQuestion = QuestionModel(); - + List answerStatusesList = []; 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 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 onJoinMarathonPressed(BuildContext context) async { + Future 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 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); + } + } } diff --git a/lib/ui/marathon/marathon_sponsor_video_screen.dart b/lib/ui/marathon/marathon_sponsor_video_screen.dart new file mode 100644 index 0000000..c81f59b --- /dev/null +++ b/lib/ui/marathon/marathon_sponsor_video_screen.dart @@ -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 onSponsorVideoClosed(BuildContext context) async { + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + MarathonProvider provider = context.watch(); + return WillPopScope( + onWillPop: () { + provider.videoController.dispose(); + return Future.value(true); + }, + child: Scaffold( + backgroundColor: MyColors.black, + body: SafeArea( + child: Stack( + children: [ + 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), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/marathon/marathon_waiting_screen.dart b/lib/ui/marathon/marathon_waiting_screen.dart new file mode 100644 index 0000000..9f52bf6 --- /dev/null +++ b/lib/ui/marathon/marathon_waiting_screen.dart @@ -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(); + 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: [ + Container( + width: double.infinity, + margin: const EdgeInsets.all(21), + decoration: MyDecorations.shadowDecoration, + child: Stack( + children: [ + Align( + child: Lottie.asset(MyLottieConsts.marathonWaiting, height: 200), + ), + Align( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + LocaleKeys.startingIn.tr().toText16(), + "00:${provider.currentQuestionTime < 10 ? "0${provider.currentQuestionTime}" : provider.currentQuestionTime}" + .toText18(color: provider.currentQuestionTime < 5 ? MyColors.redColor : MyColors.black), + ], + ), + ), + ], + ), + ).expanded, + ], + ), + ); + } +} diff --git a/lib/ui/marathon/marathon_winner_selection.dart b/lib/ui/marathon/marathon_winner_selection.dart index 6aeb3d2..ed49f04 100644 --- a/lib/ui/marathon/marathon_winner_selection.dart +++ b/lib/ui/marathon/marathon_winner_selection.dart @@ -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: () { diff --git a/lib/ui/marathon/widgets/marathon_progress_container.dart b/lib/ui/marathon/widgets/marathon_progress_container.dart index fbf0428..4f76301 100644 --- a/lib/ui/marathon/widgets/marathon_progress_container.dart +++ b/lib/ui/marathon/widgets/marathon_progress_container.dart @@ -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 createState() => _MarathonProgressContainerState(); -} - -class _MarathonProgressContainerState extends State { - @override - void initState() { - widget.provider.startTimerForQuestion(context); - super.initState(); - } - @override Widget build(BuildContext context) { return Container( @@ -40,20 +29,19 @@ class _MarathonProgressContainerState extends State { 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.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 { ); } - 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 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: [ + for (int i = 0; i < totalQuestions - 1; i++) + if (value <= i) + roundContainer(MyColors.lightGreyDeColor, i != 0) + else + roundContainer( + getStepColor(statusesList[i], isOutOfGame), + i != 0, + ) ], ), ); diff --git a/lib/ui/marathon/widgets/marathon_qualifiers_container.dart b/lib/ui/marathon/widgets/marathon_qualifiers_container.dart index 50a7e6d..0308027 100644 --- a/lib/ui/marathon/widgets/marathon_qualifiers_container.dart +++ b/lib/ui/marathon/widgets/marathon_qualifiers_container.dart @@ -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 createState() => _QualifiersContainerState(); +} + +class _QualifiersContainerState extends State { + @override + void initState() { + widget.provider.startTimerForWinnerSelection(); + super.initState(); + } @override Widget build(BuildContext context) { - MarathonProvider provider = context.watch(); return Container( width: double.infinity, decoration: MyDecorations.shadowDecoration, @@ -28,14 +36,13 @@ class QualifiersContainer extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ 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: [ - 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), ], diff --git a/lib/ui/marathon/widgets/question_card.dart b/lib/ui/marathon/widgets/question_card.dart index af538f8..4009983 100644 --- a/lib/ui/marathon/widgets/question_card.dart +++ b/lib/ui/marathon/widgets/question_card.dart @@ -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(); 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), ), ), diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart index c2129c1..6842dde 100644 --- a/lib/widgets/app_bar_widget.dart +++ b/lib/widgets/app_bar_widget.dart @@ -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? actions}) { + {required String title, + bool showHomeButton = true, + bool showNotificationButton = false, + bool showMemberButton = false, + String? image, + List? 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 ?? [] ], ); }