Merge branch 'master' into development_haroon

merge-requests/87/head
haroon amjad 3 years ago
commit a947b759f8

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

@ -500,5 +500,9 @@
"codeExpire": "انتهت صلاحية رمز التحقق", "codeExpire": "انتهت صلاحية رمز التحقق",
"typeheretoreply": "اكتب هنا للرد", "typeheretoreply": "اكتب هنا للرد",
"favorite": "مفضلتي", "favorite": "مفضلتي",
"searchfromchat": "البحث من الدردشة" "searchfromchat": "البحث من الدردشة",
"yourAnswerCorrect": "إجابتك صحيحة",
"youMissedTheQuestion": "فاتك !! أنت خارج اللعبة. لكن يمكنك المتابعة.",
"wrongAnswer": "إجابة خاطئة! أنت خارج اللعبة. لكن يمكنك المتابعة."
} }

@ -500,6 +500,9 @@
"allQuestionsCorrect": "You have answered all questions correct", "allQuestionsCorrect": "You have answered all questions correct",
"typeheretoreply": "Type here to reply", "typeheretoreply": "Type here to reply",
"favorite" : "My Favorites", "favorite" : "My Favorites",
"searchfromchat": "Search from chat" "searchfromchat": "Search from chat",
"yourAnswerCorrect": "Your answer is correct",
"youMissedTheQuestion": "You Missed !! You are out of the game. But you can follow up.",
"wrongAnswer": "Wrong Answer! You are out of the game. But you can follow up."
} }

5
ios/.gitignore vendored

@ -31,3 +31,8 @@ Runner/GeneratedPluginRegistrant.*
!default.mode2v3 !default.mode2v3
!default.pbxuser !default.pbxuser
!default.perspectivev3 !default.perspectivev3
ios/Podfile
ios/Runner/Runner.entitlements

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.cloudsolutions.mohemm</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.networking.HotspotConfiguration</key>
<true/>
<key>com.apple.developer.networking.networkextension</key>
<array/>
<key>com.apple.developer.networking.wifi-info</key>
<true/>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>TAG</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.com.cloudsolutions.mohemm</string>
</array>
</dict>
</plist>

@ -28,9 +28,7 @@ class ChatApiClient {
"password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG", "password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG",
}, },
); );
user.UserAutoLoginModel userLoginResponse = user.userAutoLoginModelFromJson( user.UserAutoLoginModel userLoginResponse = user.userAutoLoginModelFromJson(response.body);
response.body,
);
return userLoginResponse; return userLoginResponse;
} }
@ -42,9 +40,7 @@ class ChatApiClient {
return searchUserJsonModel(response.body); return searchUserJsonModel(response.body);
} }
List<ChatUser> searchUserJsonModel(String str) => List<ChatUser>.from( List<ChatUser> searchUserJsonModel(String str) => List<ChatUser>.from(json.decode(str).map((x) => ChatUser.fromJson(x)));
json.decode(str).map((x) => ChatUser.fromJson(x)),
);
Future<ChatUserModel> getRecentChats() async { Future<ChatUserModel> getRecentChats() async {
try { try {
@ -58,7 +54,6 @@ class ChatApiClient {
} catch (e) { } catch (e) {
e as APIException; e as APIException;
if (e.message == "api_common_unauthorized") { if (e.message == "api_common_unauthorized") {
logger.d("Token Generated On APIIIIII");
user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken(); user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
if (userLoginResponse.response != null) { if (userLoginResponse.response != null) {
AppState().setchatUserDetails = userLoginResponse; AppState().setchatUserDetails = userLoginResponse;
@ -98,9 +93,7 @@ class ChatApiClient {
AppState().setchatUserDetails = userLoginResponse; AppState().setchatUserDetails = userLoginResponse;
getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal); getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
} else { } else {
Utils.showToast( Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr");
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
);
} }
} }
throw e; throw e;
@ -108,13 +101,7 @@ class ChatApiClient {
} }
Future<fav.FavoriteChatUser> favUser({required int userID, required int targetUserID}) async { Future<fav.FavoriteChatUser> favUser({required int userID, required int targetUserID}) async {
Response response = await ApiClient().postJsonForResponse( Response response = await ApiClient().postJsonForResponse("${ApiConsts.chatFavUser}addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token);
"${ApiConsts.chatFavUser}addFavUser",
{
"targetUserId": targetUserID,
"userId": userID,
},
token: AppState().chatDetails!.response!.token);
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
return favoriteChatUser; return favoriteChatUser;
} }
@ -137,9 +124,7 @@ class ChatApiClient {
AppState().setchatUserDetails = userLoginResponse; AppState().setchatUserDetails = userLoginResponse;
unFavUser(userID: userID, targetUserID: targetUserID); unFavUser(userID: userID, targetUserID: targetUserID);
} else { } else {
Utils.showToast( Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr");
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
);
} }
} }
throw e; throw e;

@ -0,0 +1,178 @@
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 {
Future<String> getMarathonToken() async {
String employeeUserName = AppState().getUserName ?? "";
String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? "";
Map<String, String> jsonObject = <String, String>{"userName": employeeUserName, "password": employeeSession};
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonParticipantLoginUrl, jsonObject);
var json = jsonDecode(response.body);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
AppState().setMarathonToken = marathonModel.data["token"] ?? "";
print("bearer: ${AppState().getMarathonToken}");
return marathonModel.data["token"] ?? "";
} else {
//TODO : DO ERROR HANDLING HERE
return "";
}
} else {
//TODO : DO ERROR HANDLING HERE
return "";
}
}
Future<String> getProjectId() async {
Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonProjectGetUrl, <String, dynamic>{}, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);
if (marathonModel.statusCode == 200) {
if (marathonModel.data != null && marathonModel.isSuccessful == true) {
logger.i("message: ${marathonModel.data[0]["id"]}");
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 "";
}
}
Future<MarathonDetailModel> getMarathonDetails() async {
String payrollString = AppState().postParamsObject?.payrollCodeStr.toString() ?? "CS";
Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonUpcomingUrl + payrollString, token: AppState().getMarathonToken ?? await getMarathonToken());
var json = jsonDecode(response.body);
MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json);
MarathonDetailModel marathonDetailModel = MarathonDetailModel.fromJson(marathonGenericModel.data);
AppState().setMarathonProjectId = marathonDetailModel.id!;
return marathonDetailModel;
}
late HubConnection hubConnection;
L.Logger logger = L.Logger();
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");
}
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);
context.read<MarathonProvider>().onNewQuestionReceived(newQuestion);
}
}
Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
logger.i("OnParticipantJoin arguments: $arguments");
context.watch<MarathonProvider>().totalMarathoners++;
}
}

