Merge branch 'master' into fatima
# Conflicts: # lib/classes/consts.dart # lib/ui/leave_balance/add_leave_balance_screen.dartfatima
commit
85be372ad8
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@ -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>
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:http/http.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/classes/utils.dart';
|
||||||
|
import 'package:mohem_flutter_app/exceptions/api_exception.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_user_login_token_model.dart' as user;
|
||||||
|
import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav;
|
||||||
|
|
||||||
|
class ChatApiClient {
|
||||||
|
static final ChatApiClient _instance = ChatApiClient._internal();
|
||||||
|
|
||||||
|
ChatApiClient._internal();
|
||||||
|
|
||||||
|
factory ChatApiClient() => _instance;
|
||||||
|
|
||||||
|
Future<user.UserAutoLoginModel> getUserLoginToken() async {
|
||||||
|
Response response = await ApiClient().postJsonForResponse(
|
||||||
|
"${ApiConsts.chatLoginTokenUrl}externaluserlogin",
|
||||||
|
{
|
||||||
|
"employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(),
|
||||||
|
"password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
user.UserAutoLoginModel userLoginResponse = user.userAutoLoginModelFromJson(response.body);
|
||||||
|
return userLoginResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<ChatUser>?> getChatMemberFromSearch(String sName, int cUserId) async {
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatLoginTokenUrl}getUserWithStatusAndFavAsync/$sName/$cUserId",
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
return searchUserJsonModel(response.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ChatUser> searchUserJsonModel(String str) => List<ChatUser>.from(json.decode(str).map((x) => ChatUser.fromJson(x)));
|
||||||
|
|
||||||
|
Future<ChatUserModel> getRecentChats() async {
|
||||||
|
try {
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatRecentUrl}getchathistorybyuserid",
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
return ChatUserModel.fromJson(
|
||||||
|
json.decode(response.body),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
e as APIException;
|
||||||
|
if (e.message == "api_common_unauthorized") {
|
||||||
|
user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
|
||||||
|
if (userLoginResponse.response != null) {
|
||||||
|
AppState().setchatUserDetails = userLoginResponse;
|
||||||
|
getRecentChats();
|
||||||
|
} else {
|
||||||
|
Utils.showToast(
|
||||||
|
userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ChatUserModel> getFavUsers() async {
|
||||||
|
Response favRes = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatFavUser}getFavUserById/${AppState().chatDetails!.response!.id}",
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
return ChatUserModel.fromJson(
|
||||||
|
json.decode(favRes.body),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Response> getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false, required int paginationVal}) async {
|
||||||
|
try {
|
||||||
|
Response response = await ApiClient().getJsonForResponse(
|
||||||
|
"${ApiConsts.chatSingleUserHistoryUrl}GetUserChatHistory/$senderUID/$receiverUID/$paginationVal",
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
e as APIException;
|
||||||
|
if (e.message == "api_common_unauthorized") {
|
||||||
|
user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
|
||||||
|
if (userLoginResponse.response != null) {
|
||||||
|
AppState().setchatUserDetails = userLoginResponse;
|
||||||
|
getSingleUserChatHistory(senderUID: senderUID, receiverUID: receiverUID, loadMore: loadMore, paginationVal: paginationVal);
|
||||||
|
} else {
|
||||||
|
Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<fav.FavoriteChatUser> favUser({required int userID, required int targetUserID}) async {
|
||||||
|
Response response = await ApiClient().postJsonForResponse("${ApiConsts.chatFavUser}addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token);
|
||||||
|
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
|
||||||
|
return favoriteChatUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<fav.FavoriteChatUser> unFavUser({required int userID, required int targetUserID}) async {
|
||||||
|
try {
|
||||||
|
Response response = await ApiClient().postJsonForResponse(
|
||||||
|
"${ApiConsts.chatFavUser}deleteFavUser",
|
||||||
|
{"targetUserId": targetUserID, "userId": userID},
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body);
|
||||||
|
return favoriteChatUser;
|
||||||
|
} catch (e) {
|
||||||
|
e as APIException;
|
||||||
|
if (e.message == "api_common_unauthorized") {
|
||||||
|
logger.d("Token Generated On APIIIIII");
|
||||||
|
user.UserAutoLoginModel userLoginResponse = await ChatApiClient().getUserLoginToken();
|
||||||
|
if (userLoginResponse.response != null) {
|
||||||
|
AppState().setchatUserDetails = userLoginResponse;
|
||||||
|
unFavUser(userID: userID, targetUserID: targetUserID);
|
||||||
|
} else {
|
||||||
|
Utils.showToast(userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StreamedResponse> uploadMedia(String userId, File file) async {
|
||||||
|
dynamic request = MultipartRequest('POST', Uri.parse('${ApiConsts.chatMediaImageUploadUrl}upload'));
|
||||||
|
request.fields.addAll({'userId': userId, 'fileSource': '1'});
|
||||||
|
request.files.add(await MultipartFile.fromPath('files', file.path));
|
||||||
|
request.headers.addAll({'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}'});
|
||||||
|
StreamedResponse response = await request.send();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download File For Chat
|
||||||
|
|
||||||
|
Future<Uint8List> downloadURL({required String fileName, required String fileTypeDescription}) async {
|
||||||
|
Response response = await ApiClient().postJsonForResponse(
|
||||||
|
"${ApiConsts.chatMediaImageUploadUrl}download",
|
||||||
|
{"fileType": fileTypeDescription, "fileName": fileName, "fileSource": 1},
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
Uint8List data = Uint8List.fromList(response.bodyBytes);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future getUsersImages({required List encryptedEmails}) async {
|
||||||
|
Response response = await ApiClient().postJsonForResponse(
|
||||||
|
"${ApiConsts.chatUserImages}images",
|
||||||
|
{
|
||||||
|
"encryptedEmails": ["/g8Rc+s6eEOdci41PwJuV5dX+gXe51G9OTHzb9ahcVlHCmVvNhxReirudF79+hdxVSkCnQ6wC5DBFV8xnJlC74X6157PxF7mNYrAYuHRgp4="],
|
||||||
|
"fromClient": true
|
||||||
|
},
|
||||||
|
token: AppState().chatDetails!.response!.token,
|
||||||
|
);
|
||||||
|
logger.d(response.body);
|
||||||
|
// Uint8List data = Uint8List.fromList(response.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,150 +1,237 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mohem_flutter_app/api/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/classes/colors.dart';
|
import 'package:mohem_flutter_app/classes/colors.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:sizer/sizer.dart';
|
import 'package:mohem_flutter_app/main.dart';
|
||||||
|
import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart';
|
||||||
|
import 'package:mohem_flutter_app/ui/chat/chat_full_image_preview.dart';
|
||||||
|
import 'package:mohem_flutter_app/widgets/bottom_sheet.dart';
|
||||||
|
|
||||||
|
// todo: @aamir use extension methods, and use correct widgets.
|
||||||
|
|
||||||
class ChatBubble extends StatelessWidget {
|
class ChatBubble extends StatelessWidget {
|
||||||
const ChatBubble(
|
ChatBubble({Key? key, required this.dateTime, required this.cItem}) : super(key: key);
|
||||||
{Key? key,
|
|
||||||
required this.text,
|
|
||||||
required this.replyText,
|
|
||||||
required this.isCurrentUser,
|
|
||||||
required this.isSeen,
|
|
||||||
required this.isDelivered,
|
|
||||||
required this.dateTime,
|
|
||||||
required this.isReplied,
|
|
||||||
required this.userName})
|
|
||||||
: super(key: key);
|
|
||||||
final String text;
|
|
||||||
final String replyText;
|
|
||||||
final bool isCurrentUser;
|
|
||||||
final bool isSeen;
|
|
||||||
final bool isDelivered;
|
|
||||||
final String dateTime;
|
final String dateTime;
|
||||||
final bool isReplied;
|
final SingleUserChatModel cItem;
|
||||||
final String userName;
|
|
||||||
|
bool isCurrentUser = false;
|
||||||
|
bool isSeen = false;
|
||||||
|
bool isReplied = false;
|
||||||
|
int? fileTypeID;
|
||||||
|
|
||||||
|
String? fileTypeDescription;
|
||||||
|
bool isDelivered = false;
|
||||||
|
String userName = '';
|
||||||
|
|
||||||
|
void makeAssign() {
|
||||||
|
isCurrentUser = cItem.currentUserId == AppState().chatDetails!.response!.id ? true : false;
|
||||||
|
isSeen = cItem.isSeen == true ? true : false;
|
||||||
|
isReplied = cItem.userChatReplyResponse != null ? true : false;
|
||||||
|
fileTypeID = cItem.fileTypeId;
|
||||||
|
fileTypeDescription = cItem.fileTypeResponse != null ? cItem.fileTypeResponse!.fileTypeDescription : "";
|
||||||
|
isDelivered = cItem.currentUserId == AppState().chatDetails!.response!.id && cItem.isDelivered == true ? true : false;
|
||||||
|
userName = AppState().chatDetails!.response!.userName == cItem.currentUserName.toString() ? "You" : cItem.currentUserName.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
makeAssign();
|
||||||
// padding: EdgeInsets.zero,
|
return isCurrentUser ? currentUser(context) : receiptUser(context);
|
||||||
padding: EdgeInsets.only(
|
}
|
||||||
left: isCurrentUser ? 110 : 20,
|
|
||||||
right: isCurrentUser ? 20 : 110,
|
|
||||||
bottom: 9,
|
|
||||||
),
|
|
||||||
|
|
||||||
child: Align(
|
Widget currentUser(context) {
|
||||||
alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
|
return Column(
|
||||||
child: DecoratedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
color: MyColors.white,
|
if (isReplied)
|
||||||
gradient: isCurrentUser
|
ClipRRect(
|
||||||
? null
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
: const LinearGradient(
|
child: Container(
|
||||||
transform: GradientRotation(
|
width: double.infinity,
|
||||||
.46,
|
decoration: BoxDecoration(
|
||||||
),
|
border: Border(
|
||||||
begin: Alignment.topRight,
|
left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white),
|
||||||
end: Alignment.bottomLeft,
|
),
|
||||||
colors: <Color>[
|
color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
|
||||||
MyColors.gradiantEndColor,
|
),
|
||||||
MyColors.gradiantStartColor,
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
(userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5),
|
||||||
|
(cItem.userChatReplyResponse != null ? cItem.userChatReplyResponse!.contant.toString() : "")
|
||||||
|
.toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4)
|
||||||
|
.paddingOnly(right: 5, top: 5, bottom: 8, left: 5),
|
||||||
],
|
],
|
||||||
),
|
).expanded,
|
||||||
borderRadius: BorderRadius.circular(
|
if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 ||
|
||||||
10,
|
cItem.userChatReplyResponse!.fileTypeId == 3 ||
|
||||||
),
|
cItem.userChatReplyResponse!.fileTypeId == 4)
|
||||||
),
|
// Container(
|
||||||
child: Padding(
|
// padding: EdgeInsets.all(0), // Border width
|
||||||
padding: EdgeInsets.only(
|
// decoration: BoxDecoration(color: Colors.red, borderRadius: const BorderRadius.all(Radius.circular(8))),
|
||||||
top: isReplied ? 8 : 5,
|
// child: ClipRRect(
|
||||||
right: 8,
|
// borderRadius: const BorderRadius.all(
|
||||||
left: 8,
|
// Radius.circular(8),
|
||||||
bottom: 5,
|
// ),
|
||||||
),
|
// child: SizedBox.fromSize(
|
||||||
child: Column(
|
// size: Size.fromRadius(8), // Image radius
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
// child: showImage(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
// isReplyPreview: true,
|
||||||
children: [
|
// fileName: cItem.userChatReplyResponse!.contant!,
|
||||||
if (isReplied)
|
// fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg"),
|
||||||
ClipRRect(
|
// ),
|
||||||
borderRadius: BorderRadius.circular(
|
// ),
|
||||||
5.0,
|
// ),
|
||||||
),
|
ClipRRect(
|
||||||
child: Container(
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
decoration: BoxDecoration(
|
child: SizedBox(
|
||||||
border: Border(
|
height: 32,
|
||||||
left: BorderSide(
|
width: 32,
|
||||||
width: 6,
|
child: showImage(
|
||||||
color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white,
|
isReplyPreview: true,
|
||||||
),
|
fileName: cItem.userChatReplyResponse!.contant!,
|
||||||
),
|
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg")
|
||||||
color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
|
.paddingOnly(left: 10, right: 10, bottom: 16, top: 16),
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
(userName)
|
|
||||||
.toText12(
|
|
||||||
color: MyColors.gradiantStartColor,
|
|
||||||
isBold: false,
|
|
||||||
)
|
|
||||||
.paddingOnly(
|
|
||||||
right: 5,
|
|
||||||
top: 5,
|
|
||||||
bottom: 0,
|
|
||||||
left: 5,
|
|
||||||
),
|
|
||||||
replyText
|
|
||||||
.toText10(
|
|
||||||
color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5),
|
|
||||||
isBold: false,
|
|
||||||
maxlines: 4,
|
|
||||||
)
|
|
||||||
.paddingOnly(
|
|
||||||
right: 5,
|
|
||||||
top: 5,
|
|
||||||
bottom: 8,
|
|
||||||
left: 5,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
if (isReplied) 8.height,
|
),
|
||||||
text.toText12(
|
),
|
||||||
color: isCurrentUser ? MyColors.grey57Color : MyColors.white,
|
).paddingOnly(right: 5, bottom: 7),
|
||||||
|
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
|
||||||
|
showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription).paddingOnly(right: 5).onPress(() {
|
||||||
|
showDialog(context: context, builder: (BuildContext context) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!));
|
||||||
|
}),
|
||||||
|
cItem.contant!.toText12(),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
dateTime.toText10(
|
||||||
|
color: MyColors.grey41Color.withOpacity(.5),
|
||||||
|
),
|
||||||
|
7.width,
|
||||||
|
Icon(isDelivered ? Icons.done_all : Icons.done_all, color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, size: 14),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingOnly(top: 11, left: 13, right: 7, bottom: 5).objectContainerView(disablePadding: true).paddingOnly(left: MediaQuery.of(context).size.width * 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget receiptUser(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(top: 11, left: 13, right: 7, bottom: 5),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
gradient: const LinearGradient(
|
||||||
|
transform: GradientRotation(.83),
|
||||||
|
begin: Alignment.topRight,
|
||||||
|
end: Alignment.bottomLeft,
|
||||||
|
colors: <Color>[
|
||||||
|
MyColors.gradiantEndColor,
|
||||||
|
MyColors.gradiantStartColor,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (isReplied)
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(left: BorderSide(width: 6, color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white)),
|
||||||
|
color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30),
|
||||||
),
|
),
|
||||||
Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
dateTime.toText12(
|
children: <Widget>[
|
||||||
color: isCurrentUser ? MyColors.grey41Color.withOpacity(.5) : MyColors.white.withOpacity(0.7),
|
(userName).toText12(color: MyColors.gradiantStartColor, isBold: false).paddingOnly(right: 5, top: 5, bottom: 0, left: 5),
|
||||||
),
|
(cItem.userChatReplyResponse != null ? cItem.userChatReplyResponse!.contant.toString() : "")
|
||||||
if (isCurrentUser) 5.width,
|
.toText10(color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), isBold: false, maxlines: 4)
|
||||||
// if (isCurrentUser)
|
.paddingOnly(right: 5, top: 5, bottom: 8, left: 5),
|
||||||
// Icon(
|
],
|
||||||
// isDelivered ? Icons.done_all : Icons.done_all,
|
).expanded,
|
||||||
// color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor,
|
if (cItem.userChatReplyResponse != null && cItem.userChatReplyResponse!.fileTypeId == 12 ||
|
||||||
// size: 14,
|
cItem.userChatReplyResponse!.fileTypeId == 3 ||
|
||||||
// ),
|
cItem.userChatReplyResponse!.fileTypeId == 4)
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
child: showImage(
|
||||||
|
isReplyPreview: true,
|
||||||
|
fileName: cItem.userChatReplyResponse!.contant!,
|
||||||
|
fileTypeDescription: cItem.userChatReplyResponse!.fileTypeResponse!.fileTypeDescription ?? "image/jpg")),
|
||||||
|
).paddingOnly(left: 10, right: 10, bottom: 16, top: 16)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
).paddingOnly(right: 5, bottom: 7),
|
||||||
|
if (fileTypeID == 12 || fileTypeID == 4 || fileTypeID == 3)
|
||||||
|
showImage(isReplyPreview: false, fileName: cItem.contant!, fileTypeDescription: cItem.fileTypeResponse!.fileTypeDescription ?? "image/jpg").paddingOnly(right: 5).onPress(() {
|
||||||
|
showDialog(context: context, builder: (BuildContext context) => ChatImagePreviewScreen(imgTitle: cItem.contant!, img: cItem.image!));
|
||||||
|
})
|
||||||
|
else
|
||||||
|
(cItem.contant! ?? "").toText12(color: Colors.white),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: dateTime.toText10(color: Colors.white.withOpacity(.71)),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
).paddingOnly(right: MediaQuery.of(context).size.width * 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget showImage({required bool isReplyPreview, required String fileName, required String fileTypeDescription}) {
|
||||||
|
if (cItem.isImageLoaded! && cItem.image != null) {
|
||||||
|
return Image.memory(
|
||||||
|
cItem.image!,
|
||||||
|
height: isReplyPreview ? 32 : 140,
|
||||||
|
width: isReplyPreview ? 32 : 227,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return FutureBuilder<Uint8List>(
|
||||||
|
future: ChatApiClient().downloadURL(fileName: fileName, fileTypeDescription: fileTypeDescription),
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
|
||||||
|
if (snapshot.connectionState != ConnectionState.waiting) {
|
||||||
|
if (snapshot.data == null) {
|
||||||
|
return SizedBox();
|
||||||
|
} else {
|
||||||
|
cItem.image = snapshot.data;
|
||||||
|
cItem.isImageLoaded = true;
|
||||||
|
return Image.memory(
|
||||||
|
snapshot.data,
|
||||||
|
height: isReplyPreview ? 32 : 140,
|
||||||
|
width: isReplyPreview ? 32 : 227,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return SizedBox(
|
||||||
|
height: isReplyPreview ? 32 : 140,
|
||||||
|
width: isReplyPreview ? 32 : 227,
|
||||||
|
child: const Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
|
||||||
|
|
||||||
|
class ChatImagePreviewScreen extends StatelessWidget {
|
||||||
|
const ChatImagePreviewScreen({Key? key, required this.imgTitle, required this.img}) : super(key: key);
|
||||||
|
|
||||||
|
final String imgTitle;
|
||||||
|
final Uint8List img;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
insetPadding: const EdgeInsets.all(10),
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Image.memory(
|
||||||
|
img,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
height: 400,
|
||||||
|
width: double.infinity,
|
||||||
|
).paddingAll(10),
|
||||||
|
const Positioned(
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
child: Icon(Icons.cancel, color: MyColors.redA3Color, size: 35),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io' as Io;
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
import 'package:mohem_flutter_app/api/dashboard_api_client.dart';
|
||||||
|
import 'package:mohem_flutter_app/classes/colors.dart';
|
||||||
|
import 'package:mohem_flutter_app/main.dart';
|
||||||
|
import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads;
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
|
class ITGAdsScreen extends StatefulWidget {
|
||||||
|
const ITGAdsScreen({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ITGAdsScreenState createState() => _ITGAdsScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ITGAdsScreenState extends State<ITGAdsScreen> {
|
||||||
|
late Future<VideoPlayerController> _futureController;
|
||||||
|
late VideoPlayerController _controller;
|
||||||
|
bool skip = false;
|
||||||
|
bool isVideo = false;
|
||||||
|
bool isImage = false;
|
||||||
|
String ext = '';
|
||||||
|
late File imageFile;
|
||||||
|
ads.Advertisement? advertisementData;
|
||||||
|
dynamic data;
|
||||||
|
String? masterID;
|
||||||
|
|
||||||
|
void checkFileType() async {
|
||||||
|
String? rFile = advertisementData!.viewAttachFileColl!.first.base64String;
|
||||||
|
String? rFileExt = advertisementData!.viewAttachFileColl!.first.fileName;
|
||||||
|
ext = "." + rFileExt!.split(".").last.toLowerCase();
|
||||||
|
if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif") {
|
||||||
|
await processImage(rFile!);
|
||||||
|
isImage = true;
|
||||||
|
} else {
|
||||||
|
isVideo = true;
|
||||||
|
_futureController = createVideoPlayer(rFile!);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
initTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future processImage(String encodedBytes) async {
|
||||||
|
try {
|
||||||
|
Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last);
|
||||||
|
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1
|
||||||
|
imageFile = Io.File("${appDocumentsDirectory.path}/addImage$ext");
|
||||||
|
imageFile.writeAsBytesSync(decodedBytes);
|
||||||
|
} catch (e) {
|
||||||
|
logger.d(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<VideoPlayerController> createVideoPlayer(String encodedBytes) async {
|
||||||
|
try {
|
||||||
|
Uint8List decodedBytes = base64Decode(encodedBytes.split("base64,").last);
|
||||||
|
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory(); // 1
|
||||||
|
File file = Io.File("${appDocumentsDirectory.path}/myAdsVideo.mp4");
|
||||||
|
file.writeAsBytesSync(decodedBytes);
|
||||||
|
VideoPlayerController controller = VideoPlayerController.file(file);
|
||||||
|
await controller.initialize();
|
||||||
|
await controller.play();
|
||||||
|
await controller.setVolume(1.0);
|
||||||
|
await controller.setLooping(false);
|
||||||
|
return controller;
|
||||||
|
} catch (e) {
|
||||||
|
return new VideoPlayerController.asset("dataSource");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initTimer() {
|
||||||
|
Future.delayed(const Duration(seconds: 5), () {
|
||||||
|
skip = true;
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
data = ModalRoute.of(context)!.settings.arguments;
|
||||||
|
if (advertisementData == null) advertisementData = data["advertisement"] as ads.Advertisement;
|
||||||
|
if (masterID == null) masterID = data["masterId"];
|
||||||
|
if (advertisementData != null) {
|
||||||
|
checkFileType();
|
||||||
|
}
|
||||||
|
double height = MediaQuery.of(context).size.height * .25;
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
if (isVideo)
|
||||||
|
SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * .3,
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _futureController,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<Object?> snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) {
|
||||||
|
_controller = snapshot.data as VideoPlayerController;
|
||||||
|
return AspectRatio(
|
||||||
|
aspectRatio: _controller.value.aspectRatio,
|
||||||
|
child: VideoPlayer(_controller),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isImage) Image.file(imageFile),
|
||||||
|
if (skip)
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
// DashboardApiClient().setAdvertisementViewed(widget.addMasterId, widget.advertisement!.advertisementId!).then((value) {
|
||||||
|
// logger.d(value);
|
||||||
|
// });
|
||||||
|
},
|
||||||
|
child: const Text("Go To Dashboard"),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,96 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:io' as Io;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:video_player/video_player.dart';
|
|
||||||
|
|
||||||
class MovieTheaterBody extends StatefulWidget {
|
|
||||||
final String encodedBytes;
|
|
||||||
|
|
||||||
const MovieTheaterBody({required this.encodedBytes});
|
|
||||||
|
|
||||||
@override
|
|
||||||
_MovieTheaterBodyState createState() => _MovieTheaterBodyState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MovieTheaterBodyState extends State<MovieTheaterBody> {
|
|
||||||
late Future<VideoPlayerController> _futureController;
|
|
||||||
late VideoPlayerController _controller;
|
|
||||||
|
|
||||||
Future<VideoPlayerController> createVideoPlayer() async {
|
|
||||||
try {
|
|
||||||
var decodedBytes = base64Decode(widget.encodedBytes);
|
|
||||||
|
|
||||||
var file = Io.File("decodedBezkoder.mp4");
|
|
||||||
file.writeAsBytesSync(decodedBytes);
|
|
||||||
|
|
||||||
VideoPlayerController controller = VideoPlayerController.file(file);
|
|
||||||
await controller.initialize();
|
|
||||||
await controller.setLooping(true);
|
|
||||||
return controller;
|
|
||||||
} catch (e) {
|
|
||||||
print("object0000000");
|
|
||||||
print(e);
|
|
||||||
return new VideoPlayerController.asset("dataSource");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_futureController = createVideoPlayer();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: Expanded(
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: _futureController,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
//UST: 05/2021 - MovieTheaterBody - id:11 - 2pts - Criação
|
|
||||||
if (snapshot.connectionState == ConnectionState.done && snapshot.data != null) {
|
|
||||||
_controller = snapshot.data as VideoPlayerController;
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: _controller.value.aspectRatio,
|
|
||||||
child: VideoPlayer(_controller),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 50,
|
|
||||||
),
|
|
||||||
FloatingActionButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
if (_controller.value.isPlaying) {
|
|
||||||
_controller.pause();
|
|
||||||
} else {
|
|
||||||
// If the video is paused, play it.
|
|
||||||
_controller.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
backgroundColor: Colors.green[700],
|
|
||||||
child: Icon(
|
|
||||||
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue