Chat Implementation 1.0

mirza_development
Faiz Hashmi 2 years ago
parent a76b8d8244
commit 01d9ad68a2

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB

@ -260,6 +260,10 @@ 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].*");

@ -117,12 +117,19 @@ class ChatViewArguments {
final String receiverId; final String receiverId;
final int providerIndex; final int providerIndex;
final int requestIndex; final int requestIndex;
final RequestModel? requestModel;
ChatViewArguments({required this.chatTypeEnum, this.requestId, required this.senderId, required this.receiverId, required this.providerIndex, required this.requestIndex});
ChatViewArguments({
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<ServiceProvidersOffers> serviceProviderOffers;
final int? requestId; final int? requestId;

@ -28,6 +28,7 @@ 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,

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
@ -35,8 +37,13 @@ class ChatMessageModel {
this.isMyMessage, 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) { ChatMessageModel.fromJson(Map<String, dynamic> json) {
final myUserId = AppState().getUser.data!.userInfo!.userId.toString(); final myUserId = AppState().getUser.data!.userInfo!.userId.toString().toUpperCase();
id = json['id']; id = json['id'];
senderUserID = json['senderUserID']; senderUserID = json['senderUserID'];
senderName = json['senderName']; senderName = json['senderName'];
@ -50,7 +57,8 @@ class ChatMessageModel {
reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null; reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null;
isRead = json['isRead']; isRead = json['isRead'];
readOn = json['readOn']; readOn = json['readOn'];
isMyMessage = (json['senderUserId']).toString() == myUserId; isMyMessage = (json['senderUserID']).toString().toUpperCase()== myUserId;
} }
} }
@ -76,6 +84,7 @@ class ReqOffer {
}); });
ReqOffer.fromJson(Map<String, dynamic> json) { ReqOffer.fromJson(Map<String, dynamic> json) {
log("the json: $json");
id = json['id']; id = json['id'];
requestID = json['requestID']; requestID = json['requestID'];
serviceProviderID = json['serviceProviderID']; serviceProviderID = json['serviceProviderID'];

@ -65,7 +65,7 @@ class ServiceProvidersOffers {
ServiceProvidersOffers({this.providerId, this.name, this.mobileNo, this.email, this.companyName, this.offerCount, this.chatMessages, this.providerUserId}); ServiceProvidersOffers({this.providerId, this.name, this.mobileNo, this.email, this.companyName, this.offerCount, this.chatMessages, this.providerUserId});
ServiceProvidersOffers.fromJson(Map<String, dynamic> json) { ServiceProvidersOffers.fromJson(Map<String, dynamic> json) {
providerId = json['providerId']; providerId = json['providerID'];
providerUserId = json['providerUserId'] ?? "c680271c-b2a7-4ecf-7e95-08db545aec1b"; //TODO Remove this when parameter is added in backend providerUserId = json['providerUserId'] ?? "c680271c-b2a7-4ecf-7e95-08db545aec1b"; //TODO Remove this when parameter is added in backend
name = json['name']; name = json['name'];
mobileNo = json['mobileNo']; mobileNo = json['mobileNo'];

@ -16,6 +16,8 @@ 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;
@ -51,6 +53,8 @@ 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,
@ -88,6 +92,8 @@ 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"],

@ -15,7 +15,7 @@ abstract class ChatRepo {
Future<void> markMessageAsRead({required int messageId}); Future<void> markMessageAsRead({required int messageId});
Future<List<ChatMessageModel>> getUsersChatMessages({required int providerId, required int requestOfferId, int pageIndex = 0, int pageSize = 0}); 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 {
@ -31,13 +31,14 @@ class ChatRepoImp implements ChatRepo {
HubConnection hub; HubConnection hub;
hub = HubConnectionBuilder() hub = HubConnectionBuilder()
.withUrl( .withUrl(
hubUrl, hubUrl,
HttpConnectionOptions( HttpConnectionOptions(
client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true), client: IOClient(HttpClient()
logging: (level, message) { ..badCertificateCallback = (x, y, z) => true),
log(message); logging: (level, message) {
}, log(message);
)) },
))
.build(); .build();
return hub; return hub;
} }
@ -48,7 +49,7 @@ class ChatRepoImp implements ChatRepo {
"id": messageId.toString(), "id": messageId.toString(),
}; };
await apiClient.postJsonForObject( await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.messageIsReadUpdate, ApiConsts.messageIsReadUpdate,
queryParameters, queryParameters,
token: appState.getUser.data!.accessToken, token: appState.getUser.data!.accessToken,
@ -56,24 +57,29 @@ class ChatRepoImp implements ChatRepo {
} }
@override @override
Future<List<ChatMessageModel>> getUsersChatMessages({required int providerId, required int requestOfferId, int pageIndex = 0, int pageSize = 0}) async { 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 = { var queryParameters = {
"ReqOfferID": requestOfferId.toString(), "ReqOfferID": requestOfferId.toString(),
"RequestID": requestId.toString(),
"ProviderID": providerId.toString(), "ProviderID": providerId.toString(),
"CustomerID": customerId.toString(),
"PageSize": pageSize.toString(), "PageSize": pageSize.toString(),
"PageIndex": pageIndex.toString(), "PageIndex": pageIndex.toString(),
}; };
GenericRespModel genericRespModel = await apiClient.getJsonForObject( GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json), (json) => GenericRespModel.fromJson(json),
ApiConsts.getChatMessages, ApiConsts.getChatMessages,
queryParameters: queryParameters, queryParameters: queryParameters,
token: appState.getUser.data!.accessToken, token: appState.getUser.data!.accessToken,
); );
if (genericRespModel.messageStatus == 1 && genericRespModel.data != null) {
List<ChatMessageModel> chatMessages = List.generate( List<ChatMessageModel> chatMessages = List.generate(
genericRespModel.data.length, genericRespModel.data.length,
(index) => ChatMessageModel.fromJson(genericRespModel.data[index]), (index) => ChatMessageModel.fromJson(genericRespModel.data[index]),
); );
return chatMessages; return chatMessages;
}
return [];
} }
} }