@ -51,6 +51,18 @@ class AppState {
String? get getMohemmWifiPassword => _mohemmWifiPassword; String? get getMohemmWifiPassword => _mohemmWifiPassword;
String? _marathonToken ;
set setMarathonToken(String token) => _marathonToken = token;
String? get getMarathonToken => _marathonToken;
String? _projectID ;
set setMarathonProjectId(String token) => _projectID = token;
String? get getMarathonProjectId => _projectID;
final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 5.0, mobileType: Platform.isAndroid ? "android" : "ios"); final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 5.0, mobileType: Platform.isAndroid ? "android" : "ios");
void setPostParamsInitConfig() { void setPostParamsInitConfig() {

@ -62,4 +62,5 @@ class MyColors {
static const Color grey9DColor = Color(0xff9D9D9D); static const Color grey9DColor = Color(0xff9D9D9D);
static const Color darkDigitColor = Color(0xff2D2F39); static const Color darkDigitColor = Color(0xff2D2F39);
static const Color grey71Color = Color(0xff717171); static const Color grey71Color = Color(0xff717171);
static const Color darkGrey3BColor = Color(0xff3B3B3B);
} }

@ -1,7 +1,9 @@
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts { class ApiConsts {
//static String baseUrl = "http://10.200.204.20:2801/"; // Local server //static String baseUrl = "http://10.200.204.20:2801/"; // Local server
static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server //static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server
// static String baseUrl = "https://hmgwebservices.com"; // Live server static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server static String baseUrlServices = baseUrl + "/Services/"; // server
// static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server
static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/"; static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/";
@ -24,6 +26,18 @@ class ApiConsts {
static String chatMediaImageUploadUrl = chatServerBaseApiUrl + "shared/"; static String chatMediaImageUploadUrl = chatServerBaseApiUrl + "shared/";
static String chatFavUser = chatServerBaseApiUrl + "FavUser/"; static String chatFavUser = chatServerBaseApiUrl + "FavUser/";
static String chatUserImages = chatServerBaseUrl + "empservice/api/employee/"; 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";
//DummyCards for the UI
static CardContent dummyQuestion = const CardContent();
} }
class SharedPrefsConsts { class SharedPrefsConsts {

@ -3,6 +3,26 @@ import 'package:intl/intl.dart';
class DateUtil { class DateUtil {
/// convert String To Date function /// convert String To Date function
/// [date] String we want to convert /// [date] String we want to convert
///
///
static DateTime convertStringToDateMarathon(String date) {
// /Date(1585774800000+0300)/
if (date != null) {
const start = "/Date(";
const end = "+0300)";
int startIndex = date.indexOf(start);
int endIndex = date.indexOf(end, startIndex + start.length);
return DateTime.fromMillisecondsSinceEpoch(
int.parse(
date.substring(startIndex + start.length, endIndex),
),
);
} else
return DateTime.now();
}
static DateTime convertStringToDate(String date) { static DateTime convertStringToDate(String date) {
// /Date(1585774800000+0300)/ // /Date(1585774800000+0300)/
if (date != null) { if (date != null) {
@ -55,9 +75,10 @@ class DateUtil {
} }
return DateTime.now(); return DateTime.now();
} else } else {
return DateTime.now(); return DateTime.now();
} }
}
static String convertDateToString(DateTime date) { static String convertDateToString(DateTime date) {
const start = "/Date("; const start = "/Date(";
@ -94,7 +115,7 @@ class DateUtil {
} }
static String convertDateMSToJsonDate(utc) { static String convertDateMSToJsonDate(utc) {
var dt = new DateTime.fromMicrosecondsSinceEpoch(utc); var dt = DateTime.fromMicrosecondsSinceEpoch(utc);
return "/Date(" + (dt.millisecondsSinceEpoch * 1000).toString() + '+0300' + ")/"; return "/Date(" + (dt.millisecondsSinceEpoch * 1000).toString() + '+0300' + ")/";
} }
@ -416,7 +437,7 @@ class DateUtil {
/// get data formatted like 10:30 according to lang /// get data formatted like 10:30 according to lang
static String formatDateToTimeLang(DateTime date, bool isArabic) { static String formatDateToTimeLang(DateTime date, bool isArabic) {
return DateFormat('HH:mm', isArabic ? "ar_SA" : "en_US").format(date); return DateFormat('HH:mm a', isArabic ? "ar_SA" : "en_US").format(date);
} }
/// get data formatted like 26/4/2020 10:30 /// get data formatted like 26/4/2020 10:30

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
class MyDecorations { class MyDecorations {
static Decoration shadowDecoration = BoxDecoration( static Decoration shadowDecoration = BoxDecoration(
@ -22,4 +23,18 @@ class MyDecorations {
); );
return answerContainerDecoration; return answerContainerDecoration;
} }
static Decoration getAnswersContainerColor(QuestionsOptionStatus questionsOptionStatus) {
switch (questionsOptionStatus) {
case QuestionsOptionStatus.correct:
return getContainersDecoration(MyColors.greenColor);
case QuestionsOptionStatus.wrong:
return getContainersDecoration(MyColors.redColor);
case QuestionsOptionStatus.selected:
return getContainersDecoration(MyColors.yellowColorII);
case QuestionsOptionStatus.unSelected:
return getContainersDecoration(MyColors.greyF7Color);
}
}
} }

@ -4,4 +4,6 @@ class MyLottieConsts {
static const String celebrate2Lottie = "assets/lottie/celebrate2.json"; static const String celebrate2Lottie = "assets/lottie/celebrate2.json";
static const String winnerLottie = "assets/lottie/winner3.json"; static const String winnerLottie = "assets/lottie/winner3.json";
static const String allQuestions = "assets/lottie/all_questions.json"; static const String allQuestions = "assets/lottie/all_questions.json";
static const String wrongAnswerGif = "assets/images/wrong_answer.gif";
} }

@ -487,5 +487,8 @@ abstract class LocaleKeys {
static const typeheretoreply = 'typeheretoreply'; static const typeheretoreply = 'typeheretoreply';
static const favorite = 'favorite'; static const favorite = 'favorite';
static const searchfromchat = 'searchfromchat'; static const searchfromchat = 'searchfromchat';
static const yourAnswerCorrect = 'yourAnswerCorrect';
static const youMissedTheQuestion = 'youMissedTheQuestion';
static const wrongAnswer = 'wrongAnswer';
} }

@ -0,0 +1,31 @@
class MarathonGenericModel {
MarathonGenericModel({
this.data,
this.isSuccessful,
this.message,
this.statusCode,
this.errors,
});
dynamic data;
bool? isSuccessful;
String? message;
int? statusCode;
dynamic errors;
factory MarathonGenericModel.fromJson(Map<String, dynamic> json) => MarathonGenericModel(
data: json["data"],
isSuccessful: json["isSuccessful"],
message: json["message"],
statusCode: json["statusCode"],
errors: json["errors"],
);
Map<String, dynamic> toJson() => {
"data": data,
"isSuccessful": isSuccessful,
"message": message,
"statusCode": statusCode,
"errors": errors,
};
}

@ -0,0 +1,256 @@
class MarathonDetailModel {
String? id;
String? titleEn;
String? titleAr;
String? descEn;
String? descAr;
int? winDeciderTime;
int? winnersCount;
int? questGapTime;
String? startTime;
String? endTime;
int? marathoneStatusId;
String? scheduleTime;
int? selectedLanguage;
Projects? projects;
List<Sponsors>? sponsors;
List<Questions>? questions;
int? totalQuestions;
MarathonDetailModel(
{id,
titleEn,
titleAr,
descEn,
descAr,
winDeciderTime,
winnersCount,
questGapTime,
startTime,
endTime,
marathoneStatusId,
scheduleTime,
selectedLanguage,
projects,
sponsors,
questions,
totalQuestions});
MarathonDetailModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
titleEn = json['titleEn'];
titleAr = json['titleAr'];
descEn = json['descEn'];
descAr = json['descAr'];
winDeciderTime = json['winDeciderTime'];
winnersCount = json['winnersCount'];
questGapTime = json['questGapTime'];
startTime = json['startTime'];
endTime = json['endTime'];
marathoneStatusId = json['marathoneStatusId'];
scheduleTime = json['scheduleTime'];
selectedLanguage = json['selectedLanguage'];
projects = json['projects'] != null
? Projects.fromJson(json['projects'])
: null;
if (json['sponsors'] != null) {
sponsors = <Sponsors>[];
json['sponsors'].forEach((v) {
sponsors!.add( Sponsors.fromJson(v));
});
}
if (json['questions'] != null) {
questions = <Questions>[];
json['questions'].forEach((v) {
questions!.add( Questions.fromJson(v));
});
}
totalQuestions = json["totalQuestions"];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['titleEn'] = titleEn;
data['titleAr'] = titleAr;
data['descEn'] = descEn;
data['descAr'] = descAr;
data['winDeciderTime'] = winDeciderTime;
data['winnersCount'] = winnersCount;
data['questGapTime'] = questGapTime;
data['startTime'] = startTime;
data['endTime'] = endTime;
data['marathoneStatusId'] = marathoneStatusId;
data['scheduleTime'] = scheduleTime;
data['selectedLanguage'] = selectedLanguage;
if (projects != null) {
data['projects'] = projects!.toJson();
}
if (sponsors != null) {
data['sponsors'] = sponsors!.map((v) => v.toJson()).toList();
}
if (questions != null) {
data['questions'] = questions!.map((v) => v.toJson()).toList();
}
data['totalQuestions'] = totalQuestions;
return data;
}
}
class Projects {
String? id;
String? nameEn;
String? nameAr;
String? projectCode;
Projects({id, nameEn, nameAr, projectCode});
Projects.fromJson(Map<String, dynamic> json) {
id = json['id'];
nameEn = json['nameEn'];
nameAr = json['nameAr'];
projectCode = json['projectCode'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['nameEn'] = nameEn;
data['nameAr'] = nameAr;
data['projectCode'] = projectCode;
return data;
}
}
class Sponsors {
String? id;
String? nameEn;
Null? nameAr;
String? image;
Null? video;
Null? logo;
List<SponsorPrizes>? sponsorPrizes;
Sponsors(
{id,
nameEn,
nameAr,
image,
video,
logo,
sponsorPrizes});
Sponsors.fromJson(Map<String, dynamic> json) {
id = json['id'];
nameEn = json['nameEn'];
nameAr = json['nameAr'];
image = json['image'];
video = json['video'];
logo = json['logo'];
if (json['sponsorPrizes'] != null) {
sponsorPrizes = <SponsorPrizes>[];
json['sponsorPrizes'].forEach((v) {
sponsorPrizes!.add( SponsorPrizes.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['nameEn'] = nameEn;
data['nameAr'] = nameAr;
data['image'] = image;
data['video'] = video;
data['logo'] = logo;
if (sponsorPrizes != null) {
data['sponsorPrizes'] =
sponsorPrizes!.map((v) => v.toJson()).toList();
}
return data;
}
}
class SponsorPrizes {
String? id;
String? marathonPrizeEn;
String? marathonPrizeAr;
SponsorPrizes({id, marathonPrizeEn, marathonPrizeAr});
SponsorPrizes.fromJson(Map<String, dynamic> json) {
id = json['id'];
marathonPrizeEn = json['marathonPrizeEn'];
marathonPrizeAr = json['marathonPrizeAr'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = id;
data['marathonPrizeEn'] = marathonPrizeEn;
data['marathonPrizeAr'] = marathonPrizeAr;
return data;
}
}
class Questions {
String? id;
String? titleEn;
String? titleAr;
String? marathonId;
int? questionTypeId;
int? questionTime;
int? nextQuestGap;
int? gapType;
String? gapValue;
String? gapImage;
int? questOptionsLimit;
List? questionOptions;
Questions(
{id,
titleEn,
titleAr,
marathonId,
questionTypeId,
questionTime,
nextQuestGap,
gapType,
gapValue,
gapImage,
questOptionsLimit,
questionOptions});
Questions.fromJson(Map<String, dynamic> json) {
id = json['id'];
titleEn = json['titleEn'];
titleAr = json['titleAr'];
marathonId = json['marathonId'];
questionTypeId = json['questionTypeId'];
questionTime = json['questionTime'];
nextQuestGap = json['nextQuestGap'];
gapType = json['gapType'];
gapValue = json['gapValue'];
gapImage = json['gapImage'];
questOptionsLimit = json['questOptionsLimit'];
questionOptions = json['questionOptions'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['titleEn'] = titleEn;
data['titleAr'] = titleAr;
data['marathonId'] = marathonId;
data['questionTypeId'] = questionTypeId;
data['questionTime'] = questionTime;
data['nextQuestGap'] = nextQuestGap;
data['gapType'] = gapType;
data['gapValue'] = gapValue;
data['gapImage'] = gapImage;
data['questOptionsLimit'] = questOptionsLimit;
data['questionOptions'] = questionOptions;
return data;
}
}

@ -0,0 +1,117 @@
enum QuestionsOptionStatus { correct, wrong, selected, unSelected }
enum QuestionCardStatus { question, wrongAnswer, correctAnswer, skippedAnswer, completed, findingWinner, winnerFound }
class QuestionModel {
String? id;
String? titleEn;
String? titleAr;
String? marathonId;
int? questionTypeId;
int? questionTime;
int? nextQuestGap;
int? gapType;
String? gapText;
String? gapImage;
int? questOptionsLimit;
List<QuestionOptions>? questionOptions;
QuestionModel({
String? id,
String? titleEn,
String? titleAr,
String? marathonId,
int? questionTypeId,
int? questionTime,
int? nextQuestGap,
int? gapType,
String? gapText,
String? gapImage,
int? questOptionsLimit,
List<QuestionOptions>? questionOptions,
});
QuestionModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
titleEn = json['titleEn'];
titleAr = json['titleAr'];
marathonId = json['marathonId'];
questionTypeId = json['questionTypeId'];
questionTime = json['questionTime'];
nextQuestGap = json['nextQuestGap'];
gapType = json['gapType'];
gapText = json['gapText'];
gapImage = json['gapImage'];
questOptionsLimit = json['questOptionsLimit'];
if (json['questionOptions'] != null) {
questionOptions = <QuestionOptions>[];
json['questionOptions'].forEach((v) {
questionOptions!.add(QuestionOptions.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['titleEn'] = titleEn;
data['titleAr'] = titleAr;
data['marathonId'] = marathonId;
data['questionTypeId'] = questionTypeId;
data['questionTime'] = questionTime;
data['nextQuestGap'] = nextQuestGap;
data['gapType'] = gapType;
data['gapText'] = gapText;
data['gapImage'] = gapImage;
data['questOptionsLimit'] = questOptionsLimit;
if (questionOptions != null) {
data['questionOptions'] = questionOptions!.map((v) => v.toJson()).toList();
}
return data;
}
}
class QuestionOptions {
String? id;
String? titleEn;
String? titleAr;
String? questionId;
int? sequence;
String? image;
bool? isCorrectOption;
QuestionsOptionStatus? optionStatus;
QuestionOptions({
String? id,
String? titleEn,
String? titleAr,
String? questionId,
int? sequence,
String? image,
bool? isCorrectOption,
QuestionsOptionStatus? optionStatus,
});
QuestionOptions.fromJson(Map<String, dynamic> json) {
id = json['id'];
titleEn = json['titleEn'];
titleAr = json['titleAr'];
questionId = json['questionId'];
sequence = json['sequence'];
image = json['image'];
isCorrectOption = json['isCorrectOption'];
optionStatus = QuestionsOptionStatus.unSelected;
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['titleEn'] = titleEn;
data['titleAr'] = titleAr;
data['questionId'] = questionId;
data['sequence'] = sequence;
data['image'] = image;
data['isCorrectOption'] = isCorrectOption;
return data;
}
}

@ -5,20 +5,15 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:mohem_flutter_app/api/chat/chat_api_client.dart'; import 'package:mohem_flutter_app/api/chat/chat_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.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/classes/utils.dart';
import 'package:mohem_flutter_app/exceptions/api_exception.dart';
import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart';
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as login;
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart';
import 'package:mohem_flutter_app/widgets/image_picker.dart'; import 'package:mohem_flutter_app/widgets/image_picker.dart';
import 'package:signalr_netcore/signalr_client.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
@ -92,12 +87,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
if (isNewChat) userChatHistory = []; if (isNewChat) userChatHistory = [];
if (!loadMore) paginationVal = 0; if (!loadMore) paginationVal = 0;
isChatScreenActive = true; isChatScreenActive = true;
Response response = await ChatApiClient().getSingleUserChatHistory( Response response = await ChatApiClient().getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
senderUID: senderUID,
receiverUID: receiverUID,
loadMore: loadMore,
paginationVal: paginationVal,
);
if (response.statusCode == 204) { if (response.statusCode == 204) {
if (isNewChat) { if (isNewChat) {
userChatHistory = []; userChatHistory = [];
@ -107,25 +97,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
} else { } else {
if (loadMore) { if (loadMore) {
List<SingleUserChatModel> temp = getSingleUserChatModel( List<SingleUserChatModel> temp = getSingleUserChatModel(response.body).reversed.toList();
response.body, userChatHistory.addAll(temp);
).reversed.toList();
userChatHistory.addAll(
temp,
);
} else { } else {
userChatHistory = getSingleUserChatModel( userChatHistory = getSingleUserChatModel(response.body).reversed.toList();
response.body,
).reversed.toList();
} }
} }
isLoading = false; isLoading = false;
notifyListeners(); notifyListeners();
markRead( markRead(userChatHistory, receiverUID);
userChatHistory,
receiverUID,
);
generateConvId(); generateConvId();
} }
@ -139,13 +119,10 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
for (SingleUserChatModel element in data!) { for (SingleUserChatModel element in data!) {
if (element.isSeen != null) { if (element.isSeen != null) {
if (!element.isSeen!) { if (!element.isSeen!) {
print("Found Un Read");
logger.d(jsonEncode(element));
dynamic data = [ dynamic data = [
{ {"userChatHistoryId": element.userChatHistoryId, "TargetUserId": element.targetUserId, "isDelivered": true, "isSeen": true}
"userChatHistoryId": element.userChatHistoryId,
"TargetUserId": element.targetUserId,
"isDelivered": true,
"isSeen": true,
}
]; ];
updateUserChatHistoryStatusAsync(data); updateUserChatHistoryStatusAsync(data);
} }
@ -161,17 +138,22 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
} }
void updateUserChatHistoryStatusAsync(List data) { void updateUserChatHistoryStatusAsync(List data) {
hubConnection.invoke( try {
"UpdateUserChatHistoryStatusAsync", hubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]);
args: [data], } catch (e) {
); throw e;
}
} }
List<SingleUserChatModel> getSingleUserChatModel(String str) => List<SingleUserChatModel>.from( void updateUserChatHistoryOnMsg(List data) {
json.decode(str).map( try {
(x) => SingleUserChatModel.fromJson(x), hubConnection.invoke("UpdateUserChatHistoryStatusAsync", args: [data]);
), } catch (e) {
); throw e;
}
}
List<SingleUserChatModel> getSingleUserChatModel(String str) => List<SingleUserChatModel>.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x)));
Future<dynamic> uploadAttachments(String userId, File file) async { Future<dynamic> uploadAttachments(String userId, File file) async {
dynamic result; dynamic result;
@ -191,15 +173,15 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
void updateUserChatStatus(List<Object?>? args) { void updateUserChatStatus(List<Object?>? args) {
dynamic items = args!.toList(); dynamic items = args!.toList();
for (dynamic cItem in items[0]) { for (var cItem in items[0]) {
for (SingleUserChatModel chat in userChatHistory) { for (SingleUserChatModel chat in userChatHistory) {
if (chat.userChatHistoryId.toString() == cItem["userChatHistoryId"].toString()) { if (cItem["contantNo"].toString() == chat.contantNo.toString()) {
chat.isSeen = cItem["isSeen"]; chat.isSeen = cItem["isSeen"];
chat.isDelivered = cItem["isDelivered"]; chat.isDelivered = cItem["isDelivered"];
notifyListeners();
} }
} }
} }
notifyListeners();
} }
void onChatSeen(List<Object?>? args) { void onChatSeen(List<Object?>? args) {
@ -308,14 +290,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
userChatHistory.insert(0, data.first); userChatHistory.insert(0, data.first);
var list = [ var list = [
{ {"userChatHistoryId": data.first.userChatHistoryId, "TargetUserId": temp.first.targetUserId, "isDelivered": true, "isSeen": isChatScreenActive ? true : false}
"userChatHistoryId": data.first.userChatHistoryId,
"TargetUserId": data.first.targetUserId,
"isDelivered": true,
"isSeen": isChatScreenActive ? true : false,
}
]; ];
updateUserChatHistoryStatusAsync(list); updateUserChatHistoryOnMsg(list);
notifyListeners(); notifyListeners();
} }
@ -412,13 +389,14 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
Uint8List? image, Uint8List? image,
required bool isImageLoaded}) async { required bool isImageLoaded}) async {
Uuid uuid = const Uuid(); Uuid uuid = const Uuid();
var contentNo = uuid.v4();
var msg = message.text; var msg = message.text;
SingleUserChatModel data = SingleUserChatModel( SingleUserChatModel data = SingleUserChatModel(
chatEventId: chatEventId, chatEventId: chatEventId,
chatSource: 1, chatSource: 1,
contant: msg, contant: msg,
contantNo: uuid.v4(), contantNo: contentNo,
conversationId: chatCID, conversationId: chatCID,
createdDate: DateTime.now(), createdDate: DateTime.now(),
currentUserId: AppState().chatDetails!.response!.id, currentUserId: AppState().chatDetails!.response!.id,
@ -438,8 +416,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
) )
: null, : null,
image: image, image: image,
isImageLoaded: isImageLoaded, isImageLoaded: isImageLoaded);
);
userChatHistory.insert(0, data); userChatHistory.insert(0, data);
isFileSelected = false; isFileSelected = false;
isMsgReply = false; isMsgReply = false;
@ -448,7 +425,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
notifyListeners(); notifyListeners();
String chatData = String chatData =
'{"contant":"$msg","contantNo":"${uuid.v4()}","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}'; '{"contant":"$msg","contantNo":"$contentNo","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}';
await hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]); await hubConnection.invoke("AddChatUserAsync", args: <Object>[json.decode(chatData)]);
} }
@ -456,11 +433,7 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId);
if (contain.isEmpty) { if (contain.isEmpty) {
searchedChats!.add( searchedChats!.add(
ChatUser( ChatUser(id: targetUserId, userName: targetUserName, unreadMessageCount: 0),
id: targetUserId,
userName: targetUserName,
unreadMessageCount: 0
),
); );
notifyListeners(); notifyListeners();
} }
@ -693,17 +666,21 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
// } // }
void msgScroll() { void msgScroll() {
scrollController.animateTo( // scrollController.animateTo(
scrollController.position.minScrollExtent - 100, // // index: 150,
duration: const Duration(milliseconds: 500), // duration: Duration(seconds: 2),
curve: Curves.easeIn, // curve: Curves.easeInOutCubic);
); // scrollController.animateTo(
// scrollController.position.minScrollExtent - 100,
// duration: const Duration(milliseconds: 500),
// curve: Curves.easeIn,
// );
} }
// Future<void> getDownLoadFile(String fileName) async { // Future<void> getDownLoadFile(String fileName) async {
// var data = await ChatApiClient().downloadURL(fileName: "data"); // var data = await ChatApiClient().downloadURL(fileName: "data");
// Image.memory(data); // Image.memory(data);
// } // }
// void getUserChatHistoryNotDeliveredAsync({required int userId}) async { // void getUserChatHistoryNotDeliveredAsync({required int userId}) async {
// try { // try {
@ -713,12 +690,4 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
// } // }
// } // }
} }

@ -74,23 +74,41 @@ class ChatBubble extends StatelessWidget {
if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 || if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 ||
cItem.userChatReplyResponse!.fileTypeId == 3 || cItem.userChatReplyResponse!.fileTypeId == 3 ||
cItem.userChatReplyResponse!.fileTypeId == 4) cItem.userChatReplyResponse!.fileTypeId == 4)
// Container(
// padding: EdgeInsets.all(0), // Border width
// decoration: BoxDecoration(color: Colors.red, borderRadius: const BorderRadius.all(Radius.circular(8))),
// child: ClipRRect(
// borderRadius: const BorderRadius.all(
// Radius.circular(8),
// ),
// child: SizedBox.fromSize(
// size: Size.fromRadius(8), // Image radius
// child: showImage(
// isReplyPreview: true,
// fileName: cItem.userChatReplyResponse!.contant!,
// fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg"),
// ),
// ),
// ),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(8.0),
8, child: SizedBox(
), height: 32,
width: 32,
child: showImage( child: showImage(
isReplyPreview: true, isReplyPreview: true,
fileName: cItem.userChatReplyResponse!.contant!, fileName: cItem.userChatReplyResponse!.contant!,
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg") fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg")
.paddingOnly(left: 10, right: 10, bottom: 16, top: 16), .paddingOnly(left: 10, right: 10, bottom: 16, top: 16),
) ),
),
], ],
), ),
), ),
).paddingOnly(right: 5, bottom: 7), ).paddingOnly(right: 5, bottom: 7),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription).paddingOnly(right: 5).onPress(() { showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription).paddingOnly(right: 5).onPress(() {
showDialog(context: context, builder: (index) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!)); showDialog(context: context, builder: (BuildContext context) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!));
}), }),
cItem.contant!.toText12(), cItem.contant!.toText12(),
Align( Align(
@ -110,7 +128,7 @@ class ChatBubble extends StatelessWidget {
).paddingOnly(top: 11, left: 13, right: 7, bottom: 5).objectContainerView(disablePadding: true).paddingOnly(left: MediaQuery.of(context).size.width * 0.3); ).paddingOnly(top: 11, left: 13, right: 7, bottom: 5).objectContainerView(disablePadding: true).paddingOnly(left: MediaQuery.of(context).size.width * 0.3);
} }
Widget receiptUser(context) { Widget receiptUser(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.only(top: 11, left: 13, right: 7, bottom: 5), padding: const EdgeInsets.only(top: 11, left: 13, right: 7, bottom: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -153,27 +171,27 @@ class ChatBubble extends StatelessWidget {
cItem.userChatReplyResponse!.fileTypeId == 4) cItem.userChatReplyResponse!.fileTypeId == 4)
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(8.0),
child: SizedBox(
height: 32,
width: 32,
child: showImage( child: showImage(
isReplyPreview: true, isReplyPreview: true,
fileName: cItem.userChatReplyResponse!.contant!, fileName: cItem.userChatReplyResponse!.contant!,
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg") fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg")),
.paddingOnly(left: 10, right: 10, bottom: 16, top: 16), ).paddingOnly(left: 10, right: 10, bottom: 16, top: 16)
)
], ],
), ),
), ),
).paddingOnly(right: 5, bottom: 7), ).paddingOnly(right: 5, bottom: 7),
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3) if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription ?? "image/jpg").paddingOnly(right: 5).onPress(() { showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription ?? "image/jpg").paddingOnly(right: 5).onPress(() {
showDialog(context: context, builder: (index) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!)); showDialog(context: context, builder: (BuildContext context) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!));
}) })
else else
(cItem.contant! ?? "").toText12(color: Colors.white), (cItem.contant! ?? "").toText12(color: Colors.white),
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: dateTime.toText10( child: dateTime.toText10(color: Colors.white.withOpacity(.71)),
color: Colors.white.withOpacity(.71),
),
), ),
], ],
), ),
@ -196,7 +214,6 @@ class ChatBubble extends StatelessWidget {
if (snapshot.data == null) { if (snapshot.data == null) {
return SizedBox(); return SizedBox();
} else { } else {
//data = image;
cItem.image = snapshot.data; cItem.image = snapshot.data;
cItem.isImageLoaded = true; cItem.isImageLoaded = true;
return Image.memory( return Image.memory(

@ -221,14 +221,11 @@ class _ChatDetailScreenState extends State<ChatDetailScreen> {
Widget showReplyImage(List<SingleUserChatModel> data) { Widget showReplyImage(List<SingleUserChatModel> data) {
if (data.first.isImageLoaded! && data.first.image != null) { if (data.first.isImageLoaded! && data.first.image != null) {
return ClipRRect( return Container(
borderRadius: BorderRadius.circular(10.0),
child: Image.memory(
data.first.image!,
height: 43,
width: 43, width: 43,
fit: BoxFit.cover, height: 43,
), decoration: BoxDecoration(
border: Border.all(color: MyColors.darkGrey3BColor, width: 1), borderRadius: BorderRadius.circular(10.0), image: DecorationImage(image: MemoryImage(data.first.image!), fit: BoxFit.cover)),
); );
} else { } else {
return const SizedBox(); return const SizedBox();

@ -25,7 +25,8 @@ class ChatImagePreviewScreen extends StatelessWidget {
Image.memory( Image.memory(
img, img,
fit: BoxFit.cover, fit: BoxFit.cover,
height: 400,
width: double.infinity,
).paddingAll(10), ).paddingAll(10),
const Positioned( const Positioned(
right: 0, right: 0,

@ -1,3 +1,4 @@
import 'dart:convert';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -210,6 +211,9 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
), ),
), ),
onPressed: () async { onPressed: () async {
// String plainText = 'Muhamad.Alam@cloudsolutions.com.sa';
// String key = "PeShVmYp";
// passEncrypt(plainText, "PeShVmYp");
showMyBottomSheet( showMyBottomSheet(
context, context,
callBackFunc: () {}, callBackFunc: () {},
@ -237,4 +241,127 @@ class _ChatHomeScreenState extends State<ChatHomeScreen> {
), ),
); );
} }
//
// void passEncrypt(String text, String pass) async {
// var salt = randomUint8List(8);
// var keyndIV = deriveKeyAndIV(pass, salt);
// var key = encrypt.Key(keyndIV.item1);
// var iv = encrypt.IV(keyndIV.item2);
// var encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
// var encrypted = encrypter.encrypt(text, iv: iv);
// Uint8List encryptedBytesWithSalt = Uint8List.fromList(createUint8ListFromString("Salted__") + salt + encrypted.bytes);
// var resulttt = base64.encode(encryptedBytesWithSalt);
// print("Enc : " + resulttt);
//
// decryptAESCryptoJS(resulttt, pass);
// }
//
// Uint8List randomUint8List(int length) {
// assert(length > 0);
// var random = Random();
// var ret = Uint8List(length);
// for (var i = 0; i < length; i++) {
// ret[i] = random.nextInt(256);
// }
// return ret;
// }
//
// void decryptAESCryptoJS(String encrypted, String passphrase) {
// try {
// Uint8List encryptedBytesWithSalt = base64.decode(encrypted);
// Uint8List encryptedBytes = encryptedBytesWithSalt.sublist(16, encryptedBytesWithSalt.length);
// var salt = encryptedBytesWithSalt.sublist(8, 16);
// var keyndIV = deriveKeyAndIV(passphrase, salt);
// var key = encrypt.Key(keyndIV.item1);
// var iv = encrypt.IV(keyndIV.item2);
// var encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: "PKCS7"));
// var decrypted = encrypter.decrypt64(base64.encode(encryptedBytes), iv: iv);
// print("Dec : " + decrypted);
// // return decrypted;
// } catch (error) {
// throw error;
// }
// }
void enc(String input) {
var ekey = "PeShVmYp";
var eIV = "j70IbWYn";
List<int> eByt = utf8.encode(ekey);
List<int> eIvByt = utf8.encode(eIV);
List<int> iByt = utf8.encode(input);
}
// ///Accepts encrypted data and decrypt it. Returns plain text
// String decryptWithAES(String key, Encrypted encryptedData) {
// var cipherKey = encrypt.Key.fromUtf8(key);
// var encryptService = Encrypter(AES(cipherKey, mode: AESMode.cbc,padding: null));
// var initVector = IV.fromUtf8(key.substring(0, 16));
// return encryptService.decrypt(encryptedData, iv: initVector);
// }
//
// ///Encrypts the given plainText using the key. Returns encrypted data
// Encrypted encryptWithAES(String key, String plainText) {
// var cipherKey = encrypt.Key.fromUtf8(key);
// var encryptService = Encrypter(AES(cipherKey, mode: AESMode.cbc,padding: null));
// var initVector = IV.fromUtf8("j70IbWYn");
// Encrypted encryptedData = encryptService.encrypt(plainText, iv: initVector);
// print(encryptedData.base64);
// return encryptedData;
// }
//
// Tuple2<Uint8List, Uint8List> deriveKeyAndIV(String passphrase, Uint8List salt) {
// var password = createUint8ListFromString(passphrase);
// Uint8List concatenatedHashes = Uint8List(0);
// Uint8List currentHash = Uint8List(0);
// bool enoughBytesForKey = false;
// Uint8List preHash = Uint8List(0);
//
// while (!enoughBytesForKey) {
// int preHashLength = currentHash.length + password.length + salt.length;
// if (currentHash.length > 0)
// preHash = Uint8List.fromList(currentHash + password + salt);
// else
// preHash = Uint8List.fromList(password + salt);
//
// currentHash = preHash;
// concatenatedHashes = Uint8List.fromList(concatenatedHashes + currentHash);
// if (concatenatedHashes.length >= 48) enoughBytesForKey = true;
// }
//
// var keyBtyes = concatenatedHashes.sublist(0, 32);
// var ivBtyes = concatenatedHashes.sublist(32, 48);
// return new Tuple2(keyBtyes, ivBtyes);
// }
//
// Uint8List createUint8ListFromString(String s) {
// var ret = new Uint8List(s.length);
// for (var i = 0; i < s.length; i++) {
// ret[i] = s.codeUnitAt(i);
// }
// return ret;
// }
//
// Uint8List genRandomWithNonZero(int seedLength) {
// var random = Random.secure();
// const int randomMax = 245;
// Uint8List uint8list = Uint8List(seedLength);
// for (int i = 0; i < seedLength; i++) {
// uint8list[i] = random.nextInt(randomMax) + 1;
// }
// return uint8list;
// }
//
//
//
// void test(String text, String kk) {
// Uint8List key = Uint8List.fromList(utf8.encode(kk));
// PaddedBlockCipher cipher = exp.PaddedBlockCipherImpl(exp.PKCS7Padding(), exp.ECBBlockCipher(exp.AESEngine()));
// cipher.init(true, PaddedBlockCipherParameters<CipherParameters, CipherParameters>(KeyParameter(key), null));
// var byte = Uint8List.fromList(utf8.encode(text));
// var data = cipher.process(byte);
// print(data);
// }
} }

@ -1,28 +1,25 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_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/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/main.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart';
import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart'; import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart';
import 'package:mohem_flutter_app/ui/landing/widget/menus_widget.dart'; import 'package:mohem_flutter_app/ui/landing/widget/menus_widget.dart';
import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart'; import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_banner.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/marathon_banner.dart';
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
import 'package:mohem_flutter_app/widgets/mark_attendance_widget.dart'; import 'package:mohem_flutter_app/widgets/mark_attendance_widget.dart';
@ -45,6 +42,7 @@ class DashboardScreen extends StatefulWidget {
class _DashboardScreenState extends State<DashboardScreen> { class _DashboardScreenState extends State<DashboardScreen> {
late DashboardProviderModel data; late DashboardProviderModel data;
late MarathonProvider marathonProvider;
final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey(); final GlobalKey<ScaffoldState> _scaffoldState = GlobalKey();
final RefreshController _refreshController = RefreshController(initialRefresh: false); final RefreshController _refreshController = RefreshController(initialRefresh: false);
@ -56,6 +54,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
super.initState(); super.initState();
scheduleMicrotask(() { scheduleMicrotask(() {
data = Provider.of<DashboardProviderModel>(context, listen: false); data = Provider.of<DashboardProviderModel>(context, listen: false);
marathonProvider = Provider.of<MarathonProvider>(context, listen: false);
_bHubCon(); _bHubCon();
_onRefresh(); _onRefresh();
}); });
@ -92,6 +91,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
data.fetchLeaveTicketBalance(context, DateTime.now()); data.fetchLeaveTicketBalance(context, DateTime.now());
data.fetchMenuEntries(); data.fetchMenuEntries();
data.getCategoryOffersListAPI(context); data.getCategoryOffersListAPI(context);
marathonProvider.getMarathonDetailsFromApi();
data.fetchChatCounts(); data.fetchChatCounts();
_refreshController.refreshCompleted(); _refreshController.refreshCompleted();
} }
@ -237,12 +237,15 @@ class _DashboardScreenState extends State<DashboardScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
9.height, 9.height,
CountdownTimer( Directionality(
textDirection: ui.TextDirection.ltr,
child: CountdownTimer(
endTime: model.endTime, endTime: model.endTime,
onEnd: null, onEnd: null,
endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
), ),
),
LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
9.height, 9.height,
ClipRRect( ClipRRect(
@ -318,7 +321,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
], ],
).paddingOnly(left: 21, right: 21, top: 7), ).paddingOnly(left: 21, right: 21, top: 7),
const MarathonBanner().paddingAll(21), context.watch<MarathonProvider>().isLoading ? MarathonBannerShimmer().paddingAll(20) : MarathonBanner().paddingAll(20),
ServicesWidget(), ServicesWidget(),
// 8.height, // 8.height,
Container( Container(
@ -364,7 +367,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
], ],
).paddingOnly(left: 21, right: 21), ).paddingOnly(left: 21, right: 21),
Consumer<DashboardProviderModel>( Consumer<DashboardProviderModel>(
builder: (context, model, child) { builder: (BuildContext context, DashboardProviderModel model, Widget? child) {
return SizedBox( return SizedBox(
height: 103 + 33, height: 103 + 33,
child: ListView.separated( child: ListView.separated(

@ -99,10 +99,13 @@ class _TodayAttendanceScreenState extends State<TodayAttendanceScreen2> {
child: CountdownTimer( child: CountdownTimer(
endTime: model.endTime, endTime: model.endTime,
widgetBuilder: (context, v) { widgetBuilder: (context, v) {
return AutoSizeText( return Directionality(
textDirection: TextDirection.ltr,
child: AutoSizeText(
getValue(v?.hours) + " : " + getValue(v?.min) + " : " + getValue(v?.sec), getValue(v?.hours) + " : " + getValue(v?.min) + " : " + getValue(v?.sec),
maxLines: 1, maxLines: 1,
style: const TextStyle(color: Colors.white, fontSize: 42, letterSpacing: -1.92, fontWeight: FontWeight.bold, height: 1), style: const TextStyle(color: Colors.white, fontSize: 42, letterSpacing: -1.92, fontWeight: FontWeight.bold, height: 1),
),
); );
}, },
onEnd: null, onEnd: null,
@ -116,7 +119,7 @@ class _TodayAttendanceScreenState extends State<TodayAttendanceScreen2> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
LocaleKeys.shiftTime.tr().tr().toTextAuto(color: MyColors.greyACColor, fontSize: 18, maxLine: 1).paddingOnly(left: 21,right: 21), LocaleKeys.shiftTime.tr().tr().toTextAuto(color: MyColors.greyACColor, fontSize: 18, maxLine: 1).paddingOnly(left: 21, right: 21),
(model.attendanceTracking!.pShtName ?? "00:00:00").toString().toTextAuto(color: Colors.white, isBold: true, fontSize: 26, maxLine: 1), (model.attendanceTracking!.pShtName ?? "00:00:00").toString().toTextAuto(color: Colors.white, isBold: true, fontSize: 26, maxLine: 1),
], ],
), ),

@ -143,8 +143,8 @@ class _LoginScreenState extends State<LoginScreen> {
isAppOpenBySystem = (ModalRoute.of(context)!.settings.arguments ?? true) as bool; isAppOpenBySystem = (ModalRoute.of(context)!.settings.arguments ?? true) as bool;
if (!kReleaseMode) { if (!kReleaseMode) {
// username.text = "15444"; // Maha User // username.text = "15444"; // Maha User
// username.text = "15153"; // Tamer User username.text = "15153"; // Tamer User
// password.text = "Abcd@12345"; password.text = "Abcd@1234";
// username.text = "206535"; // Hashim User // username.text = "206535"; // Hashim User
// password.text = "Namira786"; // password.text = "Namira786";

@ -1,9 +1,12 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart'; import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/date_uitl.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.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/lottie_consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
@ -15,8 +18,6 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
final int dummyEndTime = DateTime.now().millisecondsSinceEpoch + 1000 * 30;
class MarathonIntroScreen extends StatelessWidget { class MarathonIntroScreen extends StatelessWidget {
const MarathonIntroScreen({Key? key}) : super(key: key); const MarathonIntroScreen({Key? key}) : super(key: key);
@ -32,7 +33,7 @@ class MarathonIntroScreen extends StatelessWidget {
children: <Widget>[ children: <Widget>[
MarathonDetailsCard(provider: provider), MarathonDetailsCard(provider: provider),
10.height, 10.height,
MarathonTimerCard(provider: provider, timeToMarathon: dummyEndTime), MarathonTimerCard(provider: provider, timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch,),
], ],
).expanded, ).expanded,
1.divider, 1.divider,
@ -58,38 +59,56 @@ class MarathonDetailsCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
LocaleKeys.contestTopicAbout.tr().toText16(color: MyColors.grey57Color), Column(
"Saudi Arabia".toText20(color: MyColors.textMixColor), crossAxisAlignment: CrossAxisAlignment.start,
"Nam suscipit turpis in pharetra euismsdef. Duis rutrum at nulla id aliquam".toText14(color: MyColors.grey57Color, weight: FontWeight.w500), children: <Widget>[
if (provider.itsMarathonTime) ...<Widget>[ LocaleKeys.contestTopicAbout.tr().toText16(color: MyColors.grey77Color),
5.height, "${AppState().isArabic(context) ? provider.marathonDetailModel.titleAr : provider.marathonDetailModel.titleEn}".toText20(color: MyColors.textMixColor, isBold: true),
Row( Row(
children: <Widget>[ children: <Widget>[
LocaleKeys.prize.tr().toText16(color: MyColors.grey57Color), Flexible(
" LED 55\" Android TV".toText16(color: MyColors.greenColor, isBold: true), child: "${AppState().isArabic(context) ? provider.marathonDetailModel.descAr : provider.marathonDetailModel.descEn}".toText14(color: MyColors.grey77Color),
)
], ],
), ),
if (provider.itsMarathonTime && provider.marathonDetailModel.sponsors != null) ...<Widget>[
5.height,
provider.marathonDetailModel.sponsors?.first.sponsorPrizes != null
? Row(
children: <Widget>[
"${LocaleKeys.prize.tr()} ".toText16(color: MyColors.grey77Color, isBold: true),
"${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr : provider.marathonDetailModel.sponsors?.first.sponsorPrizes?.first.marathonPrizeAr}"
.toText16(color: MyColors.greenColor, isBold: true),
],
)
: const SizedBox(),
Row( Row(
children: <Widget>[ children: <Widget>[
LocaleKeys.sponsoredBy.tr().toText16(color: MyColors.grey57Color), "${LocaleKeys.sponsoredBy.tr()} ".toText16(color: MyColors.grey77Color),
" Extra".toText16(color: MyColors.darkTextColor), "${AppState().isArabic(context) ? provider.marathonDetailModel.sponsors?.first.nameAr : provider.marathonDetailModel.sponsors?.first.nameEn}"
.toText16(color: MyColors.darkTextColor, isBold: true),
], ],
), ),
10.height, 10.height,
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Image.asset( Image.network(
"assets/images/logos/main_mohemm_logo.png", provider.marathonDetailModel.sponsors!.first.image!,
height: 40, height: 40,
fit: BoxFit.fill,
width: 150, width: 150,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
return const Center();
},
) )
], ],
), ),
] ]
], ],
), ),
],
),
); );
} }
} }
@ -114,14 +133,14 @@ class MarathonTimerCard extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Row( Row(
children: <Widget>[ children: <Widget>[
LocaleKeys.gameDate.tr().toText16(color: MyColors.grey57Color), "${LocaleKeys.gameDate.tr()} ".toText16(color: MyColors.grey77Color),
" 10 Oct, 2022".toText16(color: MyColors.darkTextColor), DateUtil.getMonthDayYearDateFormatted(DateTime.parse(provider.marathonDetailModel.startTime!)).toText16(color: MyColors.darkTextColor, isBold: true),
], ],
), ),
Row( Row(
children: <Widget>[ children: <Widget>[
LocaleKeys.gameTime.tr().toText16(color: MyColors.grey57Color), "${LocaleKeys.gameTime.tr()} ".toText16(color: MyColors.grey77Color),
" 3:00 pm".toText16(color: MyColors.darkTextColor), DateUtil.formatDateToTimeLang(DateTime.parse(provider.marathonDetailModel.startTime!), AppState().isArabic(context)).toText16(color: MyColors.darkTextColor, isBold: true),
], ],
), ),
Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200), Lottie.asset(MyLottieConsts.hourGlassLottie, height: 200),
@ -167,10 +186,21 @@ class MarathonFooter extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return provider.itsMarathonTime return !provider.itsMarathonTime
? DefaultButton( ? DefaultButton(
LocaleKeys.joinMarathon.tr(), LocaleKeys.joinMarathon.tr(),
() => Navigator.pushNamed(context, AppRoutes.marathonScreen), () async {
Utils.showLoading(context);
try {
provider.resetValues();
await provider.connectSignalrAndJoinMarathon(context);
} catch (e, s) {
Utils.confirmDialog(context, e.toString());
print(s);
}
Utils.hideLoading(context);
Navigator.pushNamed(context, AppRoutes.marathonScreen);
},
).insideContainer ).insideContainer
: Container( : Container(
color: Colors.white, color: Colors.white,
@ -180,7 +210,9 @@ class MarathonFooter extends StatelessWidget {
buildNoteForDemo(), buildNoteForDemo(),
DefaultButton( DefaultButton(
LocaleKeys.joinDemoMarathon.tr(), LocaleKeys.joinDemoMarathon.tr(),
() {}, () {
provider.connectSignalrAndJoinMarathon(context);
},
color: MyColors.yellowColorII, color: MyColors.yellowColorII,
).insideContainer, ).insideContainer,
], ],

@ -3,12 +3,64 @@ import 'dart:async';
import 'package:appinio_swiper/appinio_swiper.dart'; import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/api/marathon/marathon_api_client.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:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class MarathonProvider extends ChangeNotifier { class MarathonProvider extends ChangeNotifier {
final AppinioSwiperController swiperController = AppinioSwiperController(); final AppinioSwiperController swiperController = AppinioSwiperController();
MarathonDetailModel marathonDetailModel = MarathonDetailModel();
List<CardContent> cardContentList = <CardContent>[];
QuestionModel currentQuestion = QuestionModel();
QuestionCardStatus questionCardStatus = QuestionCardStatus.question;
int? selectedOptionIndex;
int currentQuestionTime = 0;
void onNewQuestionReceived(QuestionModel newQuestion) {
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) {
questionCardStatus = status;
notifyListeners();
}
bool _isLoading = false;
bool get isLoading => _isLoading;
set isLoading(bool value) {
_isLoading = value;
notifyListeners();
}
bool _itsMarathonTime = false; bool _itsMarathonTime = false;
bool get itsMarathonTime => _itsMarathonTime; bool get itsMarathonTime => _itsMarathonTime;
@ -27,14 +79,7 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void swipeCardLeft() { int _currentQuestionNumber = 0;
currentQuestionNumber = currentQuestionNumber + 1;
swiperController.swipeLeft();
notifyListeners();
}
int _currentQuestionNumber = 1;
final int totalQuestions = 10;
int get currentQuestionNumber => _currentQuestionNumber; int get currentQuestionNumber => _currentQuestionNumber;
@ -43,44 +88,73 @@ class MarathonProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void resetAll() { int _totalMarathoners = 23;
isSelectedOptions[0] = false;
isSelectedOptions[1] = false; int get totalMarathoners => _totalMarathoners;
isSelectedOptions[2] = false;
isSelectedOptions[3] = false; set totalMarathoners(int value) {
_totalMarathoners = value;
notifyListeners();
}
void swipeCardLeft() {
swiperController.swipeLeft();
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);
} else {
updateQuestionCardStatus(QuestionCardStatus.wrongAnswer);
}
} else {
updateQuestionCardStatus(QuestionCardStatus.skippedAnswer);
}
} }
Timer timerU = Timer.periodic(const Duration(seconds: 1), (Timer timer) {}); Timer timerU = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});
int start = 8;
void startTimer(BuildContext context) { void startTimer(BuildContext context) {
start = 8;
const Duration oneSec = Duration(seconds: 1); const Duration oneSec = Duration(seconds: 1);
timerU = Timer.periodic( timerU = Timer.periodic(
oneSec, oneSec,
(Timer timer) async { (Timer timer) async {
if (start == 0) { if (currentQuestionTime == 2) {
if (currentQuestionNumber == totalQuestions) { getCorrectAnswerAndUpdateAnswerColor();
timer.cancel();
cancelTimer();
isMarathonCompleted = true;
await Future<dynamic>.delayed(const Duration(seconds: 3)).whenComplete(
() => Navigator.pushReplacementNamed(
context,
AppRoutes.marathonWinnerSelection,
),
);
resetValues();
return;
} }
resetAll(); if (currentQuestionTime == 0) {
timer.cancel(); updateCardStatusToAnswer();
cancelTimer(); // if (currentQuestionNumber == 9) {
swipeCardLeft(); // timer.cancel();
// cancelTimer();
// isMarathonCompleted = true;
// await Future<dynamic>.delayed(const Duration(seconds: 2)).whenComplete(
// () => Navigator.pushReplacementNamed(context, AppRoutes.marathonWinnerSelection),
// );
//
// resetValues();
//
// return;
// }
// timer.cancel();
} else { } else {
start--; currentQuestionTime--;
} }
notifyListeners(); notifyListeners();
}, },
@ -88,9 +162,12 @@ class MarathonProvider extends ChangeNotifier {
} }
void resetValues() { void resetValues() {
_currentQuestionNumber = 0;
cardContentList.clear();
timerU.cancel(); timerU.cancel();
_isMarathonCompleted = false; _isMarathonCompleted = false;
_currentQuestionNumber = 1; currentQuestionTime = 0;
currentQuestion = QuestionModel();
notifyListeners(); notifyListeners();
} }
@ -98,4 +175,18 @@ class MarathonProvider extends ChangeNotifier {
timerU.cancel(); timerU.cancel();
notifyListeners(); notifyListeners();
} }
Future<void> getMarathonDetailsFromApi() async {
isLoading = true;
notifyListeners();
await MarathonApiClient().getMarathonToken().whenComplete(() async {
marathonDetailModel = await MarathonApiClient().getMarathonDetails();
isLoading = false;
notifyListeners();
});
}
Future<void> connectSignalrAndJoinMarathon(BuildContext context) async {
await MarathonApiClient().buildHubConnection(context);
}
} }

