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.
905 lines
43 KiB
Dart
905 lines
43 KiB
Dart
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/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,
|
|
required 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(),
|
|
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
|
|
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
|
|
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
|
|
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}) {
|
|
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: [
|
|
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 {
|
|
if (chatVM.dealOptionsModelList[0].isSelected ?? false) {
|
|
final requestVM = context.read<RequestsVM>();
|
|
return dealCompletedConfirmationBottomSheet(
|
|
mainContext: context,
|
|
requestStatusEnum: RequestStatusEnum.completed,
|
|
requestId: requestVM.currentSelectedRequest!.id,
|
|
);
|
|
} 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(),
|
|
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
|
|
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
|
|
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
|
|
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(),
|
|
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
|
|
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
|
|
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
|
|
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("Offer Accepted");
|
|
// 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);
|
|
} 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: [
|
|
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.manufacturedById ?? "").toString());
|
|
requestVM.updateServiceItemCreatedOn((offer.manufacturedOn ?? "").toString());
|
|
requestVM.updateOfferDescription((widget.chatMessageModel.chatText ?? "").toString());
|
|
requestVM.updateIsDeliveryAvailableStatus((offer.isDeliveryAvailable ?? false));
|
|
if (offer.reqOfferImages != null && offer.reqOfferImages!.isNotEmpty) {
|
|
for (var element in offer.reqOfferImages!) {
|
|
if (element.imageUrl != null) {
|
|
ImageModel imageModel = ImageModel(id: element.id, filePath: element.imageUrl, isFromNetwork: 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.isMyMessage == true)) ...[
|
|
MyAssets.icEdit.buildSvg(color: MyColors.white, height: 15).onPress(() => onOfferEditIconPressed()),
|
|
],
|
|
Expanded(
|
|
child: Directionality(
|
|
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.ltr : TextDirection.rtl,
|
|
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 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(
|
|
children: [
|
|
buildFreeTextDetailsInMessage(chatMessageTypeEnum: chatMessageTypeEnum),
|
|
if (widget.requestsTypeEnum == RequestsTypeEnum.serviceRequest) ...[
|
|
2.height,
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
"${LocaleKeys.deliveryAvailable.tr()} : ${(widget.chatMessageModel.reqOffer!.isDeliveryAvailable ?? false) ? LocaleKeys.yes.tr() : LocaleKeys.no.tr()}".toText(
|
|
fontSize: 10,
|
|
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
|
|
fontWeight: MyFonts.Medium,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
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) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true),
|
|
],
|
|
),
|
|
5.height,
|
|
messageWidgetBasedOnType(chatMessageTypeEnum: widget.chatMessageModel.chatMessageTypeEnum),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|