Compare commits

..

No commits in common. '5eb483c3fd71cdb932df6fcb1369daea5efadc5c' and '511342adefb19975cf7c2afe91d1ed6f3ec3162b' have entirely different histories.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.9 KiB

@ -138,13 +138,9 @@ class ApiConsts {
static String createRequest = "${baseUrlServices}api/RequestManagement/Request_Create"; static String createRequest = "${baseUrlServices}api/RequestManagement/Request_Create";
static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get"; static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get";
static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_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 //Chat
static String chatHubUrl = "$baseUrlServices/McHub"; static String chatHubUrl = "$baseUrlServices/McHub";
static String messageIsReadUpdate = "${baseUrlServices}api/RequestManagement/ReqOfferChatIsRead_Update";
static String getChatMessages = "${baseUrlServices}api/RequestManagement/ReqOfferChat_Get";
static List<String> closingUrls = ["PayFortResponse"]; static List<String> closingUrls = ["PayFortResponse"];
} }
@ -261,10 +257,6 @@ class MyAssets {
static String tamaraArPng = "${assetPath}icons/payments/tamara_ar.png"; static String tamaraArPng = "${assetPath}icons/payments/tamara_ar.png";
static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png"; static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png";
static String visaPng = "${assetPath}icons/payments/visa.png"; static String visaPng = "${assetPath}icons/payments/visa.png";
static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg";
} }
RegExp numReg = RegExp(r".*[0-9].*"); RegExp numReg = RegExp(r".*[0-9].*");

@ -1,5 +1,4 @@
import 'package:mc_common_app/models/requests_models/offers_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/models/requests_models/request_model.dart';
import 'package:mc_common_app/models/user_models/register_user.dart'; import 'package:mc_common_app/models/user_models/register_user.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
@ -111,35 +110,17 @@ class AppRoutes {
} }
class ChatViewArguments { class ChatViewArguments {
final int? requestId; final RequestModel? requestModel;
final ChatTypeEnum chatTypeEnum; final ChatTypeEnum chatTypeEnum;
final String senderId; final String senderId;
final String receiverId; final String receiverId;
final int providerIndex;
final int requestIndex;
final RequestModel? requestModel;
ChatViewArguments({ ChatViewArguments({required this.chatTypeEnum, this.requestModel, required this.senderId, required this.receiverId});
required this.chatTypeEnum,
this.requestId,
required this.senderId,
required this.receiverId,
required this.providerIndex,
required this.requestIndex,
this.requestModel,
});
} }
class OfferListPageArguments { class OfferListPageArguments {
final List<ServiceProvidersOffers> serviceProviderOffers; final List<OffersModel> offersList;
final int? requestId; final RequestModel? requestModel;
OfferListPageArguments({required this.serviceProviderOffers, this.requestId});
}
class RequestDetailPageArguments {
final int requestIndex;
final RequestModel requestModel;
RequestDetailPageArguments({required this.requestIndex, required this.requestModel}); OfferListPageArguments({required this.offersList, this.requestModel});
} }

@ -28,7 +28,6 @@ extension EmailValidator on String {
decoration: isUnderLine decoration: isUnderLine
? TextDecoration.underline ? TextDecoration.underline
: textDecoration ?? TextDecoration.none, : textDecoration ?? TextDecoration.none,
fontSize: fontSize ?? 10, fontSize: fontSize ?? 10,
fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.w600, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.w600,
color: color ?? MyColors.darkTextColor, color: color ?? MyColors.darkTextColor,

@ -0,0 +1,103 @@
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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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<String, dynamic> 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<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
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;
}
}

@ -1,105 +0,0 @@
import 'dart:developer';
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,
});
@override
String toString() {
return 'ChatMessageModel{id: $id, senderUserID: $senderUserID, senderName: $senderName, messageType: $messageType, chatMessageTypeEnum: $chatMessageTypeEnum, chatText: $chatText, requestID: $requestID, reqOfferID: $reqOfferID, serviceProviderID: $serviceProviderID, offerStatus: $offerStatus, reqOffer: $reqOffer, isRead: $isRead, readOn: $readOn, isMyMessage: $isMyMessage}';
}
ChatMessageModel.fromJson(Map<String, dynamic> json) {
final myUserId = AppState().getUser.data!.userInfo!.userId.toString().toUpperCase();
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().toUpperCase()== 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<String, dynamic> json) {
log("the json: $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});
}

@ -1,77 +0,0 @@
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<ServiceProvidersOffers>? 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<String, dynamic> 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 = <ServiceProvidersOffers>[];
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<ChatMessageModel>? chatMessages;
ServiceProvidersOffers({this.providerId, this.name, this.mobileNo, this.email, this.companyName, this.offerCount, this.chatMessages, this.providerUserId});
ServiceProvidersOffers.fromJson(Map<String, dynamic> 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 = [];
}
}