@ -6,7 +6,6 @@ import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.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/lottie_consts.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
@ -14,6 +13,7 @@ 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/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/custom_status_widget.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/custom_status_widget.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card_builder.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -23,48 +23,52 @@ class MarathonScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>(); MarathonProvider provider = context.watch<MarathonProvider>();
return Scaffold( return WillPopScope(
child: Scaffold(
appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()), appBar: AppBarWidget(context, title: LocaleKeys.brainMarathon.tr()),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
20.height, 20.height,
MarathonProgressContainer(provider: provider).paddingOnly(left: 21, right: 21), MarathonProgressContainer(provider: provider).paddingOnly(left: 21, right: 21),
if (provider.isMarathonCompleted) QuestionCardBuilder(
InkWell( onQuestion: (BuildContext context) => QuestionCard(provider: provider),
onTap: () { onCompleted: (BuildContext context) => CustomStatusWidget(
Navigator.pushReplacementNamed( asset: Lottie.asset(MyLottieConsts.allQuestions, height: 200),
context, title: LocaleKeys.congrats.tr().toText22(color: MyColors.greenColor),
AppRoutes.marathonWinnerSelection, subTitle: LocaleKeys.allQuestionsCorrect.toText18(color: MyColors.darkTextColor),
); ),
}, onCorrectAnswer: (BuildContext context) => CustomStatusWidget(
child: CustomStatusWidget( asset: Lottie.asset(MyLottieConsts.allQuestions, height: 200),
asset: Lottie.asset( title: LocaleKeys.congrats.tr().toText22(color: MyColors.greenColor),
MyLottieConsts.allQuestions, subTitle: LocaleKeys.yourAnswerCorrect.toText18(color: MyColors.darkTextColor),
height: 200,
), ),
title: Text( onWinner: (BuildContext context) => QuestionCard(provider: provider),
LocaleKeys.congrats.tr(), onWrongAnswer: (BuildContext context) => CustomStatusWidget(
style: const TextStyle( asset: Image.asset(MyLottieConsts.wrongAnswerGif, height: 200),
height: 23 / 24, title: const Text(""),
color: MyColors.greenColor, subTitle: LocaleKeys.wrongAnswer.tr().toText18(color: MyColors.darkTextColor),
fontSize: 27,
letterSpacing: -1,
fontWeight: FontWeight.w600,
), ),
onSkippedAnswer: (BuildContext context) => CustomStatusWidget(
asset: Image.asset(MyLottieConsts.wrongAnswerGif, height: 200),
title: const Text(""),
subTitle: LocaleKeys.youMissedTheQuestion.tr().toText18(color: MyColors.darkTextColor),
), ),
subTitle: Text( questionCardStatus: provider.questionCardStatus,
LocaleKeys.allQuestionsCorrect.tr(), onFindingWinner: (BuildContext context) => CustomStatusWidget(
textAlign: TextAlign.center, asset: Lottie.asset(MyLottieConsts.winnerLottie, height: 168),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -1.08), title: LocaleKeys.fingersCrossed.tr().toText22(color: MyColors.greenColor),
subTitle: LocaleKeys.winnerSelectedRandomly.tr().toText18(color: MyColors.darkTextColor),
), ),
).paddingOnly(top: 12, left: 21, right: 21), ).paddingOnly(top: 12, left: 21, right: 21),
)
else
QuestionCard(provider: provider).paddingOnly(top: 12, left: 21, right: 21),
], ],
), ),
), ),
),
onWillPop: () {
provider.resetValues();
return Future<bool>.value(true);
},
); );
} }
} }
@ -89,7 +93,6 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
@override @override
void dispose() { void dispose() {
widget.provider.cancelTimer();
super.dispose(); super.dispose();
} }
@ -108,10 +111,12 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
Container( Container(
decoration: BoxDecoration(color: MyColors.greenColor, borderRadius: BorderRadius.circular(5)), decoration: BoxDecoration(color: MyColors.greenColor, borderRadius: BorderRadius.circular(5)),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8), padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 8),
child: "${widget.provider.currentQuestionNumber.toString()} / ${widget.provider.totalQuestions.toString()} ${LocaleKeys.question.tr()}".toText12(color: MyColors.white), child: "${widget.provider.currentQuestionNumber.toString()} / ${widget.provider.marathonDetailModel.totalQuestions.toString()} ${LocaleKeys.question.tr()}"
.toText12(color: MyColors.white),
), ),
"23 ${LocaleKeys.marathoners.tr()}".toText14(), "${widget.provider.totalMarathoners} ${LocaleKeys.marathoners.tr()}".toText14(),
"00:${widget.provider.start < 10 ? "0${widget.provider.start}" : widget.provider.start}".toText18(), "00:${widget.provider.currentQuestionTime < 10 ? "0${widget.provider.currentQuestionTime}" : widget.provider.currentQuestionTime}"
.toText18(color: widget.provider.currentQuestionTime < 5 ? MyColors.redColor : MyColors.black),
], ],
), ),
12.height, 12.height,
@ -119,7 +124,7 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
8.height, 8.height,
Row( Row(
children: <Widget>[ children: <Widget>[
"${widget.provider.currentQuestionNumber * 10}% ${LocaleKeys.completed.tr()}".toText14(), "${((widget.provider.currentQuestionNumber / widget.provider.marathonDetailModel.totalQuestions!) * 100).toInt()}% ${LocaleKeys.completed.tr()}".toText14(),
], ],
), ),
], ],
@ -161,3 +166,33 @@ class _MarathonProgressContainerState extends State<MarathonProgressContainer> {
); );
} }
} }
// InkWell(
// onTap: () {
// Navigator.pushReplacementNamed(
// context,
// AppRoutes.marathonWinnerSelection,
// );
// },
// child: CustomStatusWidget(
// asset: Lottie.asset(
// MyLottieConsts.allQuestions,
// height: 200,
// ),
// title: Text(
// LocaleKeys.congrats.tr(),
// style: const TextStyle(
// height: 23 / 24,
// color: MyColors.greenColor,
// fontSize: 27,
// letterSpacing: -1,
// fontWeight: FontWeight.w600,
// ),
// ),
// subTitle: Text(
// LocaleKeys.allQuestionsCorrect.tr(),
// textAlign: TextAlign.center,
// style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -1.08),
// ),
// ).paddingOnly(top: 12, left: 21, right: 21),
// )