@ -39,10 +39,7 @@ class AppointmentsVM extends BaseVM {
final ProviderRepo providerRepo; final ProviderRepo providerRepo;
final AppointmentRepo scheduleRepo; final AppointmentRepo scheduleRepo;
AppointmentsVM({required this.commonServices, AppointmentsVM({required this.commonServices, required this.scheduleRepo, required this.providerRepo, required this.commonRepo});
required this.scheduleRepo,
required this.providerRepo,
required this.commonRepo});
bool isFetchingLists = false; bool isFetchingLists = false;
int selectedBranch = 0; int selectedBranch = 0;
@ -68,8 +65,7 @@ class AppointmentsVM extends BaseVM {
List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = []; List<ServiceAppointmentScheduleModel> serviceAppointmentScheduleList = [];
bool ifItemAlreadySelected(int id) { bool ifItemAlreadySelected(int id) {
int indexFound = allSelectedItemsInAppointments int indexFound = allSelectedItemsInAppointments.indexWhere((element) => element.id == id);
.indexWhere((element) => element.id == id);
if (indexFound != -1) { if (indexFound != -1) {
return true; return true;
} }
@ -80,22 +76,16 @@ class AppointmentsVM extends BaseVM {
setupProviderAppointmentFilter() { setupProviderAppointmentFilter() {
appointmentsFilterOptions.clear(); appointmentsFilterOptions.clear();
appointmentsFilterOptions.add( appointmentsFilterOptions.add(FilterListModel(id: 0, title: "All Appointments", isSelected: true));
FilterListModel(id: 0, title: "All Appointments", isSelected: true)); appointmentsFilterOptions.add(FilterListModel(id: 6, title: "Upcoming", isSelected: false));
appointmentsFilterOptions appointmentsFilterOptions.add(FilterListModel(id: 3, title: "Arrived", isSelected: false));
.add(FilterListModel(id: 6, title: "Upcoming", isSelected: false)); appointmentsFilterOptions.add(FilterListModel(id: 7, title: "In Progress", isSelected: false));
appointmentsFilterOptions appointmentsFilterOptions.add(FilterListModel(id: 8, title: "Completed", isSelected: false));
.add(FilterListModel(id: 3, title: "Arrived", isSelected: false));
appointmentsFilterOptions
.add(FilterListModel(id: 7, title: "In Progress", isSelected: false));
appointmentsFilterOptions
.add(FilterListModel(id: 8, title: "Completed", isSelected: false));
} }
Future<void> onItemsSelectedInService() async { Future<void> onItemsSelectedInService() async {
if (currentServiceSelection != null) { if (currentServiceSelection != null) {
int index = servicesInCurrentAppointment.indexWhere((element) => int index = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
element.serviceId == currentServiceSelection!.serviceId!);
if (index == -1) { if (index == -1) {
double totalPrice = 0.0; double totalPrice = 0.0;
@ -116,14 +106,12 @@ class AppointmentsVM extends BaseVM {
bool isSuccess = false; bool isSuccess = false;
List<int> appointmentIdsList = []; List<int> appointmentIdsList = [];
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.createServiceAppointment(
await scheduleRepo.createServiceAppointment(
schedules: serviceAppointmentScheduleList, schedules: serviceAppointmentScheduleList,
serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0, serviceProviderID: selectedBranchModel!.serviceProviderId ?? 0,
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
@ -141,17 +129,13 @@ class AppointmentsVM extends BaseVM {
} }
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.booked);
appointmentStatusEnum: AppointmentStatusEnum.booked);
Utils.hideLoading(context); Utils.hideLoading(context);
resetAfterBookingAppointment(); resetAfterBookingAppointment();
if (isSuccess) { if (isSuccess) {
if (amountToPayForAppointment > 0) { if (amountToPayForAppointment > 0) {
context context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: appointmentIdsList);
.read<PaymentVM>() navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
.updateAppointmentIdsForPayment(ids: appointmentIdsList);
navigateWithName(context, AppRoutes.paymentMethodsView,
arguments: PaymentTypes.appointment);
} else { } else {
Utils.showToast("Your appointment has been booked successfully!"); Utils.showToast("Your appointment has been booked successfully!");
getMyAppointments(); getMyAppointments();
@ -163,36 +147,28 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<void> onConfirmAppointmentPressed( Future<void> onConfirmAppointmentPressed({required BuildContext context, required appointmentId}) async {
{required BuildContext context, required appointmentId}) async { context.read<PaymentVM>().updateAppointmentIdsForPayment(ids: [appointmentId]);
context navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.appointment);
.read<PaymentVM>()
.updateAppointmentIdsForPayment(ids: [appointmentId]);
navigateWithName(context, AppRoutes.paymentMethodsView,
arguments: PaymentTypes.appointment);
} }
Future<void> onCancelAppointmentPressed({required BuildContext context, Future<void> onCancelAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context); Utils.showLoading(context);
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentListModel.id ?? 0, serviceAppointmentID: appointmentListModel.id ?? 0,
serviceSlotID: appointmentListModel.serviceSlotID ?? 0, serviceSlotID: appointmentListModel.serviceSlotID ?? 0,
appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel appointmentScheduleAction: 2, // 1 for Reschedule and 2 for Cancel
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
} }
if (genericRespModel.data == 1) { if (genericRespModel.data == 1) {
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
await getMyAppointments(); await getMyAppointments();
Utils.hideLoading(context); Utils.hideLoading(context);
@ -223,8 +199,7 @@ class AppointmentsVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
SelectionModel branchSelectedCategoryId = SelectionModel branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
void updateProviderCategoryId(SelectionModel id) { void updateProviderCategoryId(SelectionModel id) {
branchSelectedCategoryId = id; branchSelectedCategoryId = id;
@ -243,30 +218,23 @@ class AppointmentsVM extends BaseVM {
void updateBranchServiceId(SelectionModel id) async { void updateBranchServiceId(SelectionModel id) async {
branchSelectedServiceId = id; branchSelectedServiceId = id;
currentServiceSelection = branchServices.firstWhere( currentServiceSelection = branchServices.firstWhere((element) => element.serviceProviderServiceId == id.selectedId);
(element) => element.serviceProviderServiceId == id.selectedId);
notifyListeners(); notifyListeners();
} }
void removeServiceInCurrentAppointment(int index) { void removeServiceInCurrentAppointment(int index) {
int serviceId = servicesInCurrentAppointment int serviceId = servicesInCurrentAppointment.elementAt(index).serviceProviderServiceId ?? -1;
.elementAt(index) allSelectedItemsInAppointments.removeWhere((element) => element.serviceProviderServiceId == serviceId);
.serviceProviderServiceId ??
-1;
allSelectedItemsInAppointments.removeWhere(
(element) => element.serviceProviderServiceId == serviceId);
servicesInCurrentAppointment.removeAt(index); servicesInCurrentAppointment.removeAt(index);
notifyListeners(); notifyListeners();
} }
resetCategorySelectionBottomSheet() { resetCategorySelectionBottomSheet() {
selectedSubServicesCounter = 0; selectedSubServicesCounter = 0;
branchSelectedCategoryId = branchSelectedCategoryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false; isHomeTapped = false;
branchSelectedServiceId = branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
currentServiceSelection = null; currentServiceSelection = null;
} }
@ -281,32 +249,24 @@ class AppointmentsVM extends BaseVM {
populateAppointmentsFilterList() async { populateAppointmentsFilterList() async {
appointmentsFilterOptions.clear(); appointmentsFilterOptions.clear();
myAppointmentsEnum = await commonRepo.getEnumTypeValues( myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums
enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums
for (int i = 0; i < myAppointmentsEnum.length; i++) { for (int i = 0; i < myAppointmentsEnum.length; i++) {
appointmentsFilterOptions.add(FilterListModel( appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue));
title: myAppointmentsEnum[i].enumValueStr,
isSelected: false,
id: myAppointmentsEnum[i].enumValue));
} }
appointmentsFilterOptions.insert( appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
0, FilterListModel(title: "All Appointments", isSelected: true, id: 0));
notifyListeners(); notifyListeners();
} }
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM({required AppointmentStatusEnum appointmentStatusEnum, bool isNeedCustomerFilter = false}) {
{required AppointmentStatusEnum appointmentStatusEnum,
bool isNeedCustomerFilter = false}) {
if (appointmentsFilterOptions.isEmpty) return; if (appointmentsFilterOptions.isEmpty) return;
for (var value in appointmentsFilterOptions) { for (var value in appointmentsFilterOptions) {
value.isSelected = false; value.isSelected = false;
} }
appointmentsFilterOptions.forEach((element) { appointmentsFilterOptions.forEach((element) {
if (element.id == if (element.id == appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
appointmentStatusEnum.getIdFromAppointmentStatusEnum()) {
element.isSelected = true; element.isSelected = true;
} }
}); });
@ -321,11 +281,7 @@ class AppointmentsVM extends BaseVM {
return; return;
} }
myFilteredAppointments = myAppointments myFilteredAppointments = myAppointments.where((element) => element.appointmentStatusID! == appointmentStatusEnum.getIdFromAppointmentStatusEnum()).toList();
.where((element) =>
element.appointmentStatusID! ==
appointmentStatusEnum.getIdFromAppointmentStatusEnum())
.toList();
if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers(); if (isNeedCustomerFilter) findAppointmentsBasedOnCustomers();
notifyListeners(); notifyListeners();
} }
@ -341,9 +297,7 @@ class AppointmentsVM extends BaseVM {
// Create a list of CustomerData instances // Create a list of CustomerData instances
myFilteredAppointments2 = uniqueCustomerIDs.map((id) { myFilteredAppointments2 = uniqueCustomerIDs.map((id) {
List<AppointmentListModel> list = myFilteredAppointments List<AppointmentListModel> list = myFilteredAppointments.where((item) => item.customerID == id).toList();
.where((item) => item.customerID == id)
.toList();
AppointmentListModel model = list.first; AppointmentListModel model = list.first;
model.customerAppointmentList = list; model.customerAppointmentList = list;
return model; return model;
@ -368,10 +322,7 @@ class AppointmentsVM extends BaseVM {
myAppointments = await commonRepo.getMyAppointments(); myAppointments = await commonRepo.getMyAppointments();
myFilteredAppointments = myAppointments; myFilteredAppointments = myAppointments;
myUpComingAppointments = myAppointments myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.confirmed).toList();
.where((element) =>
element.appointmentStatusEnum == AppointmentStatusEnum.booked)
.toList();
setState(ViewState.idle); setState(ViewState.idle);
// applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); // applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments);
notifyListeners(); notifyListeners();
@ -379,9 +330,7 @@ class AppointmentsVM extends BaseVM {
AppointmentSlots? appointmentSlots; AppointmentSlots? appointmentSlots;
Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, Future<void> getAppointmentSlotsInfo({required Map<String, dynamic> map, required BuildContext context, bool isNeedToRebuild = false}) async {
required BuildContext context,
bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = await scheduleRepo.getAppointmentSlots(map); MResponse genericRespModel = await scheduleRepo.getAppointmentSlots(map);
@ -395,8 +344,7 @@ class AppointmentsVM extends BaseVM {
} }
} }
Future<void> getProviderMyAppointments(Map<String, dynamic> map, Future<void> getProviderMyAppointments(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
{bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
myAppointments = await scheduleRepo.getMyAppointments(map); myAppointments = await scheduleRepo.getMyAppointments(map);
@ -406,9 +354,7 @@ class AppointmentsVM extends BaseVM {
// element.appointmentStatusEnum == AppointmentStatusEnum.booked) // element.appointmentStatusEnum == AppointmentStatusEnum.booked)
// .toList(); // .toList();
setState(ViewState.idle); setState(ViewState.idle);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments, isNeedCustomerFilter: true);
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
isNeedCustomerFilter: true);
notifyListeners(); notifyListeners();
} }
@ -418,10 +364,10 @@ class AppointmentsVM extends BaseVM {
notifyListeners(); notifyListeners();
} }
updateAppointmentStatus(Map<String, dynamic> map,{bool isNeedToRebuild = false}) async { updateAppointmentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = await scheduleRepo.updateAppointmentStatus(map); MResponse genericRespModel = await scheduleRepo.updateAppointmentStatus(map);
if (genericRespModel.messageStatus == 1) { if (genericRespModel.messageStatus == 1) {
Utils.showToast("appointment status updated"); Utils.showToast("appointment status updated");
@ -433,10 +379,10 @@ class AppointmentsVM extends BaseVM {
} }
} }
updateAppointmentPaymentStatus(Map<String, dynamic> map,{bool isNeedToRebuild = false}) async { updateAppointmentPaymentStatus(Map<String, dynamic> map, {bool isNeedToRebuild = false}) async {
if (isNeedToRebuild) setState(ViewState.busy); if (isNeedToRebuild) setState(ViewState.busy);
try { try {
MResponse genericRespModel = await scheduleRepo.updateAppointmentPaymentStatus(map); MResponse genericRespModel = await scheduleRepo.updateAppointmentPaymentStatus(map);
if (genericRespModel.messageStatus == 1) { if (genericRespModel.messageStatus == 1) {
Utils.showToast("payment status updated"); Utils.showToast("payment status updated");
@ -448,55 +394,32 @@ class AppointmentsVM extends BaseVM {
} }
} }
updateSelectedAppointmentDate( updateSelectedAppointmentDate({required int dateIndex, required int scheduleIndex}) {
{required int dateIndex, required int scheduleIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
for (var element in serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList!) {
element.date!.isSelected = false; element.date!.isSelected = false;
} }
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.isSelected = true;
.customTimeDateSlotList![dateIndex]
.date!
.isSelected = true;
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex; serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex = dateIndex;
final date = TimeSlotModel( final date = TimeSlotModel(
date: serviceAppointmentScheduleList[scheduleIndex] date: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.date,
.customTimeDateSlotList![dateIndex] slotId: serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![dateIndex].date!.slotId,
.date!
.date,
slotId: serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![dateIndex]
.date!
.slotId,
isSelected: true, isSelected: true,
slot: "", slot: "",
); );
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
.selectedCustomTimeDateSlotModel = CustomTimeDateSlotModel(date: date);
notifyListeners(); notifyListeners();
} }
updateSelectedAppointmentSlotByDate( updateSelectedAppointmentSlotByDate({required int scheduleIndex, required int slotIndex}) {
{required int scheduleIndex, required int slotIndex}) { for (var element in serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList!) {
for (var element in serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList!) {
for (var element in element.availableSlots!) { for (var element in element.availableSlots!) {
element.isSelected = false; element.isSelected = false;
} }
} }
int index = int index = serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!;
serviceAppointmentScheduleList[scheduleIndex].selectedDateIndex!; serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots![slotIndex].isSelected = true;
serviceAppointmentScheduleList[scheduleIndex] serviceAppointmentScheduleList[scheduleIndex].selectedCustomTimeDateSlotModel!.availableSlots = serviceAppointmentScheduleList[scheduleIndex].customTimeDateSlotList![index].availableSlots!;
.customTimeDateSlotList![index]
.availableSlots![slotIndex]
.isSelected = true;
serviceAppointmentScheduleList[scheduleIndex]
.selectedCustomTimeDateSlotModel!
.availableSlots =
serviceAppointmentScheduleList[scheduleIndex]
.customTimeDateSlotList![index]
.availableSlots!;
notifyListeners(); notifyListeners();
} }
@ -510,9 +433,7 @@ class AppointmentsVM extends BaseVM {
int selectedSubServicesCounter = 0; int selectedSubServicesCounter = 0;
onItemUpdateOrSelected(int index, bool selected, int itemId) { onItemUpdateOrSelected(int index, bool selected, int itemId) {
int serviceIndex = servicesInCurrentAppointment.indexWhere( int serviceIndex = servicesInCurrentAppointment.indexWhere((element) => element.serviceId == currentServiceSelection!.serviceId!);
(element) =>
element.serviceId == currentServiceSelection!.serviceId!);
// print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}"); // print("servicesInCurrentAppointment: ${servicesInCurrentAppointment.length}");
// if (serviceIndex == -1) { // if (serviceIndex == -1) {
// return; // return;
@ -527,28 +448,19 @@ class AppointmentsVM extends BaseVM {
allSelectedItemsInAppointments.add(serviceItemsFromApi[index]); allSelectedItemsInAppointments.add(serviceItemsFromApi[index]);
for (var element in allSelectedItemsInAppointments) { for (var element in allSelectedItemsInAppointments) {
if (!ifItemAlreadySelected(element.id!)) { if (!ifItemAlreadySelected(element.id!)) {
servicesInCurrentAppointment[serviceIndex] servicesInCurrentAppointment[serviceIndex].serviceItems!.add(serviceItemsFromApi[index]);
.serviceItems!
.add(serviceItemsFromApi[index]);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex] servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice + double.parse((serviceItemsFromApi[index].price) ?? "0.0");
.currentTotalServicePrice +
double.parse((serviceItemsFromApi[index].price) ?? "0.0");
} }
} }
} }
if (!selected) { if (!selected) {
selectedSubServicesCounter = selectedSubServicesCounter - 1; selectedSubServicesCounter = selectedSubServicesCounter - 1;
currentServiceSelection!.serviceItems! currentServiceSelection!.serviceItems!.removeWhere((element) => element.id == itemId);
.removeWhere((element) => element.id == itemId); allSelectedItemsInAppointments.removeWhere((element) => element.id == itemId);
allSelectedItemsInAppointments
.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice = servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice =
servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - servicesInCurrentAppointment[serviceIndex].currentTotalServicePrice - double.parse((serviceItemsFromApi[index].price) ?? "0.0");
double.parse((serviceItemsFromApi[index].price) ?? "0.0"); servicesInCurrentAppointment[serviceIndex].serviceItems!.removeWhere((element) => element.id == itemId);
servicesInCurrentAppointment[serviceIndex]
.serviceItems!
.removeWhere((element) => element.id == itemId);
} }
notifyListeners(); notifyListeners();
} }
@ -604,8 +516,7 @@ class AppointmentsVM extends BaseVM {
String pickHomeLocationError = ""; String pickHomeLocationError = "";
String selectSubServicesError = ""; String selectSubServicesError = "";
SelectionModel branchSelectedServiceId = SelectionModel branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
bool isCategoryAlreadyPresent(int id) { bool isCategoryAlreadyPresent(int id) {
final contain = branchCategories.where((element) => element.id == id); final contain = branchCategories.where((element) => element.id == id);
@ -618,16 +529,14 @@ class AppointmentsVM extends BaseVM {
void getBranchCategories() async { void getBranchCategories() async {
for (var value in selectedBranchModel!.branchServices!) { for (var value in selectedBranchModel!.branchServices!) {
if (!isCategoryAlreadyPresent(value.categoryId!)) { if (!isCategoryAlreadyPresent(value.categoryId!)) {
branchCategories branchCategories.add(DropValue(value.categoryId!, value.categoryName!, ""));
.add(DropValue(value.categoryId!, value.categoryName!, ""));
} }
} }
notifyListeners(); notifyListeners();
} }
getBranchServices({required int categoryId}) async { getBranchServices({required int categoryId}) async {
branchSelectedServiceId = branchSelectedServiceId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
SelectionModel(selectedOption: "", selectedId: -1, errorValue: "");
isHomeTapped = false; isHomeTapped = false;
pickedHomeLocation = ""; pickedHomeLocation = "";
pickHomeLocationError = ""; pickHomeLocationError = "";
@ -640,9 +549,7 @@ class AppointmentsVM extends BaseVM {
} }
List<ServiceModel> getFilteredBranchServices({required int categoryId}) { List<ServiceModel> getFilteredBranchServices({required int categoryId}) {
List<ServiceModel> filteredServices = selectedBranchModel!.branchServices! List<ServiceModel> filteredServices = selectedBranchModel!.branchServices!.where((element) => element.categoryId == categoryId).toList();
.where((element) => element.categoryId == categoryId)
.toList();
return filteredServices; return filteredServices;
} }
@ -684,8 +591,7 @@ class AppointmentsVM extends BaseVM {
return totalPrice.toString(); return totalPrice.toString();
} }
void openTheAddServiceBottomSheet(BuildContext context, void openTheAddServiceBottomSheet(BuildContext context, AppointmentsVM appointmentsVM) {
AppointmentsVM appointmentsVM) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -696,8 +602,7 @@ class AppointmentsVM extends BaseVM {
); );
} }
void priceBreakDownClicked(BuildContext context, void priceBreakDownClicked(BuildContext context, ServiceModel selectedService) {
ServiceModel selectedService) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -713,27 +618,19 @@ class AppointmentsVM extends BaseVM {
Column( Column(
children: List.generate( children: List.generate(
selectedService.serviceItems!.length, selectedService.serviceItems!.length,
(index) => (index) => Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ "${selectedService.serviceItems![index].name}".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
"${selectedService.serviceItems![index].name}" "${selectedService.serviceItems![index].price} SAR".toText(fontSize: 12, isBold: true),
.toText( ],
fontSize: 12, ),
color: MyColors.lightTextColor,
isBold: true),
"${selectedService.serviceItems![index]
.price} SAR"
.toText(fontSize: 12, isBold: true),
],
),
), ),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
"${selectedService.currentTotalServicePrice} SAR" "${selectedService.currentTotalServicePrice} SAR".toText(fontSize: 16, isBold: true),
.toText(fontSize: 16, isBold: true),
], ],
), ),
if (selectedService.isHomeSelected) ...[ if (selectedService.isHomeSelected) ...[
@ -742,20 +639,15 @@ class AppointmentsVM extends BaseVM {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
"${totalKms}km ".toText( "${totalKms}km ".toText(fontSize: 12, color: MyColors.lightTextColor, isBold: true),
fontSize: 12, "${selectedService.rangePricePerKm} x $totalKms".toText(fontSize: 12, isBold: true),
color: MyColors.lightTextColor,
isBold: true),
"${selectedService.rangePricePerKm} x $totalKms"
.toText(fontSize: 12, isBold: true),
], ],
), ),
8.height, 8.height,
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
"${selectedService.rangePricePerKm ?? 0 * totalKms} SAR" "${selectedService.rangePricePerKm ?? 0 * totalKms} SAR".toText(fontSize: 16, isBold: true),
.toText(fontSize: 16, isBold: true),
], ],
), ),
], ],
@ -768,18 +660,11 @@ class AppointmentsVM extends BaseVM {
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
(selectedService.isHomeSelected (selectedService.isHomeSelected
? "${(selectedService.currentTotalServicePrice) + ? "${(selectedService.currentTotalServicePrice) + (double.parse((selectedService.rangePricePerKm ?? "0.0")) * totalKms)}"
(double.parse((selectedService.rangePricePerKm ?? : "${selectedService.currentTotalServicePrice}")
"0.0")) * totalKms)}"
: "${selectedService.currentTotalServicePrice}")
.toText(fontSize: 29, isBold: true), .toText(fontSize: 29, isBold: true),
2.width, 2.width,
"SAR" "SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
.toText(
color: MyColors.lightTextColor,
fontSize: 16,
isBold: true)
.paddingOnly(bottom: 5),
], ],
) )
], ],
@ -799,8 +684,7 @@ class AppointmentsVM extends BaseVM {
isValidated = false; isValidated = false;
break; break;
} }
if (schedule.selectedCustomTimeDateSlotModel!.date == null || if (schedule.selectedCustomTimeDateSlotModel!.date == null || !schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
!schedule.selectedCustomTimeDateSlotModel!.date!.isSelected) {
isValidated = false; isValidated = false;
break; break;
} else { } else {
@ -808,9 +692,7 @@ class AppointmentsVM extends BaseVM {
isValidated = false; isValidated = false;
break; break;
} else { } else {
TimeSlotModel slot = schedule TimeSlotModel slot = schedule.selectedCustomTimeDateSlotModel!.availableSlots!.firstWhere((element) => element.isSelected);
.selectedCustomTimeDateSlotModel!.availableSlots!
.firstWhere((element) => element.isSelected);
if (slot.date.isNotEmpty) { if (slot.date.isNotEmpty) {
isValidated = true; isValidated = true;
break; break;
@ -819,8 +701,7 @@ class AppointmentsVM extends BaseVM {
} }
} }
if (!isValidated) { if (!isValidated) {
Utils.showToast( Utils.showToast("You must select appointment time for each schedule's appointment.");
"You must select appointment time for each schedule's appointment.");
return; return;
} }
navigateWithName(context, AppRoutes.reviewAppointmentView); navigateWithName(context, AppRoutes.reviewAppointmentView);
@ -839,36 +720,30 @@ class AppointmentsVM extends BaseVM {
} }
} }
serviceAppointmentScheduleList = serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
); );
if (serviceAppointmentScheduleList.isEmpty) { if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast( Utils.showToast("There are no available appointments for selected Items.");
"There are no available appointments for selected Items.");
return; return;
} }
totalAmount = 0.0; totalAmount = 0.0;
amountToPayForAppointment = 0.0; amountToPayForAppointment = 0.0;
for (var schedule in serviceAppointmentScheduleList) { for (var schedule in serviceAppointmentScheduleList) {
amountToPayForAppointment = amountToPayForAppointment = amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
amountToPayForAppointment + (schedule.amountToPay ?? 0.0);
totalAmount = totalAmount + (schedule.amountTotal ?? 0.0); totalAmount = totalAmount + (schedule.amountTotal ?? 0.0);
} }
Utils.hideLoading(context); Utils.hideLoading(context);
navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, navigateWithName(context, AppRoutes.bookAppointmenSchedulesView, arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
arguments: ScreenArgumentsForAppointmentDetailPage(
routeFlag: 1, appointmentId: 0)); // 1 For Creating an Appointment
notifyListeners(); notifyListeners();
} }
Future<void> onRescheduleAppointmentPressed({required BuildContext context, Future<void> onRescheduleAppointmentPressed({required BuildContext context, required AppointmentListModel appointmentListModel}) async {
required AppointmentListModel appointmentListModel}) async {
Utils.showLoading(context); Utils.showLoading(context);
List<String> serviceItemIdsForHome = []; List<String> serviceItemIdsForHome = [];
@ -885,16 +760,14 @@ class AppointmentsVM extends BaseVM {
} }
} }
serviceAppointmentScheduleList = serviceAppointmentScheduleList = await scheduleRepo.mergeServiceIntoAvailableSchedules(
await scheduleRepo.mergeServiceIntoAvailableSchedules(
serviceItemIdsForHome: serviceItemIdsForHome, serviceItemIdsForHome: serviceItemIdsForHome,
serviceItemIdsForWorkshop: serviceItemIdsForWorkshop, serviceItemIdsForWorkshop: serviceItemIdsForWorkshop,
); );
if (serviceAppointmentScheduleList.isEmpty) { if (serviceAppointmentScheduleList.isEmpty) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast( Utils.showToast("There are no available appointments for selected Items.");
"There are no available appointments for selected Items.");
return; return;
} }
Utils.hideLoading(context); Utils.hideLoading(context);
@ -902,36 +775,29 @@ class AppointmentsVM extends BaseVM {
navigateWithName( navigateWithName(
context, context,
AppRoutes.bookAppointmenSchedulesView, AppRoutes.bookAppointmenSchedulesView,
arguments: ScreenArgumentsForAppointmentDetailPage( arguments: ScreenArgumentsForAppointmentDetailPage(routeFlag: 2, appointmentId: appointmentListModel.id ?? 0),
routeFlag: 2, appointmentId: appointmentListModel.id ?? 0),
); // 2 For Rescheduling an Appointment ); // 2 For Rescheduling an Appointment
notifyListeners(); notifyListeners();
} }
Future<void> onRescheduleAppointmentConfirmPressed( Future<void> onRescheduleAppointmentConfirmPressed({required BuildContext context, required int appointmentId, required int selectedSlotId}) async {
{required BuildContext context,
required int appointmentId,
required int selectedSlotId}) async {
Utils.showLoading(context); Utils.showLoading(context);
try { try {
GenericRespModel genericRespModel = GenericRespModel genericRespModel = await scheduleRepo.cancelOrRescheduleServiceAppointment(
await scheduleRepo.cancelOrRescheduleServiceAppointment(
serviceAppointmentID: appointmentId, serviceAppointmentID: appointmentId,
serviceSlotID: selectedSlotId, serviceSlotID: selectedSlotId,
appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel appointmentScheduleAction: 1, // 1 for Reschedule and 2 for Cancel
); );
if (genericRespModel.messageStatus == 2 || if (genericRespModel.messageStatus == 2 || genericRespModel.data == null) {
genericRespModel.data == null) {
Utils.hideLoading(context); Utils.hideLoading(context);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
return; return;
} }
if (genericRespModel.data == 1) { if (genericRespModel.data == 1) {
context.read<DashboardVmCustomer>().onNavbarTapped(1); context.read<DashboardVmCustomer>().onNavbarTapped(1);
applyFilterOnAppointmentsVM( applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.cancelled);
appointmentStatusEnum: AppointmentStatusEnum.cancelled);
Utils.showToast("${genericRespModel.message.toString()}"); Utils.showToast("${genericRespModel.message.toString()}");
getMyAppointments(); getMyAppointments();
Utils.hideLoading(context); Utils.hideLoading(context);

@ -24,7 +24,7 @@ class ChatVM extends ChangeNotifier {
ChatVM({required this.chatRepo, required this.requestRepo}); ChatVM({required this.chatRepo, required this.requestRepo});
late HubConnection hubConnection; HubConnection? hubConnection;
String chatMessageText = ""; String chatMessageText = "";
@ -105,7 +105,9 @@ class ChatVM extends ChangeNotifier {
} }
} else { } else {
for (var msg in messages) { for (var msg in messages) {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == msg.senderUserID); 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) { if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex); context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex);
} }
@ -116,26 +118,26 @@ class ChatVM extends ChangeNotifier {
} }
Future<void> buildHubConnection(BuildContext context) async { Future<void> buildHubConnection(BuildContext context) async {
// if (hubConnection.state != HubConnectionState.Connected) { if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
try { try {
hubConnection = await chatRepo.getHubConnection(); hubConnection = await chatRepo.getHubConnection();
await hubConnection.start(); await hubConnection!.start();
hubConnection.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) { hubConnection!.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return; if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = []; List<ChatMessageModel> chat = [];
for (var message in arguments) { for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>); final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage); chat.add(chatMessage);
} }
onNewMessageReceived(messages: chat, context: context); onNewMessageReceived(messages: chat, context: context);
logger.i("I received ping : ${arguments.toString()}"); logger.i("I received ping : ${arguments.toString()}");
}); });
} catch (e) { } catch (e) {
logger.i("Error: ${e.toString()}"); logger.i("Error: ${e.toString()}");
} }
notifyListeners(); notifyListeners();
// } }
} }
Future<bool> onSendMessageForRequestOffer({ Future<bool> onSendMessageForRequestOffer({
@ -146,12 +148,12 @@ class ChatVM extends ChangeNotifier {
required String offerPrice, required String offerPrice,
required BuildContext context, required BuildContext context,
}) async { }) async {
if (hubConnection.state != HubConnectionState.connected) { if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
await buildHubConnection(context); await buildHubConnection(context);
} }
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>{
@ -188,21 +190,19 @@ class ChatVM extends ChangeNotifier {
}) async { }) async {
if (message.isEmpty) return false; if (message.isEmpty) return false;
if (hubConnection.state != HubConnectionState.connected) { if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
await buildHubConnection(context); await buildHubConnection(context);
} }
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();
log("here the sender: $userId"); hubConnection!.invoke(
log("here the receiver: $receiverId");
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, "ChatText": message,
"RequestID": requestId, "RequestID": requestId,
@ -229,13 +229,14 @@ class ChatVM extends ChangeNotifier {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel); serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
} }
} else { } else {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == userId); int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == receiverId);
if (providerIndex != -1) { if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex); context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
} }
} }
return true;
} }
return true; return false;
} }
List<ServiceProvidersOffers> serviceProviderOffersList = []; List<ServiceProvidersOffers> serviceProviderOffersList = [];
@ -255,10 +256,17 @@ class ChatVM extends ChangeNotifier {
} }
} }
Future<void> getUsersChatMessagesForCustomer({required BuildContext context, required int providerId, required int requestOfferId, required int providerOfferIndex}) async { Future<void> getUsersChatMessagesForCustomer({
required BuildContext context,
required int providerId,
required int requestOfferId,
required int requestId,
required int providerOfferIndex,
}) async {
try { try {
int customerId = AppState().getUser.data!.userInfo!.customerId!;
Utils.showLoading(context); Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, requestOfferId: requestOfferId); List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages; serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages;
Utils.hideLoading(context); Utils.hideLoading(context);
notifyListeners(); notifyListeners();
@ -269,10 +277,17 @@ class ChatVM extends ChangeNotifier {
} }
} }
Future<void> getUsersChatMessagesForProvider({required BuildContext context, required int providerId, required int requestOfferId, required int customerRequestIndex}) async { Future<void> getUsersChatMessagesForProvider({
required BuildContext context,
required int customerId,
required int requestOfferId,
required int requestId,
required int customerRequestIndex,
}) async {
try { try {
int providerId = AppState().getUser.data!.userInfo!.providerId!;
Utils.showLoading(context); Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, requestOfferId: requestOfferId); List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex); context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex);
Utils.hideLoading(context); Utils.hideLoading(context);
} catch (e) { } catch (e) {

@ -241,7 +241,8 @@ class RequestsVM extends BaseVM {
} }
//Request Management //Request Management
String price = "", description = ""; String price = "",
description = "";
updatePrice(String v) { updatePrice(String v) {
price = v; price = v;
@ -254,7 +255,9 @@ 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.split('/').last; String fileName = file.path
.split('/')
.last;
VehiclePostingImages vehiclePostingImages = VehiclePostingImages( VehiclePostingImages vehiclePostingImages = VehiclePostingImages(
imageName: fileName, imageName: fileName,
imageStr: image, imageStr: image,
@ -409,6 +412,7 @@ class RequestsVM extends BaseVM {
required String offerPrice, required String offerPrice,
required RequestModel requestModel, required RequestModel requestModel,
required int requestIndex, required int requestIndex,
required bool isFromChatScreen,
}) async { }) async {
if (isSendOfferValidated()) { if (isSendOfferValidated()) {
final chatVM = context.read<ChatVM>(); final chatVM = context.read<ChatVM>();
@ -421,6 +425,7 @@ class RequestsVM extends BaseVM {
context: context, context: context,
); );
if (status) { if (status) {
final senderName = AppState().getUser.data!.userInfo!.firstName; final senderName = AppState().getUser.data!.userInfo!.firstName;
final senderId = AppState().getUser.data!.userInfo!.userId; final senderId = AppState().getUser.data!.userInfo!.userId;
@ -434,15 +439,17 @@ class RequestsVM extends BaseVM {
senderUserID: senderId, senderUserID: senderId,
); );
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context); context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context);
ChatViewArguments chatViewArguments = ChatViewArguments( if (!isFromChatScreen) {
chatTypeEnum: ChatTypeEnum.requestOffer, ChatViewArguments chatViewArguments = ChatViewArguments(
requestId: requestModel.id, chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: requestModel.customerID, requestId: requestModel.id,
senderId: senderId ?? "", receiverId: requestModel.customerID,
requestIndex: requestIndex, senderId: senderId ?? "",
providerIndex: -1, requestIndex: requestIndex,
); providerIndex: -1,
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments); );
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
}
} }
} }
} }