@ -1,5 +1,4 @@
import 'package:mc_common_app/extensions/string_extensions.dart'; 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'; import 'package:mc_common_app/utils/enums.dart';
class RequestModel { class RequestModel {
@ -16,8 +15,6 @@ class RequestModel {
int offerCount; int offerCount;
int id; int id;
int customerId; int customerId;
bool isChatted;
String customerUserID;
dynamic customer; dynamic customer;
String brand; String brand;
String model; String model;
@ -37,7 +34,6 @@ class RequestModel {
DateTime createdOn; DateTime createdOn;
dynamic modifiedBy; dynamic modifiedBy;
dynamic modifiedOn; dynamic modifiedOn;
List<ChatMessageModel> chatMessages;
RequestModel({ RequestModel({
required this.requestType, required this.requestType,
@ -53,8 +49,6 @@ class RequestModel {
required this.offerCount, required this.offerCount,
required this.id, required this.id,
required this.customerId, required this.customerId,
required this.isChatted,
required this.customerUserID,
required this.customer, required this.customer,
required this.brand, required this.brand,
required this.model, required this.model,
@ -74,7 +68,6 @@ class RequestModel {
required this.createdOn, required this.createdOn,
required this.modifiedBy, required this.modifiedBy,
required this.modifiedOn, required this.modifiedOn,
required this.chatMessages,
}); });
factory RequestModel.fromJson(Map<String, dynamic> json) { factory RequestModel.fromJson(Map<String, dynamic> json) {
@ -92,8 +85,6 @@ class RequestModel {
offerCount: json["offerCount"], offerCount: json["offerCount"],
id: json["id"], id: json["id"],
customerId: json["customerID"], customerId: json["customerID"],
isChatted: json["isChatted"],
customerUserID: json["customerUserID"],
customer: json["customer"], customer: json["customer"],
brand: json["brand"], brand: json["brand"],
model: json["model"], model: json["model"],
@ -113,7 +104,40 @@ class RequestModel {
createdOn: DateTime.parse(json["createdOn"]), createdOn: DateTime.parse(json["createdOn"]),
modifiedBy: json["modifiedBy"], modifiedBy: json["modifiedBy"],
modifiedOn: json["modifiedOn"], modifiedOn: json["modifiedOn"],
chatMessages: [],
); );
} }
Map<String, dynamic> 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<dynamic>.from(requestImages.map((x) => x)),
"cityID": cityId,
"city": city,
"price": price,
"paymentStatus": paymentStatus,
"vehicleTypeID": vehicleTypeId,
"countryID": countryId,
"requestProviderItem": List<dynamic>.from(requestProviderItem.map((x) => x)),
"isActive": isActive,
"createdBy": createdBy,
"createdOn": createdOn.toIso8601String(),
"modifiedBy": modifiedBy,
"modifiedOn": modifiedOn,
};
} }

@ -1,27 +1,14 @@
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:http/io_client.dart'; 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/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/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'; import 'package:signalr_core/signalr_core.dart';
abstract class ChatRepo { abstract class ChatRepo {
Future<HubConnection> getHubConnection(); Future<HubConnection> getHubConnection();
Future<void> markMessageAsRead({required int messageId});
Future<List<ChatMessageModel>> getUsersChatMessages({required int providerId, required int customerId, required int requestOfferId, required int requestId, int pageIndex = 0, int pageSize = 0});
} }
class ChatRepoImp implements ChatRepo { class ChatRepoImp implements ChatRepo {
ApiClient apiClient = injector.get<ApiClient>();
AppState appState = injector.get<AppState>();
@override @override
Future<HubConnection> getHubConnection() async { Future<HubConnection> getHubConnection() async {
final userId = AppState().getUser.data!.userInfo!.userId ?? ""; final userId = AppState().getUser.data!.userInfo!.userId ?? "";
@ -36,50 +23,10 @@ class ChatRepoImp implements ChatRepo {
client: IOClient(HttpClient() client: IOClient(HttpClient()
..badCertificateCallback = (x, y, z) => true), ..badCertificateCallback = (x, y, z) => true),
logging: (level, message) { logging: (level, message) {
log(message); print(message);
}, },
)) ))
.build(); .build();
return hub; return hub;
} }
@override
Future<void> 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<List<ChatMessageModel>> getUsersChatMessages(
{required int providerId, required int customerId, required int requestOfferId, required int requestId, int pageIndex = 0, int pageSize = 0}) async {
var queryParameters = {
"ReqOfferID": requestOfferId.toString(),
"RequestID": requestId.toString(),
"ProviderID": providerId.toString(),
"CustomerID": customerId.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,
);
if (genericRespModel.messageStatus == 1 && genericRespModel.data != null) {
List<ChatMessageModel> chatMessages = List.generate(
genericRespModel.data.length,
(index) => ChatMessageModel.fromJson(genericRespModel.data[index]),
);
return chatMessages;
}
return [];
}
} }

@ -2,23 +2,16 @@ import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependencies.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/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/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/requests_models/request_model.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class RequestRepo { abstract class RequestRepo {
Future<GenericRespModel> createRequest(Map<String, dynamic> map); Future<GenericRespModel> createRequest(Map<String, dynamic> map);
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0}); Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0});
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId});
Future<List<RequestModel>> getRequests(Map<String, dynamic> postParams); Future<List<RequestModel>> getRequests(Map<String, dynamic> postParams);
Future<GenericRespModel> updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId});
} }
class RequestRepoImp implements RequestRepo { class RequestRepoImp implements RequestRepo {
@ -67,44 +60,10 @@ class RequestRepoImp implements RequestRepo {
); );
List<OffersModel> offersList = List.generate( List<OffersModel> offersList = List.generate(
genericRespModel.data.length, genericRespModel.data.length,
(index) => OffersModel.fromJson(genericRespModel.data[index]), (index) => OffersModel.fromJson(
genericRespModel.data[index],
),
); );
return offersList; return offersList;
} }
@override
Future<ProviderOffersModel> 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<GenericRespModel> 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;
}
} }