@ -108,14 +108,14 @@ class _QualifiersContainerState extends State<QualifiersContainer> {
@override @override
void initState() { void initState() {
scheduleMicrotask(() { scheduleMicrotask(() {
widget.provider.startTimer(context); // widget.provider.startTimer(context);
}); });
super.initState(); super.initState();
} }
@override @override
void dispose() { void dispose() {
widget.provider.cancelTimer(); // widget.provider.cancelTimer();
super.dispose(); super.dispose();
} }
@ -132,7 +132,7 @@ class _QualifiersContainerState extends State<QualifiersContainer> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor), LocaleKeys.winnerSelection.tr().toText21(color: MyColors.grey3AColor),
"00:${widget.provider.start < 10 ? "0${widget.provider.start}" : widget.provider.start}".toText18(color: MyColors.redColor), // "00:${widget.provider.start < 10 ? "0${widget.provider.start}" : widget.provider.start}".toText18(color: MyColors.redColor),
], ],
), ),
10.height, 10.height,

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui;
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -29,7 +30,7 @@ class BuildCountdownTimer extends StatelessWidget {
); );
final TextStyle styleDigitHome = const TextStyle( final TextStyle styleDigitHome = const TextStyle(
height: 23 / 27, height: 22 / 27,
color: MyColors.white, color: MyColors.white,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
letterSpacing: -1.44, letterSpacing: -1.44,
@ -53,7 +54,9 @@ class BuildCountdownTimer extends StatelessWidget {
); );
Widget buildEmptyWidget() { Widget buildEmptyWidget() {
return Row( return Directionality(
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -127,6 +130,7 @@ class BuildCountdownTimer extends StatelessWidget {
], ],
), ),
], ],
),
); );
} }
@ -149,7 +153,9 @@ class BuildCountdownTimer extends StatelessWidget {
return buildEmptyWidget(); return buildEmptyWidget();
} }
return Row( return Directionality(
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -251,6 +257,7 @@ class BuildCountdownTimer extends StatelessWidget {
], ],
), ),
], ],
),
); );
} }