@ -434,20 +434,19 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
}, },
), ),
), ),
if (adDetailsModel.whatsAppNo != null) ...[ if (adDetailsModel.whatsAppNo != null && adDetailsModel.whatsAppNo!.isNotEmpty) ...[
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)),
//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 ?? "");
}), }),
], ],
if (adDetailsModel.phoneNo != null) ...[ if (adDetailsModel.phoneNo != null && adDetailsModel.phoneNo!.isNotEmpty) ...[
8.width, 8.width,
Container( Container(
height: 55, height: 55,
@ -490,7 +489,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: const Icon(Icons.message, color: MyColors.black), child: MyAssets.whatsAppIcon.buildSvg()
).onPress(() { ).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}), }),

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

@ -7,6 +7,7 @@ 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/chat_message_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/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';
@ -24,6 +25,69 @@ 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(
@ -38,19 +102,34 @@ class ChatView extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
itemCount: chatMessages.length, itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height, separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index]; ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel); return ChatMessageCustomWidget(chatMessageModel: chatMessageModel);
}).horPaddingMain(), },
).horPaddingMain(),
), ),
10.width, 10.height,
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
if (AppState().currentAppType == AppType.provider) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
buildSendOfferBottomSheet(context);
},
),
),
],
Expanded( Expanded(
flex: 7, flex: 8,
child: TxtField( child: TxtField(
value: chatVM.chatMessageText, value: chatVM.chatMessageText,
hint: "Type your message here..", hint: "Type your message here..",
@ -67,6 +146,7 @@ class ChatView extends StatelessWidget {
size: 30, size: 30,
).onPress( ).onPress(
() async { () async {
log("chatViewArguments.receiverId:${chatViewArguments.receiverId}");
final status = await chatVM.onTextMessageSend( final status = await chatVM.onTextMessageSend(
context: context, context: context,
receiverId: chatViewArguments.receiverId, receiverId: chatViewArguments.receiverId,
@ -164,6 +244,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) { Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer; final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
switch (requestOfferStatusEnum) { switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer: case RequestOfferStatusEnum.offer:
return Column( return Column(
@ -172,75 +253,66 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
chatMessageModel.reqOffer!.price.toString().toText(fontSize: 19, isBold: true), "${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: MyColors.white),
2.width, 5.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),
], ],
), ),
10.height, if (widget.chatMessageModel.isMyMessage == false) ...[
Row( 10.height,
children: [ Row(
Expanded( children: [
child: ShowFillButton( Expanded(
maxHeight: 27, child: ShowFillButton(
title: "Accept", maxHeight: 27,
fontSize: 9, title: "Accept",
borderColor: MyColors.greenColor, fontSize: 9,
isFilled: false, borderColor: MyColors.greenColor,
onPressed: () async { isFilled: false,
log("message: ${chatMessageModel.reqOfferID}"); onPressed: () async {
int status = await context.read<ChatVM>().onActionOfferTapped( int status = await context.read<ChatVM>().onActionOfferTapped(
context: context, context: context,
requestOfferStatusEnum: RequestOfferStatusEnum.accepted, requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
reqOfferId: chatMessageModel.reqOfferID ?? -1, reqOfferId: chatMessageModel.reqOfferID ?? -1,
); );
if (status != -1) { if (status != -1) {
log("accepted: $status"); log("accepted: $status");
if (chatMessageModel.reqOfferID == status) { if (chatMessageModel.reqOfferID == status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted; chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
setState(() {}); setState(() {});
}
} }
} },
}, backgroundColor: MyColors.white,
backgroundColor: MyColors.white, txtColor: MyColors.greenColor,
txtColor: MyColors.greenColor, ),
),
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: "Reject",
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet();
// int status =
// await context.read<ChatVM>().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(() {});
// }
// }
},
), ),
) 20.width,
], Expanded(
), child: ShowFillButton(
maxHeight: 27,
title: "Reject",
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet();
},
),
)
],
),
],
], ],
); );
case RequestOfferStatusEnum.negotiate: case RequestOfferStatusEnum.negotiate:
return Column( return Column(
children: [ children: [
Center( Center(
child: "You asked for the new offer.".toText( child: "New Offer Required.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -252,7 +324,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "You have accepted the offer.".toText( child: "Offer has been accepted.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -264,7 +336,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "You have rejected the offer.".toText( child: "Offer has been rejected.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -276,7 +348,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
return Column( return Column(
children: [ children: [
Center( Center(
child: "You have cancelled the offer.".toText( child: "Offer has been cancelled.".toText(
color: MyColors.adPendingStatusColor, color: MyColors.adPendingStatusColor,
fontSize: 12, fontSize: 12,
isItalic: true, isItalic: true,
@ -291,6 +363,7 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Directionality( return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr, textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? 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: [
@ -322,10 +395,14 @@ class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: (widget.chatMessageModel.chatText ?? "").toText( child: Directionality(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor, textDirection: TextDirection.ltr,
fontSize: 12, child: (widget.chatMessageModel.chatText ?? "").toText(
// isBold: true, color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
),
), ),
), ),
], ],

@ -1,9 +1,12 @@
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/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/provider_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';
@ -88,7 +91,15 @@ class OfferListPage extends StatelessWidget {
final chatVM = context.read<ChatVM>(); final chatVM = context.read<ChatVM>();
await chatVM.getUsersChatMessagesForCustomer(context: context, providerId: offersModel.providerId ?? 0, requestOfferId: 0, providerOfferIndex: index).whenComplete( await chatVM
.getUsersChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: offerListPageArguments.requestId ?? 0,
providerOfferIndex: index,
)
.whenComplete(
() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments), () => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments),
); );
}).toContainer(isShadowEnabled: true); }).toContainer(isShadowEnabled: true);

@ -34,26 +34,9 @@ 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 = "966504278212", password = "Fa@123"; String phoneNum = "966504278213", password = "Fa@1234";
String email = ""; String email = "";
String countryCode = ""; String countryCode = "";
Country? _country; Country? _country;

@ -93,10 +93,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.17.1"
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: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.18.0"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -601,34 +601,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.15"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.2.0"
message_pack_dart:
dependency: transitive
description:
name: message_pack_dart
sha256: "71b9f0ff60e5896e60b337960bb535380d7dba3297b457ac763ccae807385b59"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.9.1"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -725,6 +717,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.7" version: "2.0.7"
percent_indicator:
dependency: "direct main"
description:
name: percent_indicator
sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c
url: "https://pub.dev"
source: hosted
version: "4.2.3"
permission_handler: permission_handler:
dependency: "direct main" dependency: "direct main"
description: description:
@ -789,14 +789,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.6"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -877,14 +869,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.1" version: "2.3.1"
shelf:
dependency: transitive
description:
name: shelf
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
url: "https://pub.dev"
source: hosted
version: "1.4.1"
shimmer: shimmer:
dependency: "direct main" dependency: "direct main"
description: description:
@ -893,14 +877,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
signalr_netcore: signalr_core:
dependency: "direct main" dependency: "direct main"
description: description:
name: signalr_netcore name: signalr_core
sha256: "8f84b4b516c03f3a6872f94e9729d1441d5d223a77c81d0a7d7dae5dd0ce1f2f" sha256: dca676372a00c051511591ed0e24521ff7aa4e9320a7fa778a1007f7f522c8c0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.6" version: "1.1.1"
simple_gesture_detector: simple_gesture_detector:
dependency: transitive dependency: transitive
description: description:
@ -926,10 +910,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.9.1"
sqflite: sqflite:
dependency: transitive dependency: transitive
description: description:
@ -946,38 +930,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0" version: "2.5.0"
sse: sse_client:
dependency: transitive dependency: transitive
description: description:
name: sse name: sse_client
sha256: "3ff9088cac3f45aa8b91336f1962e3ea6c81baaba0bbba361c05f8aa7fb59442" sha256: "71bd826430b41ab20a69d85bf2dfe9f11cfe222938e681ada1aea71fc8adf348"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.2" version: "0.1.0"
sse_channel:
dependency: transitive
description:
name: sse_channel
sha256: ba2b1382b9423c58fa83e1f01a3a40fbaa16a0594aa984870c88bad0b45d4ca4
url: "https://pub.dev"
source: hosted
version: "0.0.3"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.1" version: "1.11.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.1"
stream_transform: stream_transform:
dependency: transitive dependency: transitive
description: description:
@ -1022,10 +998,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.5.1"
tuple: tuple:
dependency: transitive dependency: transitive
description: description:
@ -1130,14 +1106,6 @@ 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:
@ -1171,5 +1139,5 @@ packages:
source: hosted source: hosted
version: "6.3.0" version: "6.3.0"
sdks: sdks:
dart: ">=3.2.0-194.0.dev <4.0.0" dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0" flutter: ">=3.10.0"

Loading…
Cancel
Save