@ -250,18 +250,14 @@ class Utils {
} }
} }
static statusContainerChip( static statusContainerChip({required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor}) {
{required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor, Color textColor = MyColors.white}) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: chipColor, color: chipColor,
borderRadius: const BorderRadius.all(Radius.circular(200)), borderRadius: const BorderRadius.all(Radius.circular(200)),
), ),
padding: padding, padding: padding,
child: text.toText( child: text.toText(fontSize: 10, color: MyColors.white));
fontSize: 10,
color: textColor,
));
} }
static InputDecoration txtField(String label) { static InputDecoration txtField(String label) {

@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/utils/enums.dart';
import '../utils/enums.dart';
class BaseVM extends ChangeNotifier { class BaseVM extends ChangeNotifier {
ViewState _state = ViewState.idle; ViewState _state = ViewState.idle;

@ -1,30 +1,21 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:developer';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.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/extensions/string_extensions.dart';
import 'package:mc_common_app/main.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/chat_models/cat_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/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/enums.dart';
import 'package:mc_common_app/utils/utils.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'; import 'package:signalr_core/signalr_core.dart';
class ChatVM extends ChangeNotifier { class ChatVM extends ChangeNotifier {
final ChatRepo chatRepo; final ChatRepo chatRepo;
final RequestRepo requestRepo;
ChatVM({required this.chatRepo, required this.requestRepo}); ChatVM({required this.chatRepo});
HubConnection? hubConnection; late HubConnection hubConnection;
List<ChatMessageModel> chatMessages = [];
String chatMessageText = ""; String chatMessageText = "";
@ -37,107 +28,32 @@ class ChatVM extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
List<OfferRequestCommentModel> offerRejectModelList = [ Future<void> onNewMessageReceived({required List<ChatMessageModel> messages}) async {
OfferRequestCommentModel( chatMessages.addAll(messages);
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(); notifyListeners();
} }
String rejectOfferDescription = ""; Future<void> buildHubConnection() async {
String rejectOfferDescriptionError = ""; // if (hubConnection.state != HubConnectionState.Connected) {
try {
void updateRejectOfferDescription(String value) { hubConnection = await chatRepo.getHubConnection();
rejectOfferDescription = value; await hubConnection.start();
if (value.isNotEmpty) { hubConnection.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) {
rejectOfferDescriptionError = ""; if (arguments == null || arguments.isEmpty) return;
} List<ChatMessageModel> chat = [];
} for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
//REJECTING OFFER chat.add(chatMessage);
bool isRejectOfferButtonValidated() {
bool isValidated = true;
if (rejectOfferDescription.isEmpty) {
rejectOfferDescriptionError = GlobalConsts.descriptionError;
isValidated = false;
} else {
rejectOfferDescriptionError = "";
}
notifyListeners();
return isValidated;
}
Future<void> onNewMessageReceived({required List<ChatMessageModel> 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<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerUserID == msg.senderUserID);
log("here is it: ${msg.senderUserID}");
log("here is it: ${context.read<RequestsVM>().myFilteredRequests.first.customerUserID.toString()}");
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex);
} }
} onNewMessageReceived(messages: chat);
logger.i("I received ping : ${arguments.toString()}");
});
} catch (e) {
logger.i("Error: ${e.toString()}");
} }
notifyListeners(); notifyListeners();
} // }
Future<void> buildHubConnection(BuildContext context) async {
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
try {
hubConnection = await chatRepo.getHubConnection();
await hubConnection!.start();
hubConnection!.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
}
onNewMessageReceived(messages: chat, context: context);
logger.i("I received ping : ${arguments.toString()}");
});
} catch (e) {
logger.i("Error: ${e.toString()}");
}
notifyListeners();
}
} }
Future<bool> onSendMessageForRequestOffer({ Future<bool> onSendMessageForRequestOffer({
@ -146,23 +62,22 @@ class ChatVM extends ChangeNotifier {
required String message, required String message,
required int requestId, required int requestId,
required String offerPrice, required String offerPrice,
required BuildContext context,
}) async { }) async {
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) { if (hubConnection.state != HubConnectionState.connected) {
await buildHubConnection(context); await buildHubConnection();
} }
if (hubConnection!.state == HubConnectionState.connected) { if (hubConnection.state == HubConnectionState.connected) {
final providerId = AppState().getUser.data!.userInfo!.providerId; final providerId = AppState().getUser.data!.userInfo!.providerId;
hubConnection!.invoke( hubConnection.invoke(
"SendMessageRequestOffer", "SendMessageRequestOffer",
args: <Object>[ args: <Object>[
<String, dynamic>{ <String, dynamic>{
"ReceiverUserID": receiverId, "ReceiverUserID": receiverId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message, "Message": message,
"RequestID": requestId, "RequestID": requestId,
"ReqOfferID": 0, "RequestOfferID": 0,
"ReqOffer": <String, dynamic>{ "RequestOffer": <String, dynamic>{
"RequestID": requestId, "RequestID": requestId,
"Price": double.parse(offerPrice), "Price": double.parse(offerPrice),
"ServiceProviderID": providerId, "ServiceProviderID": providerId,
@ -186,27 +101,23 @@ class ChatVM extends ChangeNotifier {
required String message, required String message,
required int requestId, required int requestId,
required String offerPrice, required String offerPrice,
required BuildContext context,
}) async { }) async {
if (message.isEmpty) return false; if (message.isEmpty) return false;
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) { if (hubConnection.state != HubConnectionState.connected) {
await buildHubConnection(context); await buildHubConnection();
} }
if (hubConnection!.state == HubConnectionState.connected) { if (hubConnection.state == HubConnectionState.connected) {
final userId = AppState().getUser.data!.userInfo!.userId.toString(); final userId = AppState().getUser.data!.userInfo!.userId.toString();
final name = AppState().getUser.data!.userInfo!.firstName.toString(); final name = AppState().getUser.data!.userInfo!.firstName.toString();
hubConnection.invoke(
hubConnection!.invoke(
"SendMessageRequestOffer", "SendMessageRequestOffer",
args: <Object>[ args: <Object>[
<String, dynamic>{ <String, dynamic>{
"ReceiverUserID": receiverId, "ReceiverUserID": receiverId,
"SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(), "MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"ChatText": message, "Message": message,
"RequestID": requestId, "RequestID": requestId,
"ReqOfferID": 0,
} }
], ],
).catchError((e) { ).catchError((e) {
@ -216,98 +127,15 @@ class ChatVM extends ChangeNotifier {
}); });
ChatMessageModel chatMessageModel = ChatMessageModel( ChatMessageModel chatMessageModel = ChatMessageModel(
messageType: chatMessageType.getIdFromChatMessageTypeEnum(), messageType: chatMessageType.getIdFromChatMessageTypeEnum(),
chatText: message, message: message,
isMyMessage: true, isMyMessage: true,
requestID: requestId, requestID: requestId,
senderName: name, senderName: name,
senderUserID: userId, senderUserID: userId,
); );
if (AppState().currentAppType == AppType.customer) { onNewMessageReceived(messages: [chatMessageModel]);
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == receiverId);
if (providerIndex != -1) {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
}
} else {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == receiverId);
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
}
}
return true;
}
return false;
}
List<ServiceProvidersOffers> serviceProviderOffersList = [];
Future<void> 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<void> getUsersChatMessagesForCustomer({
required BuildContext context,
required int providerId,
required int requestOfferId,
required int requestId,
required int providerOfferIndex,
}) async {
try {
int customerId = AppState().getUser.data!.userInfo!.customerId!;
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages;
Utils.hideLoading(context);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
Future<void> getUsersChatMessagesForProvider({
required BuildContext context,
required int customerId,
required int requestOfferId,
required int requestId,
required int customerRequestIndex,
}) async {
try {
int providerId = AppState().getUser.data!.userInfo!.providerId!;
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex);
Utils.hideLoading(context);
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
Future<int> 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;
} }
return true;
} }
} }

@ -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/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.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/advertisment_models/vehicle_details_models.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart'; import 'package:mc_common_app/models/chat_models/cat_message_model.dart';
import 'package:mc_common_app/models/general_models/enums_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/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/offers_model.dart';
@ -87,17 +87,6 @@ class RequestsVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
addChatMessagesInRequestsModel({required ChatMessageModel msg, required int index}) {
myFilteredRequests[index].chatMessages.add(msg);
notifyListeners();
}
overwriteChatMessagesInRequestsModel({required List<ChatMessageModel> messages, required int index}) {
myFilteredRequests[index].chatMessages.clear();
myFilteredRequests[index].chatMessages = messages;
notifyListeners();
}
applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) { applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) {
if (requestsTypeFilterOptions.isEmpty) return; if (requestsTypeFilterOptions.isEmpty) return;
for (var value in requestsTypeFilterOptions) { for (var value in requestsTypeFilterOptions) {
@ -241,8 +230,7 @@ class RequestsVM extends BaseVM {
} }
//Request Management //Request Management
String price = "", String price = "", description = "";
description = "";
updatePrice(String v) { updatePrice(String v) {
price = v; price = v;
@ -255,9 +243,7 @@ class RequestsVM extends BaseVM {
Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async { Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async {
List<int> imageBytes = await file.readAsBytes(); List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes); String image = base64Encode(imageBytes);
String fileName = file.path String fileName = file.path.split('/').last;
.split('/')
.last;
VehiclePostingImages vehiclePostingImages = VehiclePostingImages( VehiclePostingImages vehiclePostingImages = VehiclePostingImages(
imageName: fileName, imageName: fileName,
imageStr: image, imageStr: image,
@ -411,20 +397,15 @@ class RequestsVM extends BaseVM {
required int requestId, required int requestId,
required String offerPrice, required String offerPrice,
required RequestModel requestModel, required RequestModel requestModel,
required int requestIndex,
required bool isFromChatScreen,
}) async { }) async {
if (isSendOfferValidated()) { if (isSendOfferValidated()) {
final chatVM = context.read<ChatVM>(); bool status = await context.read<ChatVM>().onSendMessageForRequestOffer(
bool status = await chatVM.onSendMessageForRequestOffer( receiverId: receiverId,
receiverId: receiverId, chatMessageType: ChatMessageTypeEnum.offer,
chatMessageType: ChatMessageTypeEnum.offer, message: message,
message: message, requestId: requestId,
requestId: requestId, offerPrice: offerPrice,
offerPrice: offerPrice, );
context: context,
);
if (status) { if (status) {
final senderName = AppState().getUser.data!.userInfo!.firstName; final senderName = AppState().getUser.data!.userInfo!.firstName;
@ -433,23 +414,19 @@ class RequestsVM extends BaseVM {
Navigator.pop(context); Navigator.pop(context);
ChatMessageModel chatMessageModel = ChatMessageModel( ChatMessageModel chatMessageModel = ChatMessageModel(
isMyMessage: true, isMyMessage: true,
chatText: message, message: message,
messageType: ChatMessageTypeEnum.freeText.getIdFromChatMessageTypeEnum(), messageType: ChatMessageTypeEnum.freeText.getIdFromChatMessageTypeEnum(),
senderName: senderName, senderName: senderName,
senderUserID: senderId, senderUserID: senderId,
); );
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context); context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel]);
if (!isFromChatScreen) { ChatViewArguments chatViewArguments = ChatViewArguments(
ChatViewArguments chatViewArguments = ChatViewArguments( chatTypeEnum: ChatTypeEnum.requestOffer,
chatTypeEnum: ChatTypeEnum.requestOffer, requestModel: requestModel,
requestId: requestModel.id, receiverId: requestModel.customerID,
receiverId: requestModel.customerID, senderId: senderId ?? "",
senderId: senderId ?? "", );
requestIndex: requestIndex, navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
providerIndex: -1,
);
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
}
} }
} }
} }

@ -434,19 +434,20 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
}, },
), ),
), ),
if (adDetailsModel.whatsAppNo != null && adDetailsModel.whatsAppNo!.isNotEmpty) ...[ if (adDetailsModel.whatsAppNo != null) ...[
8.width, 8.width,
Container( Container(
height: 55, height: 55,
width: 55, width: 55,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
child: MyAssets.whatsAppIcon.buildSvg() //TODO: It Will be replaced by a WhatsApp Icon
child: const Icon(Icons.message, color: MyColors.black),
).onPress(() { ).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}), }),
], ],
if (adDetailsModel.phoneNo != null && adDetailsModel.phoneNo!.isNotEmpty) ...[ if (adDetailsModel.phoneNo != null) ...[
8.width, 8.width,
Container( Container(
height: 55, height: 55,
@ -489,7 +490,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
//TODO: It Will be replaced by a WhatsApp Icon //TODO: It Will be replaced by a WhatsApp Icon
child: MyAssets.whatsAppIcon.buildSvg() child: const Icon(Icons.message, color: MyColors.black),
).onPress(() { ).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}), }),

