You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
car_common_app/lib/views/chat/widgets/chat_message_widget.dart

1007 lines
50 KiB
Dart

import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:easy_localization/easy_localization.dart' as lcl;
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/chat/widgets/chat_bottom_sheets.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
final RequestStatusEnum? requestStatusEnum;
final ChatTypeEnum chatTypeEnum;
final RequestsTypeEnum requestsTypeEnum;
final RequestModel? requestModel;
const ChatMessageCustomWidget({
super.key,
required this.chatMessageModel,
required this.requestStatusEnum,
required this.chatTypeEnum,
this.requestModel,
this.requestsTypeEnum = RequestsTypeEnum.specialCarRequest,
});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Future buildDealNotCompletedBottomSheetOptions({required ChatMessageModel chatMessageModel}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) {
bool indexContains = chatVM.indexesForDealNotCompleted.indexWhere((i) => i == index) != -1;
if ((!indexContains)) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
bool indexContains = chatVM.indexesForDealNotCompleted.indexWhere((i) => i == index) != -1;
if (!indexContains) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
chatVM.updateSelectionInOfferRejectModelList(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
if (chatVM.offerRejectModelList.last.isSelected ?? false) ...[
// comparing if the "other" is selected
12.height,
TxtField(
maxLines: 5,
value: chatVM.rejectOfferDescription,
errorValue: chatVM.rejectOfferDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => chatVM.updateRejectOfferDescription(v),
),
],
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
String comments = "";
if (chatVM.offerRejectModelList.last.isSelected ?? false) {
comments = chatVM.rejectOfferDescription;
} else {
comments = chatVM.offerRejectModelList.firstWhere((element) => element.isSelected!).title ?? "";
}
if (!chatVM.isRejectOfferButtonValidated()) {
return;
}
Navigator.pop(context);
bool status = await chatVM.onSendMessageForActionOnRequestOffer(
receiverId: (chatMessageModel.isMyMessage ?? false) ? chatMessageModel.receiverUserID ?? "" : chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
if (status) {
final requestVM = context.read<RequestsVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected;
int index = chatVM.serviceProviderOffersList
.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
chatVM.updateRejectOfferDescription('');
Utils.showToast(LocaleKeys.offerRejected.tr());
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
Future buildDealBottomSheetOptionsForSpecialCar({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum, required bool fromCancelOffer}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext sheetContext) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.pleaseSpecify.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
CheckBoxWithTitleDescription(
isSelected: chatVM.dealOptionsModelList[0].isSelected!,
title: '${chatVM.dealOptionsModelList[0].title}',
description: '',
onSelection: (bool value) {
chatVM.updateIsSelectedInDealOptionsModelList(0, value);
},
),
CheckBoxWithTitleDescription(
isSelected: chatVM.dealOptionsModelList[1].isSelected ?? false,
title: '${chatVM.dealOptionsModelList[1].title}',
description: '',
onSelection: (bool value) {
chatVM.updateIsSelectedInDealOptionsModelList(1, value);
},
),
],
),
25.height,
ShowFillButton(
title: LocaleKeys.continu.tr(),
onPressed: () async {
pop(sheetContext);
if (chatVM.dealOptionsModelList[0].isSelected ?? false) {
if (fromCancelOffer) {
final requestVM = context.read<RequestsVM>();
return dealCompletedConsentBottomSheet(
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestVM.currentSelectedRequest!.id,
showAcknowledgement: AppState().currentAppType == AppType.customer,
);
} else {
final requestVM = context.read<RequestsVM>();
return dealCompletedConsentBottomSheet(
mainContext: context,
requestStatusEnum: RequestStatusEnum.completed,
requestId: requestVM.currentSelectedRequest!.id,
showAcknowledgement: AppState().currentAppType == AppType.customer,
acceptRequestOffer: () async {
bool status = await chatVM.onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: GlobalConsts.acceptingThisOffer,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress;
requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!);
requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? "");
int index = chatVM.serviceProviderOffersList.indexWhere(
(element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
Utils.showToast(LocaleKeys.offerAccepted.tr());
return true;
} else {
return false;
}
});
}
} else {
if (fromCancelOffer) {
buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.cancel);
} else {
buildDealNotCompletedBottomSheetOptions(chatMessageModel: chatMessageModel);
}
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
Future buildRejectOrCancelOfferBottomSheet({required ChatMessageModel chatMessageModel, required RequestOfferStatusEnum requestOfferStatusEnum}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
String title = "";
if (requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
title = LocaleKeys.dealNotCompleted.tr();
} else {
title = LocaleKeys.pleaseSpecify.tr();
}
return InfoBottomSheet(
title: title.toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) {
bool indexContainsInCancel = widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest
? (chatVM.indexesForCancelSpecialCarOffer.indexWhere((i) => i == index) != -1)
: (chatVM.indexesForCancelSparePartOffer.indexWhere((i) => i == index) != -1);
bool indexContainsInReject = chatVM.indexesForRejectOffer.indexWhere((i) => i == index) != -1;
if ((!indexContainsInCancel) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
return const SizedBox();
}
if ((!indexContainsInReject) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
return const Divider(thickness: 0.5);
},
itemBuilder: (BuildContext context, int index) {
bool indexContainsInCancel = widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest
? (chatVM.indexesForCancelSpecialCarOffer.indexWhere((i) => i == index) != -1)
: (chatVM.indexesForCancelSparePartOffer.indexWhere((i) => i == index) != -1);
bool indexContainsInReject = chatVM.indexesForRejectOffer.indexWhere((i) => i == index) != -1;
if ((!indexContainsInCancel) && requestOfferStatusEnum == RequestOfferStatusEnum.cancel) {
return const SizedBox();
}
if ((!indexContainsInReject) && requestOfferStatusEnum == RequestOfferStatusEnum.rejected) {
return const SizedBox();
}
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
chatVM.updateSelectionInOfferRejectModelList(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) ...[
// comparing if the "other" is selected
12.height,
TxtField(
maxLines: 5,
value: chatVM.rejectOfferDescription,
errorValue: chatVM.rejectOfferDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => chatVM.updateRejectOfferDescription(v),
),
],
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
String comments = "";
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) //Other
{
comments = chatVM.rejectOfferDescription;
} else {
comments = chatVM.selectedOfferRequestCommentModel.title ?? "";
}
if (!chatVM.isRejectOfferButtonValidated()) {
return;
}
Navigator.pop(context);
bool status = await chatVM.onSendMessageForActionOnRequestOffer(
receiverId: (chatMessageModel.isMyMessage ?? false) ? chatMessageModel.receiverUserID ?? "" : chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: requestOfferStatusEnum,
context: context,
);
if (status) {
final requestVM = context.read<RequestsVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = requestOfferStatusEnum;
int index = chatVM.serviceProviderOffersList
.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
// Navigator.pop(context);
chatVM.updateRejectOfferDescription('');
Utils.showToast("Offer ${requestOfferStatusEnum == RequestOfferStatusEnum.rejected ? "Rejected" : "Cancelled"}");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
void offerAcceptConfirmationBottomSheet({required ChatMessageModel chatMessageModel}) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.acceptOfferConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.acceptOfferConfirmationMessage.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
Navigator.pop(context);
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: GlobalConsts.acceptingThisOffer,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
isDeliveryAvailable: chatMessageModel.reqOffer!.isDeliveryAvailable ?? false,
offerDeliveryOption: chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.selfPickup,
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedByName: chatMessageModel.reqOffer!.manufacturedByName ?? "",
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
if (status) {
final requestVM = context.read<RequestsVM>();
ChatVM chatVM = context.read<ChatVM>();
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
requestVM.currentSelectedRequest!.requestStatus = RequestStatusEnum.inProgress;
requestVM.updateAcceptedReqOffer(chatMessageModel.reqOffer!);
requestVM.updateAcceptedRequestOfferProviderName(chatMessageModel.senderName ?? "");
int index = chatVM.serviceProviderOffersList
.indexWhere((element) => (element.providerId == requestVM.currentSelectedOffer!.providerId) && (element.requestId == requestVM.currentSelectedOffer!.requestId));
if (index != -1) {
chatVM.serviceProviderOffersList[index].requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum;
}
setState(() {});
// Navigator.pop(context);
Utils.showToast(LocaleKeys.offerAccepted.tr());
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
Widget buildImagesInOffer(List<MessageImageModel> offerImages, bool isMyMessage) {
int imageCount = offerImages.length;
return SizedBox(
height: 50,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: imageCount,
itemBuilder: (BuildContext context, int index) {
Widget widget = const SizedBox();
if ((offerImages[index].isFromNetwork ?? false)) {
widget = offerImages[index].imageUrl.buildNetworkImage(fit: BoxFit.cover, width: 50);
} else {
widget = offerImages[index].imagePath.buildFileImage(fit: BoxFit.cover, width: 50);
}
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), // Optional: Rounded corners
),
child: ClipRRect(borderRadius: BorderRadius.circular(5), child: widget))
.paddingOnly(left: isMyMessage ? 10 : 0, right: isMyMessage ? 0 : 10)
.onPress(() => navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: offerImages));
}),
);
}
Widget buildOfferDetailsInChatMessage({required RequestStatusEnum requestStatusEnum, required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
if (requestStatusEnum != RequestStatusEnum.submitted && requestOfferStatusEnum != RequestOfferStatusEnum.accepted) {
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerNoLongerAvailable.tr().toText(
color: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor : MyColors.lightTextColor,
fontSize: 12,
isItalic: true,
),
).toContainer(
borderRadius: 40, width: double.infinity, backgroundColor: chatMessageModel.isMyMessage! ? MyColors.adPendingStatusColor.withOpacity(0.16) : MyColors.grey98Color.withOpacity(0.1)),
],
);
}
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
children: [
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}"
.toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
if (widget.chatMessageModel.isMyMessage == false) ...[
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.accept.tr(),
fontSize: 9,
borderColor: MyColors.greenColor,
isFilled: false,
onPressed: () {
if (widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest) {
buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum, fromCancelOffer: false);
} else {
offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel);
}
},
backgroundColor: MyColors.white,
txtColor: MyColors.greenColor,
),
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.reject.tr(),
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () {
buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.rejected);
},
),
)
],
),
] else if (requestStatusEnum == RequestStatusEnum.submitted) ...[
10.height,
Row(
children: [
if (widget.requestsTypeEnum == RequestsTypeEnum.specialCarRequest) ...[
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.more.tr(),
fontSize: 9,
borderColor: MyColors.lightTextColor,
isFilled: false,
onPressed: () {
buildDealBottomSheetOptionsForSpecialCar(chatMessageModel: chatMessageModel, requestOfferStatusEnum: requestOfferStatusEnum, fromCancelOffer: true);
},
backgroundColor: MyColors.white,
txtColor: MyColors.lightTextColor,
),
),
20.width,
],
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.cancelOffer.tr(),
fontSize: 9,
borderColor: MyColors.lightTextColor,
isFilled: false,
onPressed: () {
buildRejectOrCancelOfferBottomSheet(chatMessageModel: chatMessageModel, requestOfferStatusEnum: RequestOfferStatusEnum.cancel);
},
backgroundColor: MyColors.white,
txtColor: MyColors.lightTextColor,
),
),
],
),
],
],
);
case RequestOfferStatusEnum.negotiate:
return Column(
children: [
Center(
child: LocaleKeys.newOfferRequired.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.accepted:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenAccepted.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.rejected:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenRejected.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.cancel:
return Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: chatMessageModel.isMyMessage! ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
5.height,
Center(
child: LocaleKeys.offerHasBeenCancelled.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
}
}
Widget getProfilePicture({String profileImageUrl = ""}) {
Widget widget = const SizedBox();
if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null) {
widget = Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 34,
height: 34,
fit: BoxFit.fill,
);
} else if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null) {
widget = CachedNetworkImage(
imageUrl: AppState().getUser.data!.userInfo!.userImageUrl,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined),
fadeInCurve: Curves.easeIn,
width: 34,
height: 34,
fit: BoxFit.fill,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false);
}
return widget.toCircle(borderRadius: 100);
}
Future<void> onOfferEditIconPressed() async {
int index = context.read<RequestsVM>().myFilteredRequests.indexWhere((request) => request.id == widget.requestModel!.id);
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(
requestIndex: index, // Not getting used in case of update offer
requestModel: widget.requestModel!,
);
RequestsVM requestVM = context.read<RequestsVM>();
requestVM.resetSendOfferBottomSheet();
ReqOffer offer = widget.chatMessageModel.reqOffer!;
requestVM.updateOfferPrice((offer.price ?? "").toString());
requestVM.updateServiceItem((offer.serviceItemName ?? "").toString());
requestVM.updateItemManufacturer((offer.manufacturedByName ?? "").toString());
requestVM.updateServiceItemCreatedOn(DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(offer.manufacturedOn.toString() ?? ""))));
requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString());
requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false));
requestVM.applyFilterOnDeliveryOption(requestDeliveryOptionEnum: offer.requestDeliveryOption ?? RequestDeliveryOptionEnum.none);
if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) {
for (var element in offer.reqOfferImages!) {
if (element.imageUrl != null || element.imageStr != null) {
ImageModel imageModel = ImageModel(
id: element.id,
filePath: element.imageStr != null && element.imageStr!.isNotEmpty ? element.imageStr : element.imageUrl,
isFromNetwork: element.imageStr != null && element.imageStr!.isNotEmpty ? false : true);
requestVM.addImageToPickedVehicleImages(imageModel);
}
}
}
buildSendOfferBottomSheet(context: context, requestDetailPageArguments: requestDetailArguments, isFromChatScreen: true, offerId: offer.id);
}
Widget buildFreeTextDetailsInMessage({required ChatMessageTypeEnum chatMessageTypeEnum}) {
return Row(
children: [
if (chatMessageTypeEnum == ChatMessageTypeEnum.offer && (widget.chatMessageModel.isMyMessage == true) && (widget.chatMessageModel.isRead ?? false)) ...[
Row(
children: [
const Icon(Icons.remove_red_eye_outlined, size: 12, color: MyColors.lightTextColor),
4.width,
LocaleKeys.viewed.tr().toText(fontSize: 10, color: MyColors.lightTextColor, fontWeight: MyFonts.Medium),
],
),
] else if (AppState().currentAppType == AppType.provider &&
chatMessageTypeEnum == ChatMessageTypeEnum.offer &&
widget.chatMessageModel.reqOffer!.requestOfferStatusEnum == RequestOfferStatusEnum.offer &&
(widget.chatMessageModel.isMyMessage == true && widget.chatMessageModel.reqOffer != null && widget.chatMessageModel.reqOffer!.id != null)) ...[
MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()),
],
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
),
),
),
],
);
}
Widget buildImageGridWidget({required List<MessageImageModel> messagesImages, double gridItemSize = 50.0, double spacing = 8.0, double borderWidth = 1, Color borderColor = MyColors.textColor}) {
int imageCount = messagesImages.length;
if (imageCount == 1) {
return Container(
width: double.infinity,
height: (gridItemSize * 4),
decoration: BoxDecoration(
border: Border.all(width: borderWidth, color: borderColor),
borderRadius: BorderRadius.circular(8),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: (messagesImages[0].isFromNetwork ?? false)
? messagesImages[0].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
)
: messagesImages[0].imagePath.buildFileImage(fit: BoxFit.cover),
),
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
}
return SizedBox(
// height: (gridItemSize * 2) + (spacing * 2), // Fixed height for 2 rows including spacing
child: GridView.builder(
physics: const NeverScrollableScrollPhysics(),
// Prevent scrolling inside grid
shrinkWrap: true,
// Shrink size to fit the content
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Show 2 images per row
crossAxisSpacing: spacing,
mainAxisSpacing: spacing,
childAspectRatio: 1, // Keep items square
),
itemCount: imageCount > 4 ? 4 : imageCount,
itemBuilder: (context, index) {
if (index == 3 && imageCount > 4) {
return Stack(
fit: StackFit.expand,
children: [
(messagesImages[index].isFromNetwork ?? false)
? messagesImages[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: gridItemSize,
height: gridItemSize,
)
: messagesImages[index].imagePath.buildFileImage(
fit: BoxFit.cover,
width: gridItemSize,
height: gridItemSize,
),
Container(
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
border: Border.all(width: borderWidth, color: borderColor),
borderRadius: BorderRadius.circular(8), // Optional: Rounded corners
),
child: Center(
child: '+${imageCount - 4}'.toText(fontSize: 30, color: MyColors.white, fontWeight: MyFonts.Medium),
),
),
],
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
} else {
return Container(
width: gridItemSize,
height: gridItemSize,
decoration: BoxDecoration(
border: Border.all(width: borderWidth, color: borderColor),
borderRadius: BorderRadius.circular(8), // Optional: Rounded corners
),
child: ClipRRect(
borderRadius: BorderRadius.circular(8), // Apply same radius to image
child: (messagesImages[index].isFromNetwork ?? false)
? messagesImages[index].imageUrl.buildNetworkImage(
fit: BoxFit.cover,
width: gridItemSize,
height: gridItemSize,
)
: messagesImages[index].imagePath.buildFileImage(
fit: BoxFit.cover,
width: gridItemSize,
height: gridItemSize,
),
),
).onPress(() {
navigateWithName(context, AppRoutes.mediaViewerScreen, arguments: messagesImages);
});
}
},
),
);
}
Widget buildDeliveryOptionsWidget({required RequestDeliveryOptionEnum deliveryOptionId}) {
Widget finalWidget = const SizedBox();
String finalText = "";
if (deliveryOptionId == RequestDeliveryOptionEnum.none) {
finalText = RequestDeliveryOptionEnum.selfPickup.getStringFromRequestDeliveryOptionEnum();
} else {
finalText = deliveryOptionId.getStringFromRequestDeliveryOptionEnum();
}
finalWidget = Directionality(
textDirection: TextDirection.ltr,
child: "${LocaleKeys.deliveryOptions.tr()} : $finalText".toText(
fontSize: 10,
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontWeight: MyFonts.Medium,
),
);
return finalWidget;
}
Widget messageWidgetBasedOnType({required ChatMessageTypeEnum? chatMessageTypeEnum}) {
Widget messageTypeWidget = const SizedBox();
if (chatMessageTypeEnum == null) {
return const SizedBox();
}
switch (chatMessageTypeEnum) {
case ChatMessageTypeEnum.freeText:
messageTypeWidget = Column(children: [buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum), 10.height]);
break;
case ChatMessageTypeEnum.image:
messageTypeWidget = buildImageGridWidget(messagesImages: widget.chatMessageModel.messageImages ?? []);
break;
case ChatMessageTypeEnum.audio:
case ChatMessageTypeEnum.video:
case ChatMessageTypeEnum.file:
case ChatMessageTypeEnum.offer:
messageTypeWidget = Column(
crossAxisAlignment: (widget.chatMessageModel.isMyMessage ?? false) ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum),
if (widget.requestsTypeEnum == RequestsTypeEnum.serviceRequest) ...[
2.height,
buildDeliveryOptionsWidget(deliveryOptionId: widget.chatMessageModel.reqOffer!.requestDeliveryOption ?? RequestDeliveryOptionEnum.none),
],
if (widget.chatMessageModel.reqOffer!.reqOfferImages != null && widget.chatMessageModel.reqOffer!.reqOfferImages!.isNotEmpty) ...[
5.height,
buildImagesInOffer(widget.chatMessageModel.reqOffer!.reqOfferImages!, widget.chatMessageModel.isMyMessage ?? false),
],
4.height,
buildOfferDetailsInChatMessage(requestStatusEnum: widget.requestStatusEnum!, chatMessageModel: widget.chatMessageModel, context: context),
],
);
break;
}
return messageTypeWidget.toContainer(
isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false),
backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white,
borderRadius: 0,
margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0),
);
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: (widget.chatMessageModel.isMyMessage ?? false)
? getProfilePicture()
: Container(
width: 34,
height: 34,
alignment: Alignment.center,
color: MyColors.darkTextColor,
child: ((widget.chatMessageModel.senderName ?? "").getInitials()).toText(color: MyColors.white, isBold: true, fontSize: 12),
).toCircle(borderRadius: 100),
),
10.width,
Expanded(
flex: 10,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
((widget.chatMessageModel.isMyMessage ?? false) ? LocaleKeys.you.tr() : widget.chatMessageModel.senderName ?? "").toText(
fontSize: 16,
isBold: true,
),
],
),
5.height,
messageWidgetBasedOnType(chatMessageTypeEnum: widget.chatMessageModel.chatMessageTypeEnum),
],
),
)
],
),
);
}
}