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/chat_view.dart

580 lines
24 KiB
Dart

import 'dart:developer';
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/utils.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/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/app_bar.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';
import 'package:easy_localization/easy_localization.dart' as lcl;
class ChatView extends StatefulWidget {
final ChatViewArguments chatViewArguments;
const ChatView({super.key, required this.chatViewArguments});
@override
State<ChatView> createState() => _ChatViewState();
}
class _ChatViewState extends State<ChatView> {
late ChatVM chatVM;
late ChatTypeEnum chatTypeEnum;
ChatViewArgumentsForRequest? chatViewArgumentsForRequest;
ChatViewArgumentsForAd? chatViewArgumentsForAd;
@override
void initState() {
chatTypeEnum = widget.chatViewArguments.chatTypeEnum;
if (chatTypeEnum == ChatTypeEnum.ads) {
chatViewArgumentsForAd = widget.chatViewArguments.chatViewArgumentsForAd;
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
chatViewArgumentsForRequest = widget.chatViewArguments.chatViewArgumentsForRequest;
}
chatVM = context.read<ChatVM>();
chatVM.isUserOnChatScreen = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (chatVM.scrollController.hasClients) {
chatVM.scrollChatDown();
}
});
super.initState();
}
@override
void dispose() {
chatVM.isUserOnChatScreen = false;
super.dispose();
}
Future buildSendOfferBottomSheet(BuildContext context) {
RequestModel requestDetail = chatViewArgumentsForRequest!.requestModel!;
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.makeAnOffer.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,
TxtField(
value: requestsVM.offerPrice,
errorValue: requestsVM.offerPriceError,
keyboardType: TextInputType.number,
hint: LocaleKeys.enterAmount.tr(),
numbersOnly: true,
onChanged: (v) => requestsVM.updateOfferPrice(v),
),
12.height,
TxtField(
maxLines: 5,
value: requestsVM.offerDescription,
errorValue: requestsVM.offerDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => requestsVM.updateOfferDescription(v),
),
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () {
requestsVM.onSendOfferPressed(
context: context,
receiverId: requestDetail.customerID,
message: requestsVM.offerDescription,
requestId: requestDetail.id,
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: chatViewArgumentsForRequest!.requestIndex,
isFromChatScreen: true,
);
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
Future<bool> onTextMessageSend() async {
bool status = false;
if (chatTypeEnum == ChatTypeEnum.requestOffer) {
status = await chatVM.onTextMessageSendForRequest(
context: context,
receiverId: chatViewArgumentsForRequest!.receiverId,
message: chatVM.chatMessageText,
requestId: chatViewArgumentsForRequest!.requestId ?? 0,
offerPrice: "0.0",
chatMessageType: ChatMessageTypeEnum.freeText,
);
} else if (chatTypeEnum == ChatTypeEnum.ads) {
} else {}
return status;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: LocaleKeys.chat.tr()),
body: Consumer2<ChatVM, RequestsVM>(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) {
List<ChatMessageModel> chatMessages = [];
if (chatTypeEnum == ChatTypeEnum.ads) {
chatMessages = chatViewArgumentsForAd!.adDetailsModel!.adMessages ?? [];
} else if (chatTypeEnum == ChatTypeEnum.requestOffer) {
if (AppState().currentAppType == AppType.customer) {
chatMessages = chatVM.serviceProviderOffersList[chatViewArgumentsForRequest!.providerIndex].chatMessages ?? [];
} else {
chatMessages = requestVM.myFilteredRequests[chatViewArgumentsForRequest!.requestIndex].chatMessages;
}
}
return Column(
children: [
Expanded(
child: chatMessages.isEmpty
? Center(child: LocaleKeys.noRequestsShow.tr().toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
controller: chatVM.scrollController,
itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel);
},
).horPaddingMain(),
),
10.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (chatTypeEnum == ChatTypeEnum.requestOffer && requestVM.currentSelectedRequest!.requestStatus == RequestStatus.inProgress) ...[
Center(
child: LocaleKeys.requestAlreadyInProgress.tr().toText(
textAlign: TextAlign.center,
color: MyColors.lightTextColor,
fontSize: 14,
),
).paddingAll(15)
] else ...[
if (AppState().currentAppType == AppType.provider && chatTypeEnum == ChatTypeEnum.requestOffer) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
buildSendOfferBottomSheet(context);
},
),
),
],
Expanded(
flex: 8,
child: TxtField(
value: chatVM.chatMessageText,
hint: LocaleKeys.typeMessageHere.tr(),
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
Expanded(
flex: 1,
child: const Icon(
Icons.send_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
final status = await chatVM.onTextMessageSendForRequest(
context: context,
receiverId: chatViewArgumentsForRequest!.receiverId,
message: chatVM.chatMessageText,
requestId: chatViewArgumentsForRequest!.requestId ?? 0,
offerPrice: "0.0",
chatMessageType: ChatMessageTypeEnum.freeText,
);
if (status) {
chatVM.scrollChatDown();
chatVM.clearChatMessageText();
}
},
),
),
],
],
).toContainer(isShadowEnabled: true),
],
);
}),
// body:
);
}
}
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Future buildRejectOfferBottomSheet({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.makeAnOffer.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,
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5),
itemBuilder: (BuildContext context, int index) {
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 context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.receiverUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Rejected");
// 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 {
log("ServiceProviderID: ${chatMessageModel.toString()}");
Navigator.pop(context);
// int status = await context.read<ChatVM>().onActionOfferTapped(
// context: context,
// requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
// reqOfferId: chatMessageModel.reqOfferID ?? -1,
// comments: GlobalConsts.acceptingThisOffer,
// );
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.receiverUserID ?? "",
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(),
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
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 buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
children: [
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${chatMessageModel.reqOffer!.price}".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: () {
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: () async {
buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel);
},
),
)
],
),
],
],
);
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: [
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: [
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: [
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)),
],
);
}
}
@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: Image.asset(
MyAssets.bnCar,
width: 34,
height: 34,
fit: BoxFit.fill,
).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,
Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
),
),
),
],
),
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context),
],
],
).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),
),
],
),
)
],
),
);
}
}