@ -17,9 +17,14 @@ import 'package:provider/provider.dart';
class AppointmentDetailView extends StatelessWidget { class AppointmentDetailView extends StatelessWidget {
final AppointmentListModel appointmentListModel; final AppointmentListModel appointmentListModel;
const AppointmentDetailView({Key? key, required this.appointmentListModel}) : super(key: key); AppointmentDetailView({Key? key, required this.appointmentListModel})
: super(key: key);
Widget getBaseActionButtonWidget({required Color color, required String text, Color textColor = MyColors.white, required Function() onPressed}) { Widget getBaseActionButtonWidget(
{required Color color,
required String text,
Color textColor = MyColors.white,
required Function() onPressed}) {
return Expanded( return Expanded(
child: ShowFillButton( child: ShowFillButton(
maxHeight: 55, maxHeight: 55,
@ -32,47 +37,26 @@ class AppointmentDetailView extends StatelessWidget {
); );
} }
Widget getArrivedBottomActionButton({required BuildContext context, required AppointmentPaymentStatusEnum appointmentPaymentStatusEnum}) { Widget buildBottomActionButton(
switch (appointmentPaymentStatusEnum) { {required AppointmentStatusEnum appointmentStatusEnum,
case AppointmentPaymentStatusEnum.defaultStatus: required BuildContext context}) {
case AppointmentPaymentStatusEnum.paid:
case AppointmentPaymentStatusEnum.payLater:
case AppointmentPaymentStatusEnum.payPartial:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "In Progress"),
],
),
);
case AppointmentPaymentStatusEnum.payNow:
return Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Pay Now",
onPressed: () {},
backgroundColor: MyColors.darkPrimaryColor,
txtColor: MyColors.white,
fontSize: 18,
),
);
}
}
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context}) {
switch (appointmentStatusEnum) { switch (appointmentStatusEnum) {
case AppointmentStatusEnum.booked: case AppointmentStatusEnum.booked:
return Align( return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
children: [ children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
12.width, 12.width,
getBaseActionButtonWidget( getBaseActionButtonWidget(
color: MyColors.greenColor, color: MyColors.greenColor,
onPressed: () { onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id); context.read<AppointmentsVM>().onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
}, },
text: "Confirm"), text: "Confirm"),
], ],
@ -83,18 +67,36 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
children: [ children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
], ],
), ),
); );
case AppointmentStatusEnum.arrived: case AppointmentStatusEnum.arrived:
return getArrivedBottomActionButton(appointmentPaymentStatusEnum: appointmentListModel.appointmentPaymentStatusEnum ?? AppointmentPaymentStatusEnum.defaultStatus, context: context); return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "In Progress"),
],
),
);
case AppointmentStatusEnum.cancelled: case AppointmentStatusEnum.cancelled:
return Align( return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
children: [ children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"), getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "Cancelled"),
], ],
), ),
); );
@ -105,12 +107,17 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: Row( child: Row(
children: [ children: [
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
12.width, 12.width,
getBaseActionButtonWidget( getBaseActionButtonWidget(
color: MyColors.greenColor, color: MyColors.greenColor,
onPressed: () { onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id); context.read<AppointmentsVM>().onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
}, },
text: "Confirm"), text: "Confirm"),
], ],
@ -125,8 +132,10 @@ class AppointmentDetailView extends StatelessWidget {
final appointmentsVm = context.read<AppointmentsVM>(); final appointmentsVm = context.read<AppointmentsVM>();
return actionConfirmationBottomSheet( return actionConfirmationBottomSheet(
context: context, context: context,
title: "Do you want to cancel this appointment?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), title: "Do you want to cancel this appointment?"
subtitle: "Your appointment will be cancelled and you cannot undo this action.", .toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle:
"Your appointment will be cancelled and you cannot undo this action.",
actionButtonYes: Expanded( actionButtonYes: Expanded(
child: ShowFillButton( child: ShowFillButton(
maxHeight: 55, maxHeight: 55,
@ -134,7 +143,8 @@ class AppointmentDetailView extends StatelessWidget {
fontSize: 15, fontSize: 15,
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
appointmentsVm.onCancelAppointmentPressed(context: context, appointmentListModel: appointmentListModel); appointmentsVm.onCancelAppointmentPressed(
context: context, appointmentListModel: appointmentListModel);
}, },
), ),
), ),
@ -172,7 +182,8 @@ class AppointmentDetailView extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
appointmentListModel.providerName!.toText(fontSize: 16, isBold: true), appointmentListModel.providerName!
.toText(fontSize: 16, isBold: true),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -182,14 +193,23 @@ class AppointmentDetailView extends StatelessWidget {
fit: BoxFit.fill, fit: BoxFit.fill,
), ),
5.width, 5.width,
"${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}".toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor), "${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}"
.toText(
fontSize: 12,
isBold: true,
color: MyColors.lightTextColor),
], ],
), ),
13.height, 13.height,
if (appointmentListModel.appointmentServicesList != null && appointmentListModel.appointmentServicesList!.isNotEmpty) ...[ if (appointmentListModel.appointmentServicesList != null &&
appointmentListModel
.appointmentServicesList!.isNotEmpty) ...[
Column( Column(
children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) { children: List.generate(
ServiceModel service = appointmentListModel.appointmentServicesList![index]; appointmentListModel.appointmentServicesList!.length,
(index) {
ServiceModel service =
appointmentListModel.appointmentServicesList![index];
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -201,14 +221,18 @@ class AppointmentDetailView extends StatelessWidget {
// fit: BoxFit.fill, // fit: BoxFit.fill,
// ), // ),
// 10.width, // 10.width,
"${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true), "${index + 1}. ${service.providerServiceDescription}"
.toText(fontSize: 14, isBold: true),
], ],
), ),
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[ if (service.serviceItems != null &&
service.serviceItems!.isNotEmpty) ...[
Column( Column(
children: List.generate( children: List.generate(
service.serviceItems!.length, service.serviceItems!.length,
(index) => "${service.serviceItems![index].name}".toText( (index) =>
"${service.serviceItems![index].name}"
.toText(
textAlign: TextAlign.start, textAlign: TextAlign.start,
fontSize: 12, fontSize: 12,
color: MyColors.lightTextColor, color: MyColors.lightTextColor,
@ -220,15 +244,22 @@ class AppointmentDetailView extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true), ((service.currentTotalServicePrice).toString())
.toText(fontSize: 25, isBold: true),
2.width, 2.width,
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5), "SAR"
.toText(
color: MyColors.lightTextColor,
fontSize: 16,
isBold: true)
.paddingOnly(bottom: 5),
Icon( Icon(
Icons.arrow_drop_down, Icons.arrow_drop_down,
size: 30, size: 30,
) )
], ],
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)), ).onPress(() => appointmentsVM.priceBreakDownClicked(
context, service)),
], ],
); );
}), }),
@ -240,16 +271,25 @@ class AppointmentDetailView extends StatelessWidget {
CardButtonWithIcon( CardButtonWithIcon(
title: "Reschedule Appointment", title: "Reschedule Appointment",
onCardTapped: () { onCardTapped: () {
context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel); context
.read<AppointmentsVM>()
.onRescheduleAppointmentPressed(
context: context,
appointmentListModel: appointmentListModel);
}, },
icon: MyAssets.scheduleAppointmentIcon.buildSvg(), icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
), ),
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[ if (appointmentListModel.appointmentStatusEnum ==
AppointmentStatusEnum.booked) ...[
10.width, 10.width,
CardButtonWithIcon( CardButtonWithIcon(
title: "Pay for Appointment", title: "Pay for Appointment",
onCardTapped: () { onCardTapped: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id); context
.read<AppointmentsVM>()
.onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
}, },
icon: MyAssets.creditCardIcon.buildSvg(), icon: MyAssets.creditCardIcon.buildSvg(),
), ),
@ -259,7 +299,10 @@ class AppointmentDetailView extends StatelessWidget {
15.height, 15.height,
], ],
).toWhiteContainer(width: double.infinity, allPading: 12), ).toWhiteContainer(width: double.infinity, allPading: 12),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context), buildBottomActionButton(
appointmentStatusEnum:
appointmentListModel.appointmentStatusEnum!,
context: context),
], ],
), ),
), ),