@ -18,6 +18,7 @@ class CustomStatusWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: double.infinity, width: double.infinity,
height: 440,
decoration: MyDecorations.shadowDecoration, decoration: MyDecorations.shadowDecoration,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column( child: Column(

@ -11,7 +11,6 @@ import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_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/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_intro_screen.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart'; import 'package:mohem_flutter_app/ui/marathon/widgets/countdown_timer.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -22,7 +21,8 @@ class MarathonBanner extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MarathonProvider provider = context.read<MarathonProvider>(); MarathonProvider provider = context.read<MarathonProvider>();
return Container( return provider.marathonDetailModel.startTime != null
? Container(
decoration: MyDecorations.shadowDecoration, decoration: MyDecorations.shadowDecoration,
height: MediaQuery.of(context).size.height * 0.11, height: MediaQuery.of(context).size.height * 0.11,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
@ -39,7 +39,20 @@ class MarathonBanner extends StatelessWidget {
width: double.infinity, width: double.infinity,
), ),
), ),
Positioned( AppState().isArabic(context)
? Positioned(
right: -15,
top: -10,
child: Transform.rotate(
angle: 10,
child: Container(
width: 65,
height: 32,
color: MyColors.darkDigitColor,
),
),
)
: Positioned(
left: -20, left: -20,
top: -10, top: -10,
child: Transform.rotate( child: Transform.rotate(
@ -64,7 +77,7 @@ class MarathonBanner extends StatelessWidget {
), ),
), ),
Expanded( Expanded(
flex: 5, flex: AppState().isArabic(context) ? 4 : 5,
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
@ -89,7 +102,7 @@ class MarathonBanner extends StatelessWidget {
), ),
), ),
AutoSizeText( AutoSizeText(
"Saudi Arabia", AppState().isArabic(context) ? provider.marathonDetailModel.titleAr ?? "" : provider.marathonDetailModel.titleEn ?? "",
style: TextStyle( style: TextStyle(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
fontSize: 19, fontSize: 19,
@ -100,7 +113,7 @@ class MarathonBanner extends StatelessWidget {
), ),
3.height, 3.height,
BuildCountdownTimer( BuildCountdownTimer(
timeToMarathon: dummyEndTime, timeToMarathon: DateTime.parse(provider.marathonDetailModel.startTime!).millisecondsSinceEpoch,
provider: provider, provider: provider,
screenFlag: 0, screenFlag: 0,
), ),
@ -116,7 +129,29 @@ class MarathonBanner extends StatelessWidget {
], ],
), ),
), ),
Align( AppState().isArabic(context)
? Align(
alignment: Alignment.topRight,
child: SizedBox(
height: 20,
width: 35,
child: Transform.rotate(
angle: math.pi / 4.5,
child: Text(
LocaleKeys.brainMarathon.tr(),
textAlign: TextAlign.center,
maxLines: 2,
style: const TextStyle(
color: MyColors.white,
fontWeight: FontWeight.bold,
fontSize: 6,
height: 1.2,
),
),
),
),
).paddingOnly(top: 5)
: Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: SizedBox( child: SizedBox(
height: 20, height: 20,
@ -158,6 +193,7 @@ class MarathonBanner extends StatelessWidget {
).onPress( ).onPress(
() => Navigator.pushNamed(context, AppRoutes.marathonIntroScreen), () => Navigator.pushNamed(context, AppRoutes.marathonIntroScreen),
), ),
); )
: const SizedBox();
} }
} }

