From 837191a5e10d4965b2eb51c30aa0502cdda9ab6e Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Tue, 26 Dec 2023 10:53:39 +0300 Subject: [PATCH] Chat Implementation 1.0 --- lib/classes/consts.dart | 4 + lib/config/routes.dart | 22 +- lib/models/chat_models/cat_message_model.dart | 103 ------- .../chat_models/chat_message_model.dart | 96 +++++++ .../provider_offers_model.dart | 77 +++++ lib/models/requests_models/request_model.dart | 38 +-- lib/repositories/chat_repo.dart | 63 ++++- lib/repositories/request_repo.dart | 47 +++- lib/utils/utils.dart | 8 +- lib/view_models/base_view_model.dart | 2 +- lib/view_models/chat_view_model.dart | 189 +++++++++++-- lib/view_models/requests_view_model.dart | 38 ++- lib/views/chat/chat_view.dart | 262 ++++++++++++------ lib/views/requests/offer_list_page.dart | 110 ++++---- lib/views/requests/widget/request_item.dart | 34 ++- lib/views/user/login_with_password_page.dart | 4 +- lib/widgets/checkbox_with_title_desc.dart | 48 ++++ 17 files changed, 802 insertions(+), 343 deletions(-) delete mode 100644 lib/models/chat_models/cat_message_model.dart create mode 100644 lib/models/chat_models/chat_message_model.dart create mode 100644 lib/models/requests_models/provider_offers_model.dart diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index d24672d..c215016 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -132,9 +132,13 @@ class ApiConsts { static String createRequest = "${baseUrlServices}api/RequestManagement/Request_Create"; static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get"; static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_Get"; + static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus"; + static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get"; //Chat static String chatHubUrl = "$baseUrlServices/McHub"; + static String messageIsReadUpdate = "${baseUrlServices}api/RequestManagement/ReqOfferChatIsRead_Update"; + static String getChatMessages = "${baseUrlServices}api/RequestManagement/ReqOfferChat_Get"; static List closingUrls = ["PayFortResponse"]; } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index fceeb3d..11f3232 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,4 +1,5 @@ import 'package:mc_common_app/models/requests_models/offers_model.dart'; +import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/models/requests_models/request_model.dart'; import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/utils/enums.dart'; @@ -110,17 +111,28 @@ class AppRoutes { } class ChatViewArguments { - final RequestModel? requestModel; + final int? requestId; final ChatTypeEnum chatTypeEnum; final String senderId; final String receiverId; + final int providerIndex; + final int requestIndex; - ChatViewArguments({required this.chatTypeEnum, this.requestModel, required this.senderId, required this.receiverId}); + ChatViewArguments({required this.chatTypeEnum, this.requestId, required this.senderId, required this.receiverId, required this.providerIndex, required this.requestIndex}); } + + class OfferListPageArguments { - final List offersList; - final RequestModel? requestModel; + final List serviceProviderOffers; + final int? requestId; + + OfferListPageArguments({required this.serviceProviderOffers, this.requestId}); +} + +class RequestDetailPageArguments { + final int requestIndex; + final RequestModel requestModel; - OfferListPageArguments({required this.offersList, this.requestModel}); + RequestDetailPageArguments({required this.requestIndex, required this.requestModel}); } diff --git a/lib/models/chat_models/cat_message_model.dart b/lib/models/chat_models/cat_message_model.dart deleted file mode 100644 index 09e7844..0000000 --- a/lib/models/chat_models/cat_message_model.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:mc_common_app/classes/app_state.dart'; -import 'package:mc_common_app/extensions/string_extensions.dart'; -import 'package:mc_common_app/utils/enums.dart'; - -class ChatMessageModel { - String? senderUserID; - String? senderName; - int? messageType; - ChatMessageTypeEnum? messageTypeEnum; - String? message; - RequestOffer? requestOffer; - int? requestID; - int? requestOfferID; - bool? isMyMessage; - - ChatMessageModel({ - this.senderUserID, - this.senderName, - this.messageType, - this.message, - this.requestOffer, - this.requestID, - this.requestOfferID, - this.isMyMessage = true, - }); - - ChatMessageModel.fromJson(Map json) { - final myUserId = AppState().getUser.data!.userInfo!.userId.toString(); - senderUserID = json['senderUserID']; - senderName = json['senderName']; - messageType = json['messageType']; - messageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum(); - message = json['message']; - requestOffer = json['requestOffer'] != null ? RequestOffer.fromJson(json['requestOffer']) : null; - requestID = json['requestID']; - requestOfferID = json['requestOfferID']; - isMyMessage = (json['senderUserId']).toString() == myUserId; - } - - Map toJson() { - final Map data = {}; - data['senderUserID'] = senderUserID; - data['senderName'] = senderName; - data['messageType'] = messageType; - data['message'] = message; - if (requestOffer != null) { - data['requestOffer'] = requestOffer!.toJson(); - } - data['requestID'] = requestID; - data['requestOfferID'] = requestOfferID; - return data; - } -} - -class RequestOffer { - int? id; - int? requestID; - int? serviceProviderID; - int? offerStatus; - RequestOfferStatusEnum? requestOfferStatusEnum; - String? comment; - double? price; - String? offeredItemCreatedBy; - String? offeredItemCreatedOn; - - RequestOffer({ - this.id, - this.requestID, - this.serviceProviderID, - this.offerStatus, - this.comment, - this.price, - this.offeredItemCreatedBy, - this.offeredItemCreatedOn, - this.requestOfferStatusEnum = RequestOfferStatusEnum.offer, - }); - - RequestOffer.fromJson(Map json) { - id = json['id']; - requestID = json['requestID']; - serviceProviderID = json['serviceProviderID']; - offerStatus = json['offerStatus']; - offerStatus = json['offerStatus']; - requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum(); - comment = json['comment']; - price = json['price']; - offeredItemCreatedBy = json['offeredItemCreatedBy']; - offeredItemCreatedOn = json['offeredItemCreatedOn']; - } - - Map toJson() { - final Map data = {}; - data['id'] = id; - data['requestID'] = requestID; - data['serviceProviderID'] = serviceProviderID; - data['offerStatus'] = offerStatus; - data['comment'] = comment; - data['price'] = price; - data['offeredItemCreatedBy'] = offeredItemCreatedBy; - data['offeredItemCreatedOn'] = offeredItemCreatedOn; - return data; - } -} diff --git a/lib/models/chat_models/chat_message_model.dart b/lib/models/chat_models/chat_message_model.dart new file mode 100644 index 0000000..4121541 --- /dev/null +++ b/lib/models/chat_models/chat_message_model.dart @@ -0,0 +1,96 @@ +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/utils/enums.dart'; + +class ChatMessageModel { + int? id; + String? senderUserID; + String? senderName; + int? messageType; + ChatMessageTypeEnum? chatMessageTypeEnum; + String? chatText; + int? requestID; + int? reqOfferID; + int? serviceProviderID; + int? offerStatus; + ReqOffer? reqOffer; + bool? isRead; + String? readOn; + bool? isMyMessage; + + ChatMessageModel({ + this.id, + this.senderUserID, + this.senderName, + this.messageType, + this.chatMessageTypeEnum, + this.chatText, + this.requestID, + this.reqOfferID, + this.serviceProviderID, + this.offerStatus, + this.reqOffer, + this.isRead, + this.readOn, + this.isMyMessage, + }); + + ChatMessageModel.fromJson(Map json) { + final myUserId = AppState().getUser.data!.userInfo!.userId.toString(); + id = json['id']; + senderUserID = json['senderUserID']; + senderName = json['senderName']; + messageType = json['messageType']; + chatMessageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum(); + chatText = json['chatText']; + requestID = json['requestID']; + reqOfferID = json['reqOfferID']; + serviceProviderID = json['serviceProviderID']; + offerStatus = json['offerStatus']; + reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null; + isRead = json['isRead']; + readOn = json['readOn']; + isMyMessage = (json['senderUserId']).toString() == myUserId; + } +} + +class ReqOffer { + int? id; + int? requestID; + int? serviceProviderID; + int? offerStatus; + String? offerStatusText; + String? comment; + double? price; + RequestOfferStatusEnum? requestOfferStatusEnum; + + ReqOffer({ + this.id, + this.requestID, + this.serviceProviderID, + this.offerStatus, + this.offerStatusText, + this.comment, + this.price, + this.requestOfferStatusEnum, + }); + + ReqOffer.fromJson(Map json) { + id = json['id']; + requestID = json['requestID']; + serviceProviderID = json['serviceProviderID']; + offerStatus = json['offerStatus']; + offerStatusText = json['offerStatusText']; + comment = json['comment']; + price = json['price']; + requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum(); + } +} + +class OfferRequestCommentModel { + int? index; + String? title; + bool? isSelected; + + OfferRequestCommentModel({this.index, this.title, this.isSelected}); +} diff --git a/lib/models/requests_models/provider_offers_model.dart b/lib/models/requests_models/provider_offers_model.dart new file mode 100644 index 0000000..2b68877 --- /dev/null +++ b/lib/models/requests_models/provider_offers_model.dart @@ -0,0 +1,77 @@ +import 'dart:developer'; + +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; + +class ProviderOffersModel { + int? id; + int? customerID; + int? requestType; + int? requestStatus; + String? brand; + String? model; + int? year; + bool? isNew; + String? description; + double? price; + int? spOfferCount; + List? serviceProviders; + + ProviderOffersModel({ + this.id, + this.customerID, + this.requestType, + this.requestStatus, + this.brand, + this.model, + this.year, + this.isNew, + this.description, + this.price, + this.spOfferCount, + this.serviceProviders, + }); + + ProviderOffersModel.fromJson(Map json) { + id = json['id']; + customerID = json['customerID']; + requestType = json['requestType']; + requestStatus = json['requestStatus']; + brand = json['brand']; + model = json['model']; + year = json['year']; + isNew = json['isNew']; + description = json['description']; + price = json['price']; + spOfferCount = json['spOfferCount']; + if (json['serviceProviders'] != null) { + serviceProviders = []; + json['serviceProviders'].forEach((v) { + serviceProviders!.add(ServiceProvidersOffers.fromJson(v)); + }); + } + } +} + +class ServiceProvidersOffers { + String? providerUserId; + int? providerId; + String? name; + String? mobileNo; + String? email; + String? companyName; + int? offerCount; + List? chatMessages; + + ServiceProvidersOffers({this.providerId, this.name, this.mobileNo, this.email, this.companyName, this.offerCount, this.chatMessages, this.providerUserId}); + + ServiceProvidersOffers.fromJson(Map json) { + providerId = json['providerId']; + providerUserId = json['providerUserId'] ?? "c680271c-b2a7-4ecf-7e95-08db545aec1b"; //TODO Remove this when parameter is added in backend + name = json['name']; + mobileNo = json['mobileNo']; + email = json['email']; + companyName = json['companyName']; + offerCount = json['offerCount']; + chatMessages = []; + } +} diff --git a/lib/models/requests_models/request_model.dart b/lib/models/requests_models/request_model.dart index 5ee653e..aedb089 100644 --- a/lib/models/requests_models/request_model.dart +++ b/lib/models/requests_models/request_model.dart @@ -1,4 +1,5 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/utils/enums.dart'; class RequestModel { @@ -34,6 +35,7 @@ class RequestModel { DateTime createdOn; dynamic modifiedBy; dynamic modifiedOn; + List chatMessages; RequestModel({ required this.requestType, @@ -68,6 +70,7 @@ class RequestModel { required this.createdOn, required this.modifiedBy, required this.modifiedOn, + required this.chatMessages, }); factory RequestModel.fromJson(Map json) { @@ -104,40 +107,7 @@ class RequestModel { createdOn: DateTime.parse(json["createdOn"]), modifiedBy: json["modifiedBy"], modifiedOn: json["modifiedOn"], + chatMessages: [], ); } - - Map toJson() => { - "requestType": requestType, - "requestTypeName": requestTypeName, - "requestStatusName": requestStatusName, - "requestStatus": requestStatus, - "cityName": cityName, - "vehicleTypeName": vehicleTypeName, - "countryName": countryName, - "customerName": customerName, - "serviceProviders": serviceProviders, - "offerCount": offerCount, - "id": id, - "customerID": customerId, - "customer": customer, - "brand": brand, - "model": model, - "year": year, - "isNew": isNew, - "description": description, - "requestImages": List.from(requestImages.map((x) => x)), - "cityID": cityId, - "city": city, - "price": price, - "paymentStatus": paymentStatus, - "vehicleTypeID": vehicleTypeId, - "countryID": countryId, - "requestProviderItem": List.from(requestProviderItem.map((x) => x)), - "isActive": isActive, - "createdBy": createdBy, - "createdOn": createdOn.toIso8601String(), - "modifiedBy": modifiedBy, - "modifiedOn": modifiedOn, - }; } diff --git a/lib/repositories/chat_repo.dart b/lib/repositories/chat_repo.dart index 337a09d..66f18e1 100644 --- a/lib/repositories/chat_repo.dart +++ b/lib/repositories/chat_repo.dart @@ -1,14 +1,27 @@ +import 'dart:developer'; import 'dart:io'; import 'package:http/io_client.dart'; +import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/dependencies.dart'; import 'package:mc_common_app/main.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; +import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:signalr_core/signalr_core.dart'; abstract class ChatRepo { Future getHubConnection(); + + Future markMessageAsRead({required int messageId}); + + Future> getUsersChatMessages({required int providerId, required int requestOfferId, int pageIndex = 0, int pageSize = 0}); } class ChatRepoImp implements ChatRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + @override Future getHubConnection() async { final userId = AppState().getUser.data!.userInfo!.userId ?? ""; @@ -18,15 +31,49 @@ class ChatRepoImp implements ChatRepo { HubConnection hub; hub = HubConnectionBuilder() .withUrl( - hubUrl, - HttpConnectionOptions( - client: IOClient(HttpClient() - ..badCertificateCallback = (x, y, z) => true), - logging: (level, message) { - print(message); - }, - )) + hubUrl, + HttpConnectionOptions( + client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true), + logging: (level, message) { + log(message); + }, + )) .build(); return hub; } + + @override + Future markMessageAsRead({required int messageId}) async { + var queryParameters = { + "id": messageId.toString(), + }; + await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.messageIsReadUpdate, + queryParameters, + token: appState.getUser.data!.accessToken, + ); + } + + @override + Future> getUsersChatMessages({required int providerId, required int requestOfferId, int pageIndex = 0, int pageSize = 0}) async { + var queryParameters = { + "ReqOfferID": requestOfferId.toString(), + "ProviderID": providerId.toString(), + "PageSize": pageSize.toString(), + "PageIndex": pageIndex.toString(), + }; + GenericRespModel genericRespModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getChatMessages, + queryParameters: queryParameters, + token: appState.getUser.data!.accessToken, + ); + + List chatMessages = List.generate( + genericRespModel.data.length, + (index) => ChatMessageModel.fromJson(genericRespModel.data[index]), + ); + return chatMessages; + } } diff --git a/lib/repositories/request_repo.dart b/lib/repositories/request_repo.dart index e5b9967..eb9aaad 100644 --- a/lib/repositories/request_repo.dart +++ b/lib/repositories/request_repo.dart @@ -2,16 +2,23 @@ import 'package:mc_common_app/api/api_client.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependencies.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/requests_models/offers_model.dart'; +import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/models/requests_models/request_model.dart'; +import 'package:mc_common_app/utils/enums.dart'; abstract class RequestRepo { Future createRequest(Map map); Future> getOffersByRequest({required int requestId, int serviceProviderId = 0}); + Future getOffersFromProvidersByRequest({required int requestId}); + Future> getRequests(Map postParams); + + Future updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId}); } class RequestRepoImp implements RequestRepo { @@ -60,10 +67,44 @@ class RequestRepoImp implements RequestRepo { ); List offersList = List.generate( genericRespModel.data.length, - (index) => OffersModel.fromJson( - genericRespModel.data[index], - ), + (index) => OffersModel.fromJson(genericRespModel.data[index]), ); return offersList; } + + @override + Future getOffersFromProvidersByRequest({required int requestId}) async { + final customerId = appState.getUser.data!.userInfo!.customerId; + var queryParameters = { + "customerID": customerId.toString(), + "requestID": requestId.toString(), + }; + GenericRespModel genericRespModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.requestOffersSpsGet, + queryParameters, + token: appState.getUser.data!.accessToken, + ); + ProviderOffersModel providerOffersModel = ProviderOffersModel(); + if (genericRespModel.data != null && genericRespModel.data.length > 0) { + providerOffersModel = ProviderOffersModel.fromJson(genericRespModel.data.first); + } + return providerOffersModel; + } + + @override + Future updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId}) async { + var queryParameters = { + "requestOfferId": requestOfferId.toString(), + "offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum().toString(), + }; + GenericRespModel genericRespModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.updateRequestOffer, + queryParameters, + token: appState.getUser.data!.accessToken, + ); + + return genericRespModel; + } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 22e88cb..96d1afe 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -250,14 +250,18 @@ class Utils { } } - static statusContainerChip({required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor}) { + static statusContainerChip( + {required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor, Color textColor = MyColors.white}) { return Container( decoration: BoxDecoration( color: chipColor, borderRadius: const BorderRadius.all(Radius.circular(200)), ), padding: padding, - child: text.toText(fontSize: 10, color: MyColors.white)); + child: text.toText( + fontSize: 10, + color: textColor, + )); } static InputDecoration txtField(String label) { diff --git a/lib/view_models/base_view_model.dart b/lib/view_models/base_view_model.dart index 7db1451..12833c5 100644 --- a/lib/view_models/base_view_model.dart +++ b/lib/view_models/base_view_model.dart @@ -1,6 +1,6 @@ import 'package:flutter/cupertino.dart'; +import 'package:mc_common_app/utils/enums.dart'; -import '../utils/enums.dart'; class BaseVM extends ChangeNotifier { ViewState _state = ViewState.idle; diff --git a/lib/view_models/chat_view_model.dart b/lib/view_models/chat_view_model.dart index 49a1dde..fa8273c 100644 --- a/lib/view_models/chat_view_model.dart +++ b/lib/view_models/chat_view_model.dart @@ -1,22 +1,31 @@ +// ignore_for_file: use_build_context_synchronously + +import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/main.dart'; -import 'package:mc_common_app/models/chat_models/cat_message_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; +import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; +import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/repositories/chat_repo.dart'; +import 'package:mc_common_app/repositories/request_repo.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:provider/provider.dart'; import 'package:signalr_core/signalr_core.dart'; class ChatVM extends ChangeNotifier { final ChatRepo chatRepo; + final RequestRepo requestRepo; - ChatVM({required this.chatRepo}); + ChatVM({required this.chatRepo, required this.requestRepo}); late HubConnection hubConnection; - List chatMessages = []; - String chatMessageText = ""; updateChatMessageText(String value) { @@ -28,12 +37,85 @@ class ChatVM extends ChangeNotifier { notifyListeners(); } - Future onNewMessageReceived({required List messages}) async { - chatMessages.addAll(messages); + List offerRejectModelList = [ + OfferRequestCommentModel( + index: 0, + isSelected: false, + title: "I have changed my mind.", + ), + OfferRequestCommentModel( + index: 1, + isSelected: false, + title: "Very High Price.", + ), + OfferRequestCommentModel( + index: 2, + isSelected: false, + title: "Already Sold", + ), + OfferRequestCommentModel( + index: 3, + isSelected: true, + title: "Other", + ), + ]; + + void updateSelectionInOfferRejectModelList(int index) { + for (var value in offerRejectModelList) { + value.isSelected = false; + } + offerRejectModelList[index].isSelected = true; + notifyListeners(); + } + + String rejectOfferDescription = ""; + String rejectOfferDescriptionError = ""; + + void updateRejectOfferDescription(String value) { + rejectOfferDescription = value; + if (value.isNotEmpty) { + rejectOfferDescriptionError = ""; + } + } + + //REJECTING OFFER + bool isRejectOfferButtonValidated() { + bool isValidated = true; + if (rejectOfferDescription.isEmpty) { + rejectOfferDescriptionError = GlobalConsts.descriptionError; + isValidated = false; + } else { + rejectOfferDescriptionError = ""; + } + + notifyListeners(); + + return isValidated; + } + + Future onNewMessageReceived({required List messages, required BuildContext context}) async { + if (AppState().currentAppType == AppType.customer) { + for (var msg in messages) { + log("printing senderUserID: ${msg.senderUserID}"); + log("printing providerUserId: ${serviceProviderOffersList.first.providerUserId}"); + int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID); + if (providerIndex != -1) { + serviceProviderOffersList[providerIndex].chatMessages!.add(msg); + } + } + } else { + for (var msg in messages) { + int providerIndex = context.read().myFilteredRequests.indexWhere((element) => element.customerID == msg.senderUserID); + if (providerIndex != -1) { + context.read().addChatMessagesInRequestsModel(msg: msg, index: providerIndex); + } + } + } + notifyListeners(); } - Future buildHubConnection() async { + Future buildHubConnection(BuildContext context) async { // if (hubConnection.state != HubConnectionState.Connected) { try { hubConnection = await chatRepo.getHubConnection(); @@ -45,7 +127,7 @@ class ChatVM extends ChangeNotifier { final chatMessage = ChatMessageModel.fromJson(message as Map); chat.add(chatMessage); } - onNewMessageReceived(messages: chat); + onNewMessageReceived(messages: chat, context: context); logger.i("I received ping : ${arguments.toString()}"); }); } catch (e) { @@ -62,9 +144,10 @@ class ChatVM extends ChangeNotifier { required String message, required int requestId, required String offerPrice, + required BuildContext context, }) async { if (hubConnection.state != HubConnectionState.connected) { - await buildHubConnection(); + await buildHubConnection(context); } if (hubConnection.state == HubConnectionState.connected) { final providerId = AppState().getUser.data!.userInfo!.providerId; @@ -74,10 +157,10 @@ class ChatVM extends ChangeNotifier { { "ReceiverUserID": receiverId, "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), - "Message": message, + "ChatText": message, "RequestID": requestId, - "RequestOfferID": 0, - "RequestOffer": { + "ReqOfferID": 0, + "ReqOffer": { "RequestID": requestId, "Price": double.parse(offerPrice), "ServiceProviderID": providerId, @@ -101,23 +184,29 @@ class ChatVM extends ChangeNotifier { required String message, required int requestId, required String offerPrice, + required BuildContext context, }) async { if (message.isEmpty) return false; if (hubConnection.state != HubConnectionState.connected) { - await buildHubConnection(); + await buildHubConnection(context); } if (hubConnection.state == HubConnectionState.connected) { final userId = AppState().getUser.data!.userInfo!.userId.toString(); final name = AppState().getUser.data!.userInfo!.firstName.toString(); + + log("here the sender: $userId"); + log("here the receiver: $receiverId"); + hubConnection.invoke( "SendMessageRequestOffer", args: [ { "ReceiverUserID": receiverId, "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), - "Message": message, + "ChatText": message, "RequestID": requestId, + "ReqOfferID": 0, } ], ).catchError((e) { @@ -127,15 +216,83 @@ class ChatVM extends ChangeNotifier { }); ChatMessageModel chatMessageModel = ChatMessageModel( messageType: chatMessageType.getIdFromChatMessageTypeEnum(), - message: message, + chatText: message, isMyMessage: true, requestID: requestId, senderName: name, senderUserID: userId, ); - onNewMessageReceived(messages: [chatMessageModel]); + if (AppState().currentAppType == AppType.customer) { + int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == receiverId); + if (providerIndex != -1) { + serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); + } + } else { + int providerIndex = context.read().myFilteredRequests.indexWhere((element) => element.customerID == userId); + if (providerIndex != -1) { + context.read().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); + } + } } return true; } + + List serviceProviderOffersList = []; + + Future getOffersFromProvidersByRequest({required int requestId, required BuildContext context}) async { + try { + Utils.showLoading(context); + ProviderOffersModel providerOffersModel = await requestRepo.getOffersFromProvidersByRequest(requestId: requestId); + Utils.hideLoading(context); + serviceProviderOffersList.clear(); + serviceProviderOffersList = providerOffersModel.serviceProviders ?? []; + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } + + Future getUsersChatMessagesForCustomer({required BuildContext context, required int providerId, required int requestOfferId, required int providerOfferIndex}) async { + try { + Utils.showLoading(context); + List chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, requestOfferId: requestOfferId); + serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages; + Utils.hideLoading(context); + notifyListeners(); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } + + Future getUsersChatMessagesForProvider({required BuildContext context, required int providerId, required int requestOfferId, required int customerRequestIndex}) async { + try { + Utils.showLoading(context); + List chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, requestOfferId: requestOfferId); + context.read().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex); + Utils.hideLoading(context); + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + } + } + + Future onActionOfferTapped({required BuildContext context, required RequestOfferStatusEnum requestOfferStatusEnum, required int reqOfferId}) async { + try { + Utils.showLoading(context); + GenericRespModel genericRespModel = await requestRepo.updateOfferRequestStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId); + Utils.hideLoading(context); + return genericRespModel.messageStatus == 1 ? reqOfferId : -1; + } catch (e) { + logger.i(e.toString()); + Utils.showToast(e.toString()); + Utils.hideLoading(context); + return -1; + } + } } diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 2a8f583..4ee5cab 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -9,7 +9,7 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart'; -import 'package:mc_common_app/models/chat_models/cat_message_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/general_models/enums_model.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/requests_models/offers_model.dart'; @@ -87,6 +87,17 @@ class RequestsVM extends BaseVM { notifyListeners(); } + addChatMessagesInRequestsModel({required ChatMessageModel msg, required int index}) { + myFilteredRequests[index].chatMessages.add(msg); + notifyListeners(); + } + + overwriteChatMessagesInRequestsModel({required List messages, required int index}) { + myFilteredRequests[index].chatMessages.clear(); + myFilteredRequests[index].chatMessages = messages; + notifyListeners(); + } + applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) { if (requestsTypeFilterOptions.isEmpty) return; for (var value in requestsTypeFilterOptions) { @@ -397,15 +408,18 @@ class RequestsVM extends BaseVM { required int requestId, required String offerPrice, required RequestModel requestModel, + required int requestIndex, }) async { if (isSendOfferValidated()) { - bool status = await context.read().onSendMessageForRequestOffer( - receiverId: receiverId, - chatMessageType: ChatMessageTypeEnum.offer, - message: message, - requestId: requestId, - offerPrice: offerPrice, - ); + final chatVM = context.read(); + bool status = await chatVM.onSendMessageForRequestOffer( + receiverId: receiverId, + chatMessageType: ChatMessageTypeEnum.offer, + message: message, + requestId: requestId, + offerPrice: offerPrice, + context: context, + ); if (status) { final senderName = AppState().getUser.data!.userInfo!.firstName; @@ -414,17 +428,19 @@ class RequestsVM extends BaseVM { Navigator.pop(context); ChatMessageModel chatMessageModel = ChatMessageModel( isMyMessage: true, - message: message, + chatText: message, messageType: ChatMessageTypeEnum.freeText.getIdFromChatMessageTypeEnum(), senderName: senderName, senderUserID: senderId, ); - context.read().onNewMessageReceived(messages: [chatMessageModel]); + context.read().onNewMessageReceived(messages: [chatMessageModel], context: context); ChatViewArguments chatViewArguments = ChatViewArguments( chatTypeEnum: ChatTypeEnum.requestOffer, - requestModel: requestModel, + requestId: requestModel.id, receiverId: requestModel.customerID, senderId: senderId ?? "", + requestIndex: requestIndex, + providerIndex: -1, ); navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); } diff --git a/lib/views/chat/chat_view.dart b/lib/views/chat/chat_view.dart index c2281af..1b07641 100644 --- a/lib/views/chat/chat_view.dart +++ b/lib/views/chat/chat_view.dart @@ -1,14 +1,20 @@ +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/models/chat_models/cat_message_model.dart'; +import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.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'; @@ -22,91 +28,142 @@ class ChatView extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: const CustomAppBar(title: "Chat"), - body: Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { - return Column( - children: [ - Expanded( - child: ListView.separated( - itemCount: chatVM.chatMessages.length, - separatorBuilder: (BuildContext context, int index) => 20.height, - itemBuilder: (BuildContext context, int index) { - ChatMessageModel chatMessageModel = chatVM.chatMessages[index]; - return ChatMessageCustomWidget( - isSent: chatMessageModel.isMyMessage ?? false, - profileUrl: MyAssets.bnCar, - messageText: "${chatMessageModel.message}", - messageTypeEnum: chatMessageModel.messageTypeEnum ?? ChatMessageTypeEnum.freeText, - requestOfferStatusEnum: - (chatMessageModel.requestOffer != null ? chatMessageModel.requestOffer!.requestOfferStatusEnum : RequestOfferStatusEnum.offer) ?? RequestOfferStatusEnum.offer, - senderName: "${chatMessageModel.senderName}", - ); - }).horPaddingMain(), - ), - 10.width, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: 7, - child: TxtField( - value: chatVM.chatMessageText, - hint: "Type your message here..", - keyboardType: TextInputType.text, - isNeedBorder: false, - onChanged: (v) => chatVM.updateChatMessageText(v), + body: Consumer2(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) { + final chatMessages = AppState().currentAppType == AppType.customer + ? chatVM.serviceProviderOffersList[chatViewArguments.providerIndex].chatMessages + : requestVM.myFilteredRequests[chatViewArguments.requestIndex].chatMessages; + return chatMessages == null + ? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) + : Column( + children: [ + Expanded( + child: ListView.separated( + itemCount: chatMessages.length, + separatorBuilder: (BuildContext context, int index) => 20.height, + itemBuilder: (BuildContext context, int index) { + ChatMessageModel chatMessageModel = chatMessages[index]; + return ChatMessageCustomWidget(chatMessageModel: chatMessageModel); + }).horPaddingMain(), ), - ), - Expanded( - flex: 1, - child: const Icon( - Icons.send_rounded, - color: MyColors.darkPrimaryColor, - size: 30, - ).onPress( - () async { - final status = await chatVM.onTextMessageSend( - receiverId: chatViewArguments.receiverId, - message: chatVM.chatMessageText, - requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestModel!.id : 0, - offerPrice: "0.0", - chatMessageType: ChatMessageTypeEnum.freeText, - ); + 10.width, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 7, + child: TxtField( + value: chatVM.chatMessageText, + hint: "Type your message here..", + 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.onTextMessageSend( + context: context, + receiverId: chatViewArguments.receiverId, + message: chatVM.chatMessageText, + requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestId ?? 0 : 0, + offerPrice: "0.0", + chatMessageType: ChatMessageTypeEnum.freeText, + ); - if (status) { - chatVM.clearChatMessageText(); - } - }, - ), - ) - ], - ).toContainer(isShadowEnabled: true), - ], - ); + if (status) { + chatVM.clearChatMessageText(); + } + }, + ), + ) + ], + ).toContainer(isShadowEnabled: true), + ], + ); }), // body: ); } } -class ChatMessageCustomWidget extends StatelessWidget { - final String profileUrl; - final String senderName; - final String messageText; - final ChatMessageTypeEnum messageTypeEnum; - final RequestOfferStatusEnum requestOfferStatusEnum; - final bool isSent; +class ChatMessageCustomWidget extends StatefulWidget { + final ChatMessageModel chatMessageModel; + + const ChatMessageCustomWidget({super.key, required this.chatMessageModel}); - const ChatMessageCustomWidget({ - super.key, - required this.profileUrl, - required this.senderName, - required this.messageText, - required this.messageTypeEnum, - this.requestOfferStatusEnum = RequestOfferStatusEnum.offer, - required this.isSent, - }); + @override + State createState() => _ChatMessageCustomWidgetState(); +} + +class _ChatMessageCustomWidgetState extends State { + Future buildRejectOfferBottomSheet() { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) { + return InfoBottomSheet( + title: "Make an offer".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, + ); + }, + ), + 12.height, + TxtField( + maxLines: 5, + value: chatVM.rejectOfferDescription, + errorValue: chatVM.rejectOfferDescriptionError, + keyboardType: TextInputType.text, + hint: "Description", + onChanged: (v) => chatVM.updateRejectOfferDescription(v), + ), + ], + ), + 25.height, + ShowFillButton( + title: "Submit", + onPressed: () {}, + maxWidth: double.infinity, + ), + 19.height, + ], + ), + )); + }); + }, + ); + } - Widget buildOfferDetailsInChatMessage({required RequestOfferStatusEnum requestOfferStatusEnum}) { + Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) { + final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; switch (requestOfferStatusEnum) { case RequestOfferStatusEnum.offer: return Column( @@ -115,7 +172,7 @@ class ChatMessageCustomWidget extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - "40000".toText(fontSize: 19, isBold: true), + chatMessageModel.reqOffer!.price.toString().toText(fontSize: 19, isBold: true), 2.width, "SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), ], @@ -130,7 +187,22 @@ class ChatMessageCustomWidget extends StatelessWidget { fontSize: 9, borderColor: MyColors.greenColor, isFilled: false, - onPressed: () {}, + onPressed: () async { + log("message: ${chatMessageModel.reqOfferID}"); + int status = await context.read().onActionOfferTapped( + context: context, + requestOfferStatusEnum: RequestOfferStatusEnum.accepted, + reqOfferId: chatMessageModel.reqOfferID ?? -1, + ); + + if (status != -1) { + log("accepted: $status"); + if (chatMessageModel.reqOfferID == status) { + chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted; + setState(() {}); + } + } + }, backgroundColor: MyColors.white, txtColor: MyColors.greenColor, ), @@ -142,10 +214,22 @@ class ChatMessageCustomWidget extends StatelessWidget { title: "Reject", borderColor: MyColors.redColor, isFilled: false, - onPressed: () {}, backgroundColor: MyColors.white, txtColor: MyColors.redColor, fontSize: 9, + onPressed: () async { + buildRejectOfferBottomSheet(); + // int status = + // await context.read().onActionOfferTapped(context: context, requestOfferStatusEnum: RequestOfferStatusEnum.rejected, reqOfferId: chatMessageModel.reqOfferID ?? -1); + // + // if (status != -1) { + // log("rejected: $status"); + // if (chatMessageModel.reqOfferID == status) { + // chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected; + // setState(() {}); + // } + // } + }, ), ) ], @@ -206,14 +290,14 @@ class ChatMessageCustomWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Directionality( - textDirection: isSent ? TextDirection.rtl : TextDirection.ltr, + textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 1, child: Image.asset( - profileUrl, + MyAssets.bnCar, width: 34, height: 34, fit: BoxFit.fill, @@ -228,7 +312,7 @@ class ChatMessageCustomWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - (isSent ? "You" : senderName).toText(fontSize: 16, isBold: true), + ((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true), ], ), 5.height, @@ -238,23 +322,23 @@ class ChatMessageCustomWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( - child: messageText.toText( - color: isSent ? MyColors.white : MyColors.lightTextColor, + child: (widget.chatMessageModel.chatText ?? "").toText( + color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, fontSize: 12, // isBold: true, ), ), ], ), - if (messageTypeEnum == ChatMessageTypeEnum.offer) ...[ - buildOfferDetailsInChatMessage(requestOfferStatusEnum: requestOfferStatusEnum), + if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[ + buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context), ], ], ).toContainer( - isShadowEnabled: !isSent, - backgroundColor: isSent ? MyColors.darkIconColor : MyColors.white, + isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false), + backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white, borderRadius: 0, - margin: EdgeInsets.fromLTRB(isSent ? 25 : 0, 0, !isSent ? 25 : 0, 0), + margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0), ), ], ), diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index d3e9eda..43badc3 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -1,16 +1,16 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.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/models/requests_models/offers_model.dart'; +import 'package:mc_common_app/models/requests_models/provider_offers_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; class OfferListPage extends StatelessWidget { final OfferListPageArguments offerListPageArguments; @@ -19,76 +19,78 @@ class OfferListPage extends StatelessWidget { @override Widget build(BuildContext context) { - final List offersList = offerListPageArguments.offersList; + final List serviceProviderOffers = offerListPageArguments.serviceProviderOffers; return Scaffold( appBar: const CustomAppBar(title: "Offers"), - body: offersList.isEmpty + body: serviceProviderOffers.isEmpty ? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) : ListView.separated( - itemCount: offersList.length, + itemCount: serviceProviderOffers.length, padding: const EdgeInsets.all(16), itemBuilder: (context, index) { - OffersModel offersModel = offersList[index]; - return Stack( - alignment: Alignment.bottomRight, + ServiceProvidersOffers offersModel = serviceProviderOffers[index]; + return Column( children: [ - Column( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (offersModel.serviceProvider!.companyName ?? "").toText(fontSize: 16, isBold: true), - - //Un read offers count - - // Center( - // child: "2".toText( - // color: Colors.white, - // isBold: true, - // fontSize: 10, - // ), - // ).toContainer( - // backgroundColor: MyColors.redColor, - // borderRadius: 100, - // paddingAll: 1, - // width: 20, - // height: 20, - // ), - ], + (offersModel.name ?? "").toText(fontSize: 16, isBold: true), + Center( + child: "${offersModel.offerCount}".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.redColor, + borderRadius: 100, + paddingAll: 1, + width: 20, + height: 20, ), - 8.height, + ], + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ Row( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: "${offersModel.comment} and This is dummy text edition.".toText( - color: MyColors.lightTextColor, - fontSize: 12, - ), + "${offersModel.companyName}".toText( + color: MyColors.lightTextColor, + fontSize: 12, ), - 12.width, - "9 Hours Ago".toText( + " | 1 hour ago".toText( color: MyColors.lightTextColor, - ) + fontSize: 12, + ), ], ), + const Icon( + Icons.arrow_forward, + color: MyColors.darkIconColor, + size: 18, + ), ], ), - const Icon( - Icons.arrow_forward, - color: MyColors.darkIconColor, - size: 18, - ), ], - ).onPress(() { - log("here: ${offersModel.serviceProvider!.providerGUID}"); + ).onPress(() async { ChatViewArguments chatViewArguments = ChatViewArguments( - chatTypeEnum: ChatTypeEnum.requestOffer, - receiverId: "${offersModel.serviceProvider!.providerGUID}", - senderId: AppState().getUser.data!.userInfo!.userId.toString(), - requestModel: offerListPageArguments.requestModel); - navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); + chatTypeEnum: ChatTypeEnum.requestOffer, + receiverId: "${offersModel.providerUserId}", + senderId: AppState().getUser.data!.userInfo!.userId.toString(), + requestId: offerListPageArguments.requestId, + providerIndex: index, + requestIndex: -1, // This will be only send in case of provider + ); + + final chatVM = context.read(); + + await chatVM.getUsersChatMessagesForCustomer(context: context, providerId: offersModel.providerId ?? 0, requestOfferId: 0, providerOfferIndex: index).whenComplete( + () => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments), + ); }).toContainer(isShadowEnabled: true); }, separatorBuilder: (context, index) => 16.height, diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart index b70fed8..dc2fb1f 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -1,12 +1,13 @@ // ignore_for_file: use_build_context_synchronously +import 'dart:developer'; + import 'package:mc_common_app/utils/enums.dart'; -import 'package:mc_common_app/view_models/requests_view_model.dart'; +import 'package:mc_common_app/view_models/chat_view_model.dart'; import 'package:flutter/material.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/models/requests_models/offers_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/navigator.dart'; @@ -17,8 +18,9 @@ import 'package:provider/provider.dart'; class RequestItem extends StatelessWidget { final RequestModel request; final AppType appType; + final int requestIndex; - const RequestItem({super.key, required this.request, required this.appType}); + const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex}); @override Widget build(BuildContext context) { @@ -80,19 +82,19 @@ class RequestItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ request.price.toString().toText( - fontSize: 20, - color: Colors.black, - isBold: true, - ), + fontSize: 20, + color: Colors.black, + isBold: true, + ), 2.width, "SAR" .toText( - color: MyColors.lightTextColor, - fontSize: 10, - ) + color: MyColors.lightTextColor, + fontSize: 10, + ) .paddingOnly( - bottom: 3, - ), + bottom: 3, + ), ], ), const Icon( @@ -105,10 +107,12 @@ class RequestItem extends StatelessWidget { ], ).toContainer(isShadowEnabled: true).onPress(() async { if (appType == AppType.provider) { - navigateWithName(context, AppRoutes.requestsDetailPage, arguments: request); + RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request); + navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments); } else { - List offers = await context.read().getOffersByRequest(requestId: request.id, context: context); - OfferListPageArguments offerListPageArguments = OfferListPageArguments(offersList: offers, requestModel: request); + ChatVM chatVM = context.read(); + await chatVM.getOffersFromProvidersByRequest(requestId: request.id, context: context); + OfferListPageArguments offerListPageArguments = OfferListPageArguments(serviceProviderOffers: chatVM.serviceProviderOffersList, requestId: request.id); navigateWithName(context, AppRoutes.offersListPage, arguments: offerListPageArguments); } }); diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 07460f7..65a6a85 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -44,8 +44,8 @@ class _LoginWithPasswordState extends State { void initState() { super.initState(); if (AppState().currentAppType == AppType.provider) { - phoneNum = "966580816976"; - password = "123@Shf"; + phoneNum = "966598646795"; + password = "Zahoor@123"; } scheduleMicrotask(() { userVM = Provider.of(context, listen: false); diff --git a/lib/widgets/checkbox_with_title_desc.dart b/lib/widgets/checkbox_with_title_desc.dart index 5419254..197c936 100644 --- a/lib/widgets/checkbox_with_title_desc.dart +++ b/lib/widgets/checkbox_with_title_desc.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class CheckBoxWithTitleDescription extends StatelessWidget { final bool isSelected; @@ -38,3 +40,49 @@ class CheckBoxWithTitleDescription extends StatelessWidget { ); } } + +class CircleCheckBoxWithTitle extends StatelessWidget { + final bool isChecked; + final String title; + final Function() onSelected; + final Color selectedColor; + + const CircleCheckBoxWithTitle({super.key, required this.title, required this.isChecked, required this.onSelected, required this.selectedColor}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 20, + width: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isChecked ? selectedColor : Colors.transparent, + border: Border.all( + color: isChecked ? Colors.transparent : MyColors.darkTextColor, + ), + ), + child: const Center(child: Icon(Icons.check_rounded, size: 16, color: MyColors.white)), + ), + 5.width, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + title.toText(fontSize: 14, isBold: false), + ], + ), + ), + ], + ).onPress((){ + onSelected(); + }), + ); + } +}