@ -1,21 +1,14 @@
import 'dart:developer';
import 'package:flutter/material.dart'; 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/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; 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/models/chat_models/cat_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/theme/colors.dart';
import 'package:mc_common_app/utils/enums.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/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/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/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/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart'; import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -25,226 +18,95 @@ class ChatView extends StatelessWidget {
const ChatView({super.key, required this.chatViewArguments}); const ChatView({super.key, required this.chatViewArguments});
Future buildSendOfferBottomSheet(BuildContext context) {
RequestModel requestDetail = chatViewArguments.requestModel!;
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, RequestsVM requestsVM, 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,
TxtField(
value: requestsVM.offerPrice,
errorValue: requestsVM.offerPriceError,
keyboardType: TextInputType.number,
hint: "Enter amount",
onChanged: (v) => requestsVM.updateOfferPrice(v),
),
12.height,
TxtField(
maxLines: 5,
value: requestsVM.offerDescription,
errorValue: requestsVM.offerDescriptionError,
keyboardType: TextInputType.text,
hint: "Description",
onChanged: (v) => requestsVM.updateOfferDescription(v),
),
],
),
25.height,
ShowFillButton(
title: "Submit",
onPressed: () {
requestsVM.onSendOfferPressed(
context: context,
receiverId: requestDetail.customerID,
message: requestsVM.offerDescription,
requestId: requestDetail.id,
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: chatViewArguments.requestIndex,
isFromChatScreen: true,
);
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: const CustomAppBar(title: "Chat"), appBar: const CustomAppBar(title: "Chat"),
body: Consumer2<ChatVM, RequestsVM>(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) { body: Consumer<ChatVM>(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
final chatMessages = AppState().currentAppType == AppType.customer return Column(
? chatVM.serviceProviderOffersList[chatViewArguments.providerIndex].chatMessages children: [
: requestVM.myFilteredRequests[chatViewArguments.requestIndex].chatMessages; Expanded(
return chatMessages == null child: ListView.separated(
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) itemCount: chatVM.chatMessages.length,
: Column( separatorBuilder: (BuildContext context, int index) => 20.height,
children: [ itemBuilder: (BuildContext context, int index) {
Expanded( ChatMessageModel chatMessageModel = chatVM.chatMessages[index];
child: ListView.separated( return ChatMessageCustomWidget(
itemCount: chatMessages.length, isSent: chatMessageModel.isMyMessage ?? false,
separatorBuilder: (BuildContext context, int index) => 20.height, profileUrl: MyAssets.bnCar,
itemBuilder: (BuildContext context, int index) { messageText: "${chatMessageModel.message}",
ChatMessageModel chatMessageModel = chatMessages[index]; messageTypeEnum: chatMessageModel.messageTypeEnum ?? ChatMessageTypeEnum.freeText,
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel); requestOfferStatusEnum:
}, (chatMessageModel.requestOffer != null ? chatMessageModel.requestOffer!.requestOfferStatusEnum : RequestOfferStatusEnum.offer) ?? RequestOfferStatusEnum.offer,
).horPaddingMain(), 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),
), ),
10.height, ),
Row( Expanded(
crossAxisAlignment: CrossAxisAlignment.center, flex: 1,
children: [ child: const Icon(
if (AppState().currentAppType == AppType.provider) ...[ Icons.send_rounded,
Expanded( color: MyColors.darkPrimaryColor,
flex: 1, size: 30,
child: const Icon( ).onPress(
Icons.local_offer_rounded, () async {
color: MyColors.darkPrimaryColor, final status = await chatVM.onTextMessageSend(
size: 30, receiverId: chatViewArguments.receiverId,
).onPress( message: chatVM.chatMessageText,
() async { requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestModel!.id : 0,
buildSendOfferBottomSheet(context); offerPrice: "0.0",
}, chatMessageType: ChatMessageTypeEnum.freeText,
), );
),
],
Expanded(
flex: 8,
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 {
log("chatViewArguments.receiverId:${chatViewArguments.receiverId}");
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) { if (status) {
chatVM.clearChatMessageText(); chatVM.clearChatMessageText();
} }
}, },
), ),
) )
], ],
).toContainer(isShadowEnabled: true), ).toContainer(isShadowEnabled: true),
], ],
); );
}), }),
// body: // body:
); );
} }
} }
class ChatMessageCustomWidget extends StatefulWidget { class ChatMessageCustomWidget extends StatelessWidget {
final ChatMessageModel chatMessageModel; final String profileUrl;
final String senderName;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel}); final String messageText;
final ChatMessageTypeEnum messageTypeEnum;
@override final RequestOfferStatusEnum requestOfferStatusEnum;
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState(); final bool isSent;
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
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 ChatMessageModel chatMessageModel, required BuildContext context}) { const ChatMessageCustomWidget({
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; super.key,
required this.profileUrl,
required this.senderName,
required this.messageText,
required this.messageTypeEnum,
this.requestOfferStatusEnum = RequestOfferStatusEnum.offer,
required this.isSent,
});
Widget buildOfferDetailsInChatMessage({required RequestOfferStatusEnum requestOfferStatusEnum}) {
switch (requestOfferStatusEnum) { switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer: case RequestOfferStatusEnum.offer:
return Column( return Column(
@ -253,66 +115,48 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
"${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: MyColors.white), "40000".toText(fontSize: 19, isBold: true),
5.width, 2.width,
"SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), "SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
], ],
), ),
if (widget.chatMessageModel.isMyMessage == false) ...[ 10.height,
10.height, Row(
Row( children: [
children: [ Expanded(
Expanded( child: ShowFillButton(
child: ShowFillButton( maxHeight: 27,
maxHeight: 27, title: "Accept",
title: "Accept", fontSize: 9,
fontSize: 9, borderColor: MyColors.greenColor,
borderColor: MyColors.greenColor, isFilled: false,
isFilled: false, onPressed: () {},
onPressed: () async { backgroundColor: MyColors.white,
int status = await context.read<ChatVM>().onActionOfferTapped( txtColor: MyColors.greenColor,
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,
),
), ),
20.width, ),
Expanded( 20.width,
child: ShowFillButton( Expanded(
maxHeight: 27, child: ShowFillButton(
title: "Reject", maxHeight: 27,
borderColor: MyColors.redColor, title: "Reject",
isFilled: false, borderColor: MyColors.redColor,
backgroundColor: MyColors.white, isFilled: false,
txtColor: MyColors.redColor, onPressed: () {},
fontSize: 9, backgroundColor: MyColors.white,
onPressed: () async { txtColor: MyColors.redColor,
buildRejectOfferBottomSheet(); fontSize: 9,
}, ),
), )
) ],
], ),
),
],
], ],
); );
case RequestOfferStatusEnum.negotiate: case RequestOfferStatusEnum.negotiate:
return Column( return Column(
children: [ children: [
Center( Center(
child: "New Offer Required.".toText( child: "You asked for the new offer.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -324,7 +168,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "Offer has been accepted.".toText( child: "You have accepted the offer.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -336,7 +180,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "Offer has been rejected.".toText( child: "You have rejected the offer.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -348,7 +192,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "Offer has been cancelled.".toText( child: "You have cancelled the offer.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -362,15 +206,14 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Directionality( return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, textDirection: isSent ? TextDirection.rtl : TextDirection.ltr,
// textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
flex: 1, flex: 1,
child: Image.asset( child: Image.asset(
MyAssets.bnCar, profileUrl,
width: 34, width: 34,
height: 34, height: 34,
fit: BoxFit.fill, fit: BoxFit.fill,
@ -385,7 +228,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true), (isSent ? "You" : senderName).toText(fontSize: 16, isBold: true),
], ],
), ),
5.height, 5.height,
@ -395,27 +238,23 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: Directionality( child: messageText.toText(
textDirection: TextDirection.ltr, color: isSent ? MyColors.white : MyColors.lightTextColor,
child: (widget.chatMessageModel.chatText ?? "").toText( fontSize: 12,
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, // isBold: true,
fontSize: 12,
// isBold: true,
),
), ),
), ),
], ],
), ),
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[ if (messageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context), buildOfferDetailsInChatMessage(requestOfferStatusEnum: requestOfferStatusEnum),
], ],
], ],
).toContainer( ).toContainer(
isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false), isShadowEnabled: !isSent,
backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white, backgroundColor: isSent ? MyColors.darkIconColor : MyColors.white,
borderRadius: 0, borderRadius: 0,
margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0), margin: EdgeInsets.fromLTRB(isSent ? 25 : 0, 0, !isSent ? 25 : 0, 0),
), ),
], ],
), ),

@ -5,15 +5,12 @@ import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.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/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.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/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
class OfferListPage extends StatelessWidget { class OfferListPage extends StatelessWidget {
final OfferListPageArguments offerListPageArguments; final OfferListPageArguments offerListPageArguments;
@ -22,86 +19,76 @@ class OfferListPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final List<ServiceProvidersOffers> serviceProviderOffers = offerListPageArguments.serviceProviderOffers; final List<OffersModel> offersList = offerListPageArguments.offersList;
return Scaffold( return Scaffold(
appBar: const CustomAppBar(title: "Offers"), appBar: const CustomAppBar(title: "Offers"),
body: serviceProviderOffers.isEmpty body: offersList.isEmpty
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) ? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated( : ListView.separated(
itemCount: serviceProviderOffers.length, itemCount: offersList.length,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
itemBuilder: (context, index) { itemBuilder: (context, index) {
ServiceProvidersOffers offersModel = serviceProviderOffers[index]; OffersModel offersModel = offersList[index];
return Column( return Stack(
alignment: Alignment.bottomRight,
children: [ children: [
Row( Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
(offersModel.name ?? "").toText(fontSize: 16, isBold: true), Row(
Center( mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: "${offersModel.offerCount}".toText( crossAxisAlignment: CrossAxisAlignment.start,
color: Colors.white, children: [
isBold: true, (offersModel.serviceProvider!.companyName ?? "").toText(fontSize: 16, isBold: true),
fontSize: 10,
), //Un read offers count
).toContainer(
backgroundColor: MyColors.redColor, // Center(
borderRadius: 100, // child: "2".toText(
paddingAll: 1, // color: Colors.white,
width: 20, // isBold: true,
height: 20, // 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( Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"${offersModel.companyName}".toText( Expanded(
color: MyColors.lightTextColor, child: "${offersModel.comment} and This is dummy text edition.".toText(
fontSize: 12, color: MyColors.lightTextColor,
fontSize: 12,
),
), ),
" | 1 hour ago".toText( 12.width,
"9 Hours Ago".toText(
color: MyColors.lightTextColor, 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(() async { ).onPress(() {
log("here: ${offersModel.serviceProvider!.providerGUID}");
ChatViewArguments chatViewArguments = ChatViewArguments( ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer, chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}", receiverId: "${offersModel.serviceProvider!.providerGUID}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(), senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: offerListPageArguments.requestId, requestModel: offerListPageArguments.requestModel);
providerIndex: index, navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
requestIndex: -1, // This will be only send in case of provider
);
final chatVM = context.read<ChatVM>();
await chatVM
.getUsersChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: offerListPageArguments.requestId ?? 0,
providerOfferIndex: index,
)
.whenComplete(
() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments),
);
}).toContainer(isShadowEnabled: true); }).toContainer(isShadowEnabled: true);
}, },
separatorBuilder: (context, index) => 16.height, separatorBuilder: (context, index) => 16.height,

@ -1,13 +1,12 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:developer';
import 'package:mc_common_app/utils/enums.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:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_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/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/navigator.dart';
@ -18,9 +17,8 @@ import 'package:provider/provider.dart';
class RequestItem extends StatelessWidget { class RequestItem extends StatelessWidget {
final RequestModel request; final RequestModel request;
final AppType appType; final AppType appType;
final int requestIndex;
const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex}); const RequestItem({super.key, required this.request, required this.appType});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -82,19 +80,19 @@ class RequestItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
request.price.toString().toText( request.price.toString().toText(
fontSize: 20, fontSize: 20,
color: Colors.black, color: Colors.black,
isBold: true, isBold: true,
), ),
2.width, 2.width,
"SAR" "SAR"
.toText( .toText(
color: MyColors.lightTextColor, color: MyColors.lightTextColor,
fontSize: 10, fontSize: 10,
) )
.paddingOnly( .paddingOnly(
bottom: 3, bottom: 3,
), ),
], ],
), ),
const Icon( const Icon(
@ -107,12 +105,10 @@ class RequestItem extends StatelessWidget {
], ],
).toContainer(isShadowEnabled: true).onPress(() async { ).toContainer(isShadowEnabled: true).onPress(() async {
if (appType == AppType.provider) { if (appType == AppType.provider) {
RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request); navigateWithName(context, AppRoutes.requestsDetailPage, arguments: request);
navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments);
} else { } else {
ChatVM chatVM = context.read<ChatVM>(); List<OffersModel> offers = await context.read<RequestsVM>().getOffersByRequest(requestId: request.id, context: context);
await chatVM.getOffersFromProvidersByRequest(requestId: request.id, context: context); OfferListPageArguments offerListPageArguments = OfferListPageArguments(offersList: offers, requestModel: request);
OfferListPageArguments offerListPageArguments = OfferListPageArguments(serviceProviderOffers: chatVM.serviceProviderOffersList, requestId: request.id);
navigateWithName(context, AppRoutes.offersListPage, arguments: offerListPageArguments); navigateWithName(context, AppRoutes.offersListPage, arguments: offerListPageArguments);
} }
}); });

@ -34,9 +34,26 @@ class SettingOptionsInviteFriends extends StatelessWidget {
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () {}), CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () {}),
], ],
).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0), ).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {})
.toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0),
], ],
), ),
), ),
Row(
children: [
Expanded(
child: ShowFillButton(
borderColor: MyColors.redColor,
txtColor: MyColors.redColor,
isFilled: false,
fontSize: 16,
maxHeight: 55,
title: "Log Out",
onPressed: () {},
),
),
],
).paddingAll(21),
], ],
), ),
); );