@ -28,8 +28,6 @@ class MarathonHeader extends StatelessWidget {
color: MyColors.black, color: MyColors.black,
constraints: const BoxConstraints(), constraints: const BoxConstraints(),
onPressed: () { onPressed: () {
Provider.of<MarathonProvider>(context, listen: false)
.resetValues();
Navigator.pop(context); Navigator.pop(context);
}, },
) )

@ -1,69 +1,41 @@
import 'package:appinio_swiper/appinio_swiper.dart'; import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/decorations_helper.dart'; import 'package:mohem_flutter_app/classes/decorations_helper.dart';
import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/classes/lottie_consts.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/models/marathon_question_model.dart'; import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
List<bool> isSelectedOptions = [ class QuestionCard extends StatelessWidget {
false,
false,
false,
false,
];
class QuestionCard extends StatefulWidget {
final MarathonProvider provider; final MarathonProvider provider;
const QuestionCard({Key? key, required this.provider}) : super(key: key); const QuestionCard({Key? key, required this.provider}) : super(key: key);
@override
State<QuestionCard> createState() => _QuestionCardState();
}
class _QuestionCardState extends State<QuestionCard> {
final List<CardContent> questionCards = <CardContent>[];
@override
void initState() {
_loadCards();
super.initState();
}
void _loadCards() {
for (DummyQuestionModel question in questions) {
questionCards.add(
CardContent(
question: question,
provider: widget.provider,
),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CupertinoPageScaffold( return CupertinoPageScaffold(
child: SizedBox( child: provider.cardContentList.isEmpty
? Lottie.asset(MyLottieConsts.hourGlassLottie, height: 250).paddingOnly(top: 50)
: SizedBox(
height: 440, height: 440,
width: double.infinity, width: double.infinity,
child: Consumer<MarathonProvider>( child: Consumer<MarathonProvider>(
builder: (BuildContext context, MarathonProvider provider, _) { builder: (BuildContext context, MarathonProvider provider, _) {
return AppinioSwiper( return AppinioSwiper(
duration: const Duration(milliseconds: 400),
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
isDisabled: true, isDisabled: true,
controller: provider.swiperController, controller: provider.swiperController,
unswipe: (int index, AppinioSwiperDirection direction) {}, unswipe: (int index, AppinioSwiperDirection direction) {},
cards: questionCards, onSwipe: (int index, AppinioSwiperDirection direction) {},
onSwipe: (int index, AppinioSwiperDirection direction) { cards: provider.cardContentList,
if (direction == AppinioSwiperDirection.left) {
provider.startTimer(context);
}
},
); );
}, },
), ),
@ -73,19 +45,26 @@ class _QuestionCardState extends State<QuestionCard> {
} }
class CardContent extends StatelessWidget { class CardContent extends StatelessWidget {
final DummyQuestionModel question; const CardContent({Key? key}) : super(key: key);
final MarathonProvider provider;
const CardContent({
Key? key,
required this.question,
required this.provider,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( MarathonProvider provider = context.watch<MarathonProvider>();
mainAxisSize: MainAxisSize.min, return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: CupertinoColors.white,
boxShadow: <BoxShadow>[
BoxShadow(
color: CupertinoColors.systemGrey.withOpacity(0.2),
spreadRadius: 3,
blurRadius: 7,
offset: const Offset(0, 3),
)
],
),
alignment: Alignment.center,
child: Column(
children: <Widget>[ children: <Widget>[
Container( Container(
height: 78, height: 78,
@ -105,56 +84,33 @@ class CardContent extends StatelessWidget {
topRight: Radius.circular(10), topRight: Radius.circular(10),
), ),
), ),
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 17), child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 13),
child: Text( child: Text(
"What is the capital of Saudi Arabia?", AppState().isArabic(context) ? provider.currentQuestion.titleAr ?? "" : provider.currentQuestion.titleEn ?? "",
style: TextStyle( style: const TextStyle(
color: MyColors.white, color: MyColors.white,
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
), ),
AnswerContent(question: question, provider: provider), ),
),
const AnswerContent(),
], ],
),
); );
} }
} }
class AnswerContent extends StatefulWidget { class AnswerContent extends StatelessWidget {
final DummyQuestionModel question; const AnswerContent({Key? key}) : super(key: key);
final MarathonProvider provider;
const AnswerContent({Key? key, required this.question, required this.provider}) : super(key: key);
@override
State<AnswerContent> createState() => _AnswerContentState();
}
class _AnswerContentState extends State<AnswerContent> {
void updateOption(int index, bool value) {
isSelectedOptions[0] = false;
isSelectedOptions[1] = false;
isSelectedOptions[2] = false;
isSelectedOptions[3] = false;
isSelectedOptions[index] = value;
setState(() {});
}
Decoration getContainerColor(int index) {
if (!isSelectedOptions[index]) {
return MyDecorations.getContainersDecoration(MyColors.greyF7Color);
}
if (isSelectedOptions[index] && context.watch<MarathonProvider>().start > 0) {
return MyDecorations.getContainersDecoration(MyColors.yellowColorII);
}
return MyDecorations.getContainersDecoration(
isSelectedOptions[index] ? MyColors.greenColor : MyColors.greyF7Color,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
MarathonProvider provider = context.watch<MarathonProvider>();
return Container( return Container(
padding: const EdgeInsets.symmetric(vertical: 31, horizontal: 13), padding: const EdgeInsets.symmetric(vertical: 31, horizontal: 13),
decoration: const BoxDecoration( decoration: const BoxDecoration(
@ -164,129 +120,49 @@ class _AnswerContentState extends State<AnswerContent> {
bottomRight: Radius.circular(10), bottomRight: Radius.circular(10),
), ),
), ),
child: Column( child: provider.currentQuestion.questionOptions != null
mainAxisSize: MainAxisSize.min, ? ListView.separated(
mainAxisAlignment: MainAxisAlignment.center, itemCount: provider.currentQuestion.questionOptions!.length,
crossAxisAlignment: CrossAxisAlignment.center, shrinkWrap: true,
children: <Widget>[ itemBuilder: (BuildContext context, int index) {
// todo @faiz: Make a separate method and pass value and string , so we can minimize code duplication return AnswerTileForText(
InkWell( index: index,
onTap: () { onAnswerTapped: () {
if (widget.provider.currentQuestionNumber == 9) { provider.updateCurrentQuestionOptionStatus(QuestionsOptionStatus.selected, index);
widget.provider.cancelTimer();
widget.provider.resetValues();
Navigator.pushReplacementNamed(
context,
AppRoutes.marathonWinnerSelection,
);
return;
}
updateOption(0, true);
}, },
child: Container(
alignment: Alignment.centerLeft,
decoration: getContainerColor(0),
child: Center(
child: Text(
widget.question.opt1!,
style: TextStyle(
color: isSelectedOptions[0] ? MyColors.white : MyColors.darkTextColor,
fontWeight: FontWeight.w600,
fontSize: 16,
),
).paddingOnly(top: 17, bottom: 17),
),
),
),
const SizedBox(height: 15),
InkWell(
onTap: () {
if (widget.provider.currentQuestionNumber == 9) {
widget.provider.cancelTimer();
widget.provider.resetValues();
Navigator.pushReplacementNamed(
context,
AppRoutes.marathonWinnerSelection,
); );
return;
}
updateOption(1, true);
}, },
child: Container( separatorBuilder: (BuildContext context, int index) => 15.height,
alignment: Alignment.centerLeft, )
decoration: getContainerColor(1), : const SizedBox(),
child: Center(
child: Text(
widget.question.opt2!,
style: TextStyle(
color: isSelectedOptions[1] ? MyColors.white : MyColors.darkTextColor,
fontWeight: FontWeight.w600,
fontSize: 16,
),
).paddingOnly(top: 17, bottom: 17),
),
),
),
const SizedBox(height: 15),
InkWell(
onTap: () {
if (widget.provider.currentQuestionNumber == 9) {
widget.provider.cancelTimer();
widget.provider.resetValues();
Navigator.pushReplacementNamed(
context,
AppRoutes.marathonWinnerSelection,
); );
return;
} }
updateOption(2, true); }
},
child: Container( class AnswerTileForText extends StatelessWidget {
alignment: Alignment.centerLeft, final int index;
decoration: getContainerColor(2), final Function() onAnswerTapped;
child: Center(
child: Text( const AnswerTileForText({Key? key, required this.index, required this.onAnswerTapped}) : super(key: key);
widget.question.opt3!,
style: TextStyle( @override
color: isSelectedOptions[2] ? MyColors.white : MyColors.darkTextColor, Widget build(BuildContext context) {
fontWeight: FontWeight.w600, MarathonProvider provider = context.watch<MarathonProvider>();
fontSize: 16, return InkWell(
),
).paddingOnly(top: 17, bottom: 17),
),
),
),
const SizedBox(height: 15),
InkWell(
onTap: () { onTap: () {
if (widget.provider.currentQuestionNumber == 9) { onAnswerTapped();
widget.provider.cancelTimer();
widget.provider.resetValues();
Navigator.pushReplacementNamed(
context,
AppRoutes.marathonWinnerSelection,
);
return;
}
updateOption(3, true);
}, },
child: Container( child: Container(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
decoration: getContainerColor(3), decoration: MyDecorations.getAnswersContainerColor(provider.currentQuestion.questionOptions![index].optionStatus!),
child: Center( child: Center(
child: Text( child: (AppState().isArabic(context) ? provider.currentQuestion.questionOptions![index].titleAr! : provider.currentQuestion.questionOptions![index].titleEn!)
widget.question.opt3!, .toText16(
style: TextStyle( color: provider.currentQuestion.questionOptions![index].optionStatus == QuestionsOptionStatus.unSelected ? MyColors.darkTextColor : MyColors.white,
color: isSelectedOptions[3] ? MyColors.white : MyColors.darkTextColor, )
fontWeight: FontWeight.w600, .paddingOnly(top: 17, bottom: 17),
fontSize: 16,
),
).paddingOnly(top: 17, bottom: 17),
),
), ),
), ),
],
),
); );
} }
} }

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
class QuestionCardBuilder extends StatelessWidget {
final WidgetBuilder onQuestion;
final WidgetBuilder onCompleted;
final WidgetBuilder onWrongAnswer;
final WidgetBuilder onCorrectAnswer;
final WidgetBuilder onWinner;
final WidgetBuilder onSkippedAnswer;
final WidgetBuilder onFindingWinner;
final QuestionCardStatus questionCardStatus;
const QuestionCardBuilder({
Key? key,
required this.onQuestion,
required this.onCompleted,
required this.onCorrectAnswer,
required this.onWinner,
required this.onSkippedAnswer,
required this.onWrongAnswer,
required this.onFindingWinner,
required this.questionCardStatus,
}) : super(key: key);
@override
Widget build(BuildContext context) {
switch (questionCardStatus) {
case QuestionCardStatus.question:
return onQuestion(context);
case QuestionCardStatus.wrongAnswer:
return onWrongAnswer(context);
case QuestionCardStatus.correctAnswer:
return onCorrectAnswer(context);
case QuestionCardStatus.completed:
return onCompleted(context);
case QuestionCardStatus.winnerFound:
return onWinner(context);
case QuestionCardStatus.findingWinner:
return onFindingWinner(context);
case QuestionCardStatus.skippedAnswer:
return onSkippedAnswer(context);
}
}
}

@ -74,9 +74,9 @@ class _DynamicInputScreenState extends State<DynamicInputScreenProfile> {
getBasicDetColsStructureList?.forEach((GetBasicDetColsStructureList element) { getBasicDetColsStructureList?.forEach((GetBasicDetColsStructureList element) {
element.userBasicDetail = element.userBasicDetail =
dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME); dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME);
if (element.objectValuesList != null) { if (element.objectValuesList != null && element.userBasicDetail?.vARCHAR2VALUE != '') {
ObjectValuesList dropDownListValue = element.objectValuesList!.singleWhere((ObjectValuesList dropdown) => dropdown.cODE == element.userBasicDetail!.vARCHAR2VALUE); ObjectValuesList dropDownListValue = element.objectValuesList!.singleWhere((ObjectValuesList dropdown) => dropdown.cODE == element.userBasicDetail?.vARCHAR2VALUE);
element.userBasicDetail!.sEGMENTVALUEDSP = dropDownListValue.mEANING; element.userBasicDetail?.sEGMENTVALUEDSP = dropDownListValue.mEANING;
} }
}); });
} else { } else {
@ -93,9 +93,9 @@ class _DynamicInputScreenState extends State<DynamicInputScreenProfile> {
getBasicDetColsStructureList?.forEach((GetBasicDetColsStructureList element) { getBasicDetColsStructureList?.forEach((GetBasicDetColsStructureList element) {
element.userBasicDetail = element.userBasicDetail =
dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME); dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME);
if (element.objectValuesList != null) { if (element.objectValuesList != null && element.userBasicDetail!.vARCHAR2VALUE != '') {
ObjectValuesList dropDownListValue = element.objectValuesList!.singleWhere((ObjectValuesList dropdown) => dropdown.cODE == element.userBasicDetail!.vARCHAR2VALUE); ObjectValuesList dropDownListValue = element.objectValuesList!.singleWhere((ObjectValuesList dropdown) => dropdown.cODE == element.userBasicDetail!.vARCHAR2VALUE);
element.userBasicDetail!.sEGMENTVALUEDSP = dropDownListValue.mEANING; element.userBasicDetail?.sEGMENTVALUEDSP = dropDownListValue.mEANING;
} }
}); });
} }
@ -262,7 +262,7 @@ class _DynamicInputScreenState extends State<DynamicInputScreenProfile> {
return PopupMenuButton( return PopupMenuButton(
child: DynamicTextFieldWidget( child: DynamicTextFieldWidget(
(model.sEGMENTPROMPT ?? "") + (model.rEQUIREDFLAG == "Y" ? "*" : ""), (model.sEGMENTPROMPT ?? "") + (model.rEQUIREDFLAG == "Y" ? "*" : ""),
getBasicDetColsStructureList![index].userBasicDetail!.sEGMENTVALUEDSP ?? "", getBasicDetColsStructureList![index].userBasicDetail?.sEGMENTVALUEDSP ?? "",
isEnable: false, isEnable: false,
isPopup: true, isPopup: true,
).paddingOnly(bottom: 12), ).paddingOnly(bottom: 12),
@ -363,7 +363,7 @@ class _DynamicInputScreenState extends State<DynamicInputScreenProfile> {
Utils.showLoading(context); Utils.showLoading(context);
int numberValue = 0; int numberValue = 0;
List<Map<String, dynamic>> values = getBasicDetDffStructureList!.map((e) { List<Map<String, dynamic>> values = getBasicDetDffStructureList!.map((e) {
String tempVar = e.userBasicDetail!.vARCHAR2VALUE ?? ""; String tempVar = e.userBasicDetail?.vARCHAR2VALUE ?? "";
if (e.fORMATTYPE == "X") { if (e.fORMATTYPE == "X") {
// for date format type, date format is changed // for date format type, date format is changed

@ -188,6 +188,53 @@ class ServicesMenuShimmer extends StatelessWidget {
} }
} }
class MarathonBannerShimmer extends StatelessWidget {
const MarathonBannerShimmer({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/images/monthly_attendance.svg").toShimmer(),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"Attendan".toText11(isBold: false).toShimmer(),
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer(),
),
6.width,
SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer()
],
),
],
)
],
).paddingOnly(left: 10, right: 10, bottom: 10, top: 12),
);
}
}
class ChatHomeShimmer extends StatelessWidget { class ChatHomeShimmer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

@ -83,6 +83,9 @@ dependencies:
appinio_swiper: ^1.1.1 appinio_swiper: ^1.1.1
expandable: ^5.0.1 expandable: ^5.0.1
# networkImage
cached_network_image: ^3.2.2
#Chat #Chat
signalr_netcore: ^1.3.3 signalr_netcore: ^1.3.3
logging: ^1.0.1 logging: ^1.0.1
@ -91,6 +94,9 @@ dependencies:
camera: ^0.10.0+4 camera: ^0.10.0+4
#Encryption
cryptography: ^2.0.5
cryptography_flutter: ^2.0.2
video_player: ^2.4.7 video_player: ^2.4.7

Loading…
Cancel
Save