@ -33,7 +33,7 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
ClassType type = ClassType.EMAIL; ClassType type = ClassType.EMAIL;
//TODO: ONLY FOR DEVELOPMENT PURPOSE //TODO: ONLY FOR DEVELOPMENT PURPOSE
String phoneNum = "966504278213", password = "Fa@1234"; String phoneNum = "966504278212", password = "Fa@123";
String email = ""; String email = "";
String countryCode = ""; String countryCode = "";
Country? _country; Country? _country;
@ -44,8 +44,8 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
void initState() { void initState() {
super.initState(); super.initState();
if (AppState().currentAppType == AppType.provider) { if (AppState().currentAppType == AppType.provider) {
phoneNum = "966598646795"; phoneNum = "966580816976";
password = "Zahoor@123"; password = "123@Shf";
} }
scheduleMicrotask(() { scheduleMicrotask(() {
userVM = Provider.of(context, listen: false); userVM = Provider.of(context, listen: false);

@ -1,8 +1,6 @@
import 'package:flutter/material.dart'; 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/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class CheckBoxWithTitleDescription extends StatelessWidget { class CheckBoxWithTitleDescription extends StatelessWidget {
final bool isSelected; final bool isSelected;
@ -40,49 +38,3 @@ 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();
}),
);
}
}

@ -93,10 +93,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.1" version: "1.18.0"
country_code_picker: country_code_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -513,10 +513,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.0" version: "0.18.1"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -601,26 +601,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.15" version: "0.12.16"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "0.5.0"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -910,10 +910,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
sqflite: sqflite:
dependency: transitive dependency: transitive
description: description:
@ -942,18 +942,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
stream_transform: stream_transform:
dependency: transitive dependency: transitive
description: description:
@ -998,10 +998,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.1" version: "0.6.1"
tuple: tuple:
dependency: transitive dependency: transitive
description: description:
@ -1106,6 +1106,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.3.0"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -1139,5 +1147,5 @@ packages:
source: hosted source: hosted
version: "6.3.0" version: "6.3.0"
sdks: sdks:
dart: ">=3.0.0 <4.0.0" dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"

Loading…
Cancel
Save