Compare commits
No commits in common. 'master' and 'localization_aamir' have entirely different histories.
master
...
localizati
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,5 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="9.688" viewBox="0 0 13 9.688">
|
|
||||||
<g id="Group" transform="translate(6 17.688) rotate(180)">
|
|
||||||
<path id="Vector" d="M13,4.844a.781.781,0,0,1-.781.781H2.674L5.414,8.352a.781.781,0,0,1-1.1,1.108L.23,5.4h0a.782.782,0,0,1,0-1.106h0L4.312.227a.781.781,0,0,1,1.1,1.108L2.674,4.062h9.545A.781.781,0,0,1,13,4.844Z" transform="translate(-7 8)"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 411 B |
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
@ -1,3 +0,0 @@
|
|||||||
description: This file stores settings for Dart & Flutter DevTools.
|
|
||||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
|
||||||
extensions:
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,13 +1,135 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mc_common_app/theme/app_theme.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:provider/single_child_widget.dart';
|
||||||
|
import 'package:sizer/sizer.dart';
|
||||||
|
|
||||||
|
//testing push
|
||||||
|
final navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
Logger logger = Logger(
|
||||||
|
// printer: PrettyPrinter(lineLength: 0, methodCount: 0,),
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
Logger logger = Logger(printer: PrettyPrinter(printEmojis: false, colors: true, printTime: false));
|
class MyHttpOverrides extends HttpOverrides {
|
||||||
|
@override
|
||||||
|
HttpClient createHttpClient(SecurityContext? context) {
|
||||||
|
return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port) => true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool disableThingsForQA = true;
|
Future<void> main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
// Language Tile in Settings
|
await EasyLocalization.ensureInitialized();
|
||||||
|
// AppState().setPostParamsInitConfig();
|
||||||
|
HttpOverrides.global = MyHttpOverrides();
|
||||||
|
|
||||||
|
runApp(
|
||||||
|
EasyLocalization(
|
||||||
|
supportedLocales: const <Locale>[
|
||||||
|
Locale('en', 'US'),
|
||||||
|
Locale('ar', 'SA'),
|
||||||
|
],
|
||||||
|
path: 'resources',
|
||||||
|
// assetLoader: const CodegenLoader(),
|
||||||
|
child: MultiProvider(
|
||||||
|
providers: const <SingleChildWidget>[
|
||||||
|
// ChangeNotifierProvider<LoginProviderModel>(
|
||||||
|
// create: (_) => LoginProviderModel(),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
child: const MyApp(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// todo terminal command to genertate translation files
|
// todo terminal command to genertate translation files
|
||||||
// flutter pub run easy_localization:generate --source-dir ./assets/langs
|
// flutter pub run easy_localization:generate --source-dir ./assets/langs
|
||||||
// todo terminal command to genertate translation keys
|
// todo terminal command to genertate translation keys
|
||||||
// flutter pub run easy_localization:generate --source-dir ./assets/langs -f keys -o locale_keys.g.dart
|
// flutter pub run easy_localization:generate --source-dir ./assets/langs -f keys -o locale_keys.g.dart
|
||||||
// command to generate languages data from json
|
// command to generate languages data from json
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(builder: (context, constraints) {
|
||||||
|
return Sizer(
|
||||||
|
builder: (
|
||||||
|
BuildContext context,
|
||||||
|
Orientation orientation,
|
||||||
|
DeviceType deviceType,
|
||||||
|
) {
|
||||||
|
List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
|
||||||
|
// AppState().setPostParamsModel(
|
||||||
|
// PostParamsModel(
|
||||||
|
// languageID: EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
return MaterialApp(
|
||||||
|
// key: navigatorKey,
|
||||||
|
navigatorKey: navigatorKey,
|
||||||
|
theme: AppTheme.getTheme(
|
||||||
|
isArabic: EasyLocalization.of(context)?.locale.languageCode == "ar",
|
||||||
|
),
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
localizationsDelegates: delegates,
|
||||||
|
supportedLocales: context.supportedLocales,
|
||||||
|
locale: context.locale,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class MyApp extends StatelessWidget {
|
||||||
|
// MyApp({super.key}) {
|
||||||
|
// AppDependencies.addDependencies();
|
||||||
|
// AppState = Injector.appInstance.get<AppState>();
|
||||||
|
// // AppState.setPostParamsInitConfig();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return LayoutBuilder(builder: (context, constraints) {
|
||||||
|
// return Sizer(
|
||||||
|
// builder: (
|
||||||
|
// BuildContext context,
|
||||||
|
// Orientation orientation,
|
||||||
|
// DeviceType deviceType,
|
||||||
|
// ) {
|
||||||
|
// SizeConfig().init(constraints, orientation);
|
||||||
|
// List<LocalizationsDelegate<dynamic>> delegates = context.localizationDelegates;
|
||||||
|
// // AppState().setPostParamsModel(
|
||||||
|
// // PostParamsModel(
|
||||||
|
// // languageID: EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2,
|
||||||
|
// // ),
|
||||||
|
// // );
|
||||||
|
// return MaterialApp(
|
||||||
|
// // key: navigatorKey,
|
||||||
|
// navigatorKey: navigatorKey,
|
||||||
|
// theme: AppTheme.getTheme(
|
||||||
|
// EasyLocalization.of(context)?.locale.languageCode == "ar",
|
||||||
|
// ),
|
||||||
|
// debugShowCheckedModeBanner: false,
|
||||||
|
// localizationsDelegates: delegates,
|
||||||
|
// supportedLocales: context.supportedLocales,
|
||||||
|
// locale: context.locale,
|
||||||
|
// initialRoute: AppRoutes.initialPage,
|
||||||
|
// routes: AppRoutes.routes,
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@ -1,45 +0,0 @@
|
|||||||
class AppointmentBasicDetailsModel {
|
|
||||||
int? serviceSlotID;
|
|
||||||
String? slotDate;
|
|
||||||
String? startTime;
|
|
||||||
String? endTime;
|
|
||||||
int? appointmentStatusID;
|
|
||||||
String? appointmentStatusText;
|
|
||||||
int? serviceProviderID;
|
|
||||||
int? customerID;
|
|
||||||
|
|
||||||
AppointmentBasicDetailsModel({
|
|
||||||
this.serviceSlotID,
|
|
||||||
this.slotDate,
|
|
||||||
this.startTime,
|
|
||||||
this.endTime,
|
|
||||||
this.appointmentStatusID,
|
|
||||||
this.appointmentStatusText,
|
|
||||||
this.serviceProviderID,
|
|
||||||
this.customerID,
|
|
||||||
});
|
|
||||||
|
|
||||||
AppointmentBasicDetailsModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
serviceSlotID = json['serviceSlotID'];
|
|
||||||
slotDate = json['slotDate'];
|
|
||||||
startTime = json['startTime'];
|
|
||||||
endTime = json['endTime'];
|
|
||||||
appointmentStatusID = json['appointmentStatusID'];
|
|
||||||
appointmentStatusText = json['appointmentStatusText'];
|
|
||||||
serviceProviderID = json['serviceProviderID'];
|
|
||||||
customerID = json['customerID'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
|
||||||
data['serviceSlotID'] = serviceSlotID;
|
|
||||||
data['slotDate'] = slotDate;
|
|
||||||
data['startTime'] = startTime;
|
|
||||||
data['endTime'] = endTime;
|
|
||||||
data['appointmentStatusID'] = appointmentStatusID;
|
|
||||||
data['appointmentStatusText'] = appointmentStatusText;
|
|
||||||
data['serviceProviderID'] = serviceProviderID;
|
|
||||||
data['customerID'] = customerID;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
class BuyersChatForAdsModel {
|
|
||||||
int? id;
|
|
||||||
String? buyerUserID;
|
|
||||||
String? buyerName;
|
|
||||||
int? adsID;
|
|
||||||
int? unReadMessagesCount;
|
|
||||||
String? lastMessage;
|
|
||||||
String? lastMessageDateTime;
|
|
||||||
|
|
||||||
BuyersChatForAdsModel({this.id, this.buyerUserID, this.buyerName, this.adsID});
|
|
||||||
|
|
||||||
BuyersChatForAdsModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
buyerUserID = json['buyerUserID'];
|
|
||||||
buyerName = json['buyerName'];
|
|
||||||
adsID = json['adsID'];
|
|
||||||
unReadMessagesCount = json['unReadCount'];
|
|
||||||
lastMessage = json['lastMessage'];
|
|
||||||
lastMessageDateTime = json['lastMessageDateTime'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
// To parse this JSON data, do
|
||||||
|
//
|
||||||
|
// final mResponse = mResponseFromJson(jsonString);
|
||||||
|
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
MResponse mResponseFromJson(String str) => MResponse.fromJson(json.decode(str));
|
||||||
|
|
||||||
|
String mResponseToJson(MResponse data) => json.encode(data.toJson());
|
||||||
|
|
||||||
|
class MResponse {
|
||||||
|
MResponse({
|
||||||
|
this.totalItemsCount,
|
||||||
|
this.messageStatus,
|
||||||
|
this.message,
|
||||||
|
this.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? totalItemsCount;
|
||||||
|
int? messageStatus;
|
||||||
|
String? message;
|
||||||
|
dynamic data;
|
||||||
|
|
||||||
|
factory MResponse.fromJson(Map<String, dynamic> json) => MResponse(
|
||||||
|
totalItemsCount: json["totalItemsCount"],
|
||||||
|
messageStatus: json["messageStatus"],
|
||||||
|
message: json["message"],
|
||||||
|
data: json["data"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"totalItemsCount": totalItemsCount,
|
||||||
|
"messageStatus": messageStatus,
|
||||||
|
"message": message,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,36 +0,0 @@
|
|||||||
class BranchRatingModel {
|
|
||||||
int? id;
|
|
||||||
String? title;
|
|
||||||
String? review;
|
|
||||||
int? ratNo;
|
|
||||||
int? serviceProviderBranchID;
|
|
||||||
String? serviceProviderBranchName;
|
|
||||||
int? customerID;
|
|
||||||
String? customerName;
|
|
||||||
|
|
||||||
BranchRatingModel({this.id, this.title, this.review, this.ratNo, this.serviceProviderBranchID, this.serviceProviderBranchName, this.customerID, this.customerName});
|
|
||||||
|
|
||||||
BranchRatingModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
title = json['title'];
|
|
||||||
review = json['review'];
|
|
||||||
ratNo = json['ratNo'];
|
|
||||||
serviceProviderBranchID = json['serviceProviderBranchID'];
|
|
||||||
serviceProviderBranchName = json['serviceProviderBranchName'];
|
|
||||||
customerID = json['customerID'];
|
|
||||||
customerName = json['customerName'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
|
||||||
data['id'] = id;
|
|
||||||
data['title'] = title;
|
|
||||||
data['review'] = review;
|
|
||||||
data['ratNo'] = ratNo;
|
|
||||||
data['serviceProviderBranchID'] = serviceProviderBranchID;
|
|
||||||
data['serviceProviderBranchName'] = serviceProviderBranchName;
|
|
||||||
data['customerID'] = customerID;
|
|
||||||
data['customerName'] = customerName;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
class ProviderContactInfoModel {
|
|
||||||
int? id;
|
|
||||||
int? providerID;
|
|
||||||
String? name;
|
|
||||||
int? allDocStatus;
|
|
||||||
String? companyName;
|
|
||||||
String? companyDescription;
|
|
||||||
int? countryID;
|
|
||||||
String? countryName;
|
|
||||||
String? userID;
|
|
||||||
String? memberSince;
|
|
||||||
String? mobile;
|
|
||||||
String? whatsApp;
|
|
||||||
String? email;
|
|
||||||
|
|
||||||
ProviderContactInfoModel({
|
|
||||||
this.id,
|
|
||||||
this.providerID,
|
|
||||||
this.name,
|
|
||||||
this.allDocStatus,
|
|
||||||
this.companyName,
|
|
||||||
this.companyDescription,
|
|
||||||
this.countryID,
|
|
||||||
this.countryName,
|
|
||||||
this.userID,
|
|
||||||
this.memberSince,
|
|
||||||
this.mobile,
|
|
||||||
this.whatsApp,
|
|
||||||
this.email,
|
|
||||||
});
|
|
||||||
|
|
||||||
ProviderContactInfoModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
providerID = json['providerID'];
|
|
||||||
name = json['name'];
|
|
||||||
allDocStatus = json['allDocStatus'];
|
|
||||||
companyName = json['companyName'];
|
|
||||||
companyDescription = json['companyDescription'];
|
|
||||||
countryID = json['countryID'];
|
|
||||||
countryName = json['countryName'];
|
|
||||||
userID = json['userID'];
|
|
||||||
memberSince = json['memberSince'];
|
|
||||||
mobile = json['mobile'];
|
|
||||||
whatsApp = json['whatsApp'];
|
|
||||||
email = json['email'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
class OffersUnreadChatModel {
|
|
||||||
final int reqTotal;
|
|
||||||
final List<OffersUnreadChatDataModel> reqChatUnread;
|
|
||||||
|
|
||||||
OffersUnreadChatModel({
|
|
||||||
required this.reqTotal,
|
|
||||||
required this.reqChatUnread,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory OffersUnreadChatModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return OffersUnreadChatModel(
|
|
||||||
reqTotal: json['total'],
|
|
||||||
reqChatUnread: (json['reqChatCountData'] as List).map((e) => OffersUnreadChatDataModel.fromJson(e)).toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OffersUnreadChatDataModel {
|
|
||||||
String reqTitle;
|
|
||||||
int reqTotal;
|
|
||||||
int requestID;
|
|
||||||
String senderUserID;
|
|
||||||
int unreadMessagesCount;
|
|
||||||
String lastChatTime;
|
|
||||||
String lastChatText;
|
|
||||||
String customerName;
|
|
||||||
|
|
||||||
OffersUnreadChatDataModel({
|
|
||||||
required this.reqTitle,
|
|
||||||
required this.reqTotal,
|
|
||||||
required this.requestID,
|
|
||||||
required this.senderUserID,
|
|
||||||
required this.unreadMessagesCount,
|
|
||||||
required this.lastChatTime,
|
|
||||||
required this.lastChatText,
|
|
||||||
required this.customerName,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory OffersUnreadChatDataModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return OffersUnreadChatDataModel(
|
|
||||||
reqTitle: json['reqTitle'],
|
|
||||||
reqTotal: json['reqTotal'],
|
|
||||||
requestID: json['requestID'],
|
|
||||||
senderUserID: json['senderUserID'],
|
|
||||||
unreadMessagesCount: json['unreadMessagesCount'],
|
|
||||||
lastChatTime: json['lastChatTime'],
|
|
||||||
lastChatText: json['lastChatText'],
|
|
||||||
customerName: json['customerName'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "reqTitle": "Spare_Parts / Ford / Edge / 2016",
|
|
||||||
// I/flutter (16941): │ "reqTotal": 1,
|
|
||||||
// I/flutter (16941): │ "requestID": 145,
|
|
||||||
// I/flutter (16941): │ "senderUserID": "b56bc6bd-e45c-4644-b7c4-08dd02460895",
|
|
||||||
// I/flutter (16941): │ "unreadMessagesCount": 0,
|
|
||||||
// I/flutter (16941): │ "lastChatTime": "2025-02-12T10:33:42.1433333",
|
|
||||||
// I/flutter (16941): │ "lastChatText": "I am accepting this offer.",
|
|
||||||
// I/flutter (16941): │ "customerName": "Faiz 100"
|
|
||||||
@ -1,158 +0,0 @@
|
|||||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
|
|
||||||
class ProviderOffersChatsModel {
|
|
||||||
int? id;
|
|
||||||
int? requestID;
|
|
||||||
int? serviceProviderID;
|
|
||||||
ServiceProviderModel? serviceProvider;
|
|
||||||
int? offerStatus;
|
|
||||||
RequestOfferStatusEnum? requestOfferStatusEnum;
|
|
||||||
String? offerStatusText;
|
|
||||||
String? comment;
|
|
||||||
String? customerName;
|
|
||||||
double? price;
|
|
||||||
String? serviceItem;
|
|
||||||
String? offeredItemCreatedBy;
|
|
||||||
String? offeredItemCreatedByName;
|
|
||||||
String? offeredItemCreatedOn;
|
|
||||||
String? reqOfferImages;
|
|
||||||
bool? isDeliveryAvailable;
|
|
||||||
String? createdOn;
|
|
||||||
|
|
||||||
ProviderOffersChatsModel({
|
|
||||||
this.id,
|
|
||||||
this.requestID,
|
|
||||||
this.serviceProviderID,
|
|
||||||
this.serviceProvider,
|
|
||||||
this.offerStatus,
|
|
||||||
this.requestOfferStatusEnum,
|
|
||||||
this.offerStatusText,
|
|
||||||
this.comment,
|
|
||||||
this.customerName,
|
|
||||||
this.price,
|
|
||||||
this.serviceItem,
|
|
||||||
this.offeredItemCreatedBy,
|
|
||||||
this.offeredItemCreatedByName,
|
|
||||||
this.offeredItemCreatedOn,
|
|
||||||
this.reqOfferImages,
|
|
||||||
this.isDeliveryAvailable,
|
|
||||||
this.createdOn,
|
|
||||||
});
|
|
||||||
|
|
||||||
ProviderOffersChatsModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
requestID = json['requestID'];
|
|
||||||
serviceProviderID = json['serviceProviderID'];
|
|
||||||
serviceProvider = json['serviceProvider'] != null ? ServiceProviderModel.fromJson(json['serviceProvider']) : null;
|
|
||||||
offerStatus = json['offerStatus'];
|
|
||||||
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
|
|
||||||
offerStatusText = json['offerStatusText'];
|
|
||||||
comment = json['comment'];
|
|
||||||
customerName = json['customerName'];
|
|
||||||
price = json['price'];
|
|
||||||
serviceItem = json['serviceItem'];
|
|
||||||
offeredItemCreatedBy = json['offeredItemCreatedBy'];
|
|
||||||
offeredItemCreatedByName = json['offeredItemCreatedByName'];
|
|
||||||
offeredItemCreatedOn = json['offeredItemCreatedOn'];
|
|
||||||
reqOfferImages = json['reqOfferImages'];
|
|
||||||
isDeliveryAvailable = json['isDeliveryAvailable'];
|
|
||||||
createdOn = json['createdOn'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceProviderModel {
|
|
||||||
int? providerId;
|
|
||||||
String? providerGUID;
|
|
||||||
String? firstName;
|
|
||||||
String? lastName;
|
|
||||||
String? name;
|
|
||||||
int? gender;
|
|
||||||
String? genderName;
|
|
||||||
String? mobileNo;
|
|
||||||
String? email;
|
|
||||||
bool? isEmailVerified;
|
|
||||||
bool? isCompleted;
|
|
||||||
int? city;
|
|
||||||
String? cityName;
|
|
||||||
int? country;
|
|
||||||
String? countryName;
|
|
||||||
int? accountStatus;
|
|
||||||
String? accountStatusText;
|
|
||||||
int? activityStatus;
|
|
||||||
String? activityStatusText;
|
|
||||||
String? bankName;
|
|
||||||
String? iBanNo;
|
|
||||||
bool? isActive;
|
|
||||||
String? subscriptionDate;
|
|
||||||
String? createdOn;
|
|
||||||
String? companyName;
|
|
||||||
String? currency;
|
|
||||||
String? branch;
|
|
||||||
bool? isChatted;
|
|
||||||
bool? isDealership;
|
|
||||||
|
|
||||||
ServiceProviderModel({
|
|
||||||
this.providerId,
|
|
||||||
this.providerGUID,
|
|
||||||
this.firstName,
|
|
||||||
this.lastName,
|
|
||||||
this.name,
|
|
||||||
this.gender,
|
|
||||||
this.genderName,
|
|
||||||
this.mobileNo,
|
|
||||||
this.email,
|
|
||||||
this.isEmailVerified,
|
|
||||||
this.isCompleted,
|
|
||||||
this.city,
|
|
||||||
this.cityName,
|
|
||||||
this.country,
|
|
||||||
this.countryName,
|
|
||||||
this.accountStatus,
|
|
||||||
this.accountStatusText,
|
|
||||||
this.activityStatus,
|
|
||||||
this.activityStatusText,
|
|
||||||
this.bankName,
|
|
||||||
this.iBanNo,
|
|
||||||
this.isActive,
|
|
||||||
this.subscriptionDate,
|
|
||||||
this.createdOn,
|
|
||||||
this.companyName,
|
|
||||||
this.currency,
|
|
||||||
this.branch,
|
|
||||||
this.isChatted,
|
|
||||||
this.isDealership,
|
|
||||||
});
|
|
||||||
|
|
||||||
ServiceProviderModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
providerId = json['providerId'];
|
|
||||||
providerGUID = json['providerGUID'];
|
|
||||||
firstName = json['firstName'];
|
|
||||||
lastName = json['lastName'];
|
|
||||||
name = json['name'];
|
|
||||||
gender = json['gender'];
|
|
||||||
genderName = json['genderName'];
|
|
||||||
mobileNo = json['mobileNo'];
|
|
||||||
email = json['email'];
|
|
||||||
isEmailVerified = json['isEmailVerfied'];
|
|
||||||
isCompleted = json['isCompleted'];
|
|
||||||
city = json['city'];
|
|
||||||
cityName = json['cityName'];
|
|
||||||
country = json['country'];
|
|
||||||
countryName = json['countryName'];
|
|
||||||
accountStatus = json['accountStatus'];
|
|
||||||
accountStatusText = json['accountStatusText'];
|
|
||||||
activityStatus = json['activityStatus'];
|
|
||||||
activityStatusText = json['activityStatusText'];
|
|
||||||
bankName = json['bankName'];
|
|
||||||
iBanNo = json['iBanNo'];
|
|
||||||
isActive = json['isActive'];
|
|
||||||
subscriptionDate = json['subscriptionDate'];
|
|
||||||
createdOn = json['createdOn'];
|
|
||||||
companyName = json['companyName'];
|
|
||||||
currency = json['currency'];
|
|
||||||
branch = json['branch'];
|
|
||||||
isChatted = json['isChatted'];
|
|
||||||
isDealership = json['isDealership'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import 'package:mc_common_app/view_models/ad_view_model.dart';
|
|
||||||
|
|
||||||
class AppInfoModel {
|
|
||||||
int? id;
|
|
||||||
String? header;
|
|
||||||
String? content;
|
|
||||||
bool? isActive;
|
|
||||||
int? channel;
|
|
||||||
List<ImageModel>? images;
|
|
||||||
|
|
||||||
AppInfoModel({this.id, this.header, this.content, this.isActive, this.channel, this.images});
|
|
||||||
|
|
||||||
AppInfoModel.fromJson(Map<String, dynamic> json, {bool isForTermsAndCondition = false}) {
|
|
||||||
String imagesKey = "appInfoImages";
|
|
||||||
if (isForTermsAndCondition) {
|
|
||||||
imagesKey = "termAndConditionImages";
|
|
||||||
}
|
|
||||||
id = json['id'];
|
|
||||||
header = json['header'];
|
|
||||||
content = json['content'];
|
|
||||||
isActive = json['isActive'];
|
|
||||||
channel = json['channel'];
|
|
||||||
if (json[imagesKey] != null) {
|
|
||||||
images = <ImageModel>[];
|
|
||||||
json[imagesKey].forEach((v) {
|
|
||||||
images!.add(ImageModel.fromJson(v));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
class AppInvitationHistoryModel {
|
|
||||||
int? id;
|
|
||||||
int? customerID;
|
|
||||||
int? channelID;
|
|
||||||
int? appInvitationEnumID;
|
|
||||||
int? noOfInvites;
|
|
||||||
String? comments;
|
|
||||||
|
|
||||||
AppInvitationHistoryModel({
|
|
||||||
this.id,
|
|
||||||
this.customerID,
|
|
||||||
this.channelID,
|
|
||||||
this.appInvitationEnumID,
|
|
||||||
this.noOfInvites,
|
|
||||||
this.comments,
|
|
||||||
});
|
|
||||||
|
|
||||||
AppInvitationHistoryModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
customerID = json['customerID'];
|
|
||||||
channelID = json['channelID'];
|
|
||||||
appInvitationEnumID = json['appInvitationEnumID'];
|
|
||||||
noOfInvites = json['noofInvites'];
|
|
||||||
comments = json['comments'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
import 'package:mc_common_app/view_models/ad_view_model.dart';
|
|
||||||
|
|
||||||
class ContactInfoModel {
|
|
||||||
int? id;
|
|
||||||
String? companyName;
|
|
||||||
String? mobileNo;
|
|
||||||
String? phoneNo;
|
|
||||||
String? email;
|
|
||||||
String? address;
|
|
||||||
String? latitude;
|
|
||||||
String? longitude;
|
|
||||||
int? channel;
|
|
||||||
bool? isActive;
|
|
||||||
List<ImageModel>? contactInfoImages;
|
|
||||||
|
|
||||||
ContactInfoModel({this.id, this.companyName, this.mobileNo, this.phoneNo, this.email, this.address, this.latitude, this.longitude, this.channel, this.isActive, this.contactInfoImages});
|
|
||||||
|
|
||||||
ContactInfoModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
companyName = json['companyName'];
|
|
||||||
mobileNo = json['mobileNo'];
|
|
||||||
phoneNo = json['phoneNo'];
|
|
||||||
email = json['email'];
|
|
||||||
address = json['address'];
|
|
||||||
latitude = json['latitude'];
|
|
||||||
longitude = json['longitude'];
|
|
||||||
channel = json['channel'];
|
|
||||||
isActive = json['isActive'];
|
|
||||||
if (json['contactInfoImages'] != null) {
|
|
||||||
contactInfoImages = <ImageModel>[];
|
|
||||||
json['contactInfoImages'].forEach((v) {
|
|
||||||
contactInfoImages!.add(ImageModel.fromJson(v));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
class FAQsModel {
|
|
||||||
int? id;
|
|
||||||
String? question;
|
|
||||||
String? answer;
|
|
||||||
int? sequenceNo;
|
|
||||||
bool? isActive;
|
|
||||||
int? channel;
|
|
||||||
bool? isCollapsed;
|
|
||||||
String? questionN;
|
|
||||||
String? answerN;
|
|
||||||
|
|
||||||
FAQsModel({this.id, this.question, this.answer, this.sequenceNo, this.isActive, this.channel, this.isCollapsed,
|
|
||||||
this.questionN, this.answerN, });
|
|
||||||
|
|
||||||
FAQsModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
id = json['id'];
|
|
||||||
question = json['question'];
|
|
||||||
answer = json['answer'];
|
|
||||||
sequenceNo = json['sequenceNo'];
|
|
||||||
isActive = json['isActive'];
|
|
||||||
channel = json['channel'];
|
|
||||||
isCollapsed = false;
|
|
||||||
questionN = json['questionN'];
|
|
||||||
answerN = json['answerN'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
|
||||||
data['id'] = id;
|
|
||||||
data['question'] = question;
|
|
||||||
data['answer'] = answer;
|
|
||||||
data['sequenceNo'] = sequenceNo;
|
|
||||||
data['isActive'] = isActive;
|
|
||||||
data['channel'] = channel;
|
|
||||||
data['questionN'] = questionN;
|
|
||||||
data['answerN'] = answerN;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
|
|
||||||
class ShippingRequestModel {
|
|
||||||
int? id;
|
|
||||||
int? requestID;
|
|
||||||
Request? request;
|
|
||||||
int? pickupOrshippingStatus;
|
|
||||||
ShippingRequestStatusEnum? shippingStatusEnum;
|
|
||||||
SelfPickupRequestStatusEnum? selfPickupRequestStatusEnum;
|
|
||||||
String? deliveredorCollectedOn;
|
|
||||||
String? comment;
|
|
||||||
String? createdOn;
|
|
||||||
int? customerID;
|
|
||||||
String? customerName;
|
|
||||||
int? vehicleTypeID;
|
|
||||||
String? vehicleType;
|
|
||||||
String? numberOfShipping;
|
|
||||||
|
|
||||||
ShippingRequestModel({
|
|
||||||
this.id,
|
|
||||||
this.requestID,
|
|
||||||
this.request,
|
|
||||||
this.pickupOrshippingStatus,
|
|
||||||
this.deliveredorCollectedOn,
|
|
||||||
this.comment,
|
|
||||||
this.createdOn,
|
|
||||||
this.customerID,
|
|
||||||
this.customerName,
|
|
||||||
this.vehicleTypeID,
|
|
||||||
this.vehicleType,
|
|
||||||
this.numberOfShipping,
|
|
||||||
});
|
|
||||||
|
|
||||||
ShippingRequestModel.fromJsonShipping(Map<String, dynamic> json, bool isForShipping) {
|
|
||||||
id = json['id'];
|
|
||||||
requestID = json['requestID'];
|
|
||||||
request = json['request'] != null ? Request.fromJson(json['request']) : null;
|
|
||||||
pickupOrshippingStatus = isForShipping ? json['shippingStatus'] : json['selfPickUpStatus'];
|
|
||||||
shippingStatusEnum = isForShipping ? (json['shippingStatus'] != null ? (json['shippingStatus'] as int).toShippingStatusEnum() : ShippingRequestStatusEnum.initiated) : null;
|
|
||||||
selfPickupRequestStatusEnum =
|
|
||||||
isForShipping ? null : (json['selfPickUpStatus'] != null ? (json['selfPickUpStatus'] as int).toSelfPickupStatusEnum() : SelfPickupRequestStatusEnum.preparingToCollect);
|
|
||||||
deliveredorCollectedOn = isForShipping ? json['deliveredOn'] : json['collectedOn'];
|
|
||||||
comment = json['comment'];
|
|
||||||
createdOn = json['createdOn'];
|
|
||||||
customerID = json['customerID'];
|
|
||||||
customerName = json['customerName'];
|
|
||||||
vehicleTypeID = json['vehicleTypeID'];
|
|
||||||
vehicleType = json['vehicleType'];
|
|
||||||
numberOfShipping = json['numberOfShipping'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Request {
|
|
||||||
int? requestType;
|
|
||||||
String? brand;
|
|
||||||
String? model;
|
|
||||||
int? year;
|
|
||||||
bool? isNew;
|
|
||||||
String? description;
|
|
||||||
double? price;
|
|
||||||
|
|
||||||
Request({this.requestType, this.brand, this.model, this.year, this.isNew, this.description, this.price});
|
|
||||||
|
|
||||||
Request.fromJson(Map<String, dynamic> json) {
|
|
||||||
requestType = json['requestType'];
|
|
||||||
brand = json['brand'];
|
|
||||||
model = json['model'];
|
|
||||||
year = json['year'];
|
|
||||||
isNew = json['isNew'];
|
|
||||||
description = json['description'];
|
|
||||||
price = json['price'];
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final Map<String, dynamic> data = <String, dynamic>{};
|
|
||||||
data['requestType'] = requestType;
|
|
||||||
data['brand'] = brand;
|
|
||||||
data['model'] = model;
|
|
||||||
data['year'] = year;
|
|
||||||
data['isNew'] = isNew;
|
|
||||||
data['description'] = description;
|
|
||||||
data['price'] = price;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:mc_common_app/api/api_client.dart';
|
|
||||||
import 'package:mc_common_app/classes/app_state.dart';
|
|
||||||
import 'package:mc_common_app/classes/consts.dart';
|
|
||||||
import 'package:mc_common_app/config/dependency_injection.dart';
|
|
||||||
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
|
||||||
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
|
|
||||||
import 'package:mc_common_app/models/setting_utils_models/app_info_model.dart';
|
|
||||||
import 'package:mc_common_app/models/setting_utils_models/app_invitation_history_model.dart';
|
|
||||||
import 'package:mc_common_app/models/setting_utils_models/contact_infos_model.dart';
|
|
||||||
import 'package:mc_common_app/models/setting_utils_models/faqs_model.dart';
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
import 'package:mc_common_app/utils/utils.dart';
|
|
||||||
|
|
||||||
abstract class SettingOptionsRepo {
|
|
||||||
Future<List<FAQsModel>> getAllFaqs();
|
|
||||||
|
|
||||||
Future<List<ContactInfoModel>> getAllContactInfos();
|
|
||||||
|
|
||||||
Future<List<AppInfoModel>> getAppInfoList();
|
|
||||||
|
|
||||||
Future<List<AppInfoModel>> getTermsAndConditions();
|
|
||||||
|
|
||||||
Future<GenericRespModel> appInvitationCreate({required int channelId, required String message});
|
|
||||||
|
|
||||||
Future<List<AppInvitationHistoryModel>> getAppInvitationsHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
class SettingOptionsRepoImp extends SettingOptionsRepo {
|
|
||||||
ApiClient apiClient = injector.get<ApiClient>();
|
|
||||||
AppState appState = injector.get<AppState>();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<FAQsModel>> getAllFaqs() async {
|
|
||||||
int channel = 0;
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.provider) {
|
|
||||||
channel = 2;
|
|
||||||
} else {
|
|
||||||
channel = 3;
|
|
||||||
}
|
|
||||||
final params = {
|
|
||||||
"Channel": channel.toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.getAllFAQs,
|
|
||||||
token: token,
|
|
||||||
queryParameters: params,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<FAQsModel> list = List.generate(genericRespModel.data.length, (index) => FAQsModel.fromJson(genericRespModel.data[index]));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ContactInfoModel>> getAllContactInfos() async {
|
|
||||||
int channel = 0;
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.provider) {
|
|
||||||
channel = 2;
|
|
||||||
} else {
|
|
||||||
channel = 3;
|
|
||||||
}
|
|
||||||
final params = {"Channel": channel.toString()};
|
|
||||||
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.getContactInfo,
|
|
||||||
queryParameters: params,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ContactInfoModel> list = List.generate(genericRespModel.data.length, (index) => ContactInfoModel.fromJson(genericRespModel.data[index]));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<AppInfoModel>> getAppInfoList() async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
int channel = 0;
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.provider) {
|
|
||||||
channel = 2;
|
|
||||||
} else {
|
|
||||||
channel = 3;
|
|
||||||
}
|
|
||||||
final params = {
|
|
||||||
"Channel": channel.toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.getAppInfo,
|
|
||||||
token: token,
|
|
||||||
queryParameters: params,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<AppInfoModel> list = List.generate(genericRespModel.data.length, (index) => AppInfoModel.fromJson(genericRespModel.data[index]));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<AppInfoModel>> getTermsAndConditions() async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
int channel = 0;
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.provider) {
|
|
||||||
channel = 2;
|
|
||||||
} else {
|
|
||||||
channel = 3;
|
|
||||||
}
|
|
||||||
final params = {
|
|
||||||
"Channel": channel.toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.getTermsAndConditions,
|
|
||||||
token: token,
|
|
||||||
queryParameters: params,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<AppInfoModel> list = List.generate(genericRespModel.data.length, (index) => AppInfoModel.fromJson(genericRespModel.data[index], isForTermsAndCondition: true));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericRespModel> appInvitationCreate({required int channelId, required String message}) async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
final params = {
|
|
||||||
"appInvitationEnumID": channelId.toString(),
|
|
||||||
"comments": message,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.customer) {
|
|
||||||
if (!appState.getIsViewOnly) {
|
|
||||||
params.addAll({
|
|
||||||
"customerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
|
|
||||||
"channelID": "3",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
params.addAll({
|
|
||||||
"channelID": "3",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
params.addAll({
|
|
||||||
"providerID": (appState.getUser.data!.userInfo!.providerId ?? "0").toString(),
|
|
||||||
"channelID": "2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().postJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.createAppInvitation,
|
|
||||||
token: token,
|
|
||||||
params,
|
|
||||||
);
|
|
||||||
return genericRespModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<AppInvitationHistoryModel>> getAppInvitationsHistory() async {
|
|
||||||
final params = {
|
|
||||||
"CustomerID": (appState.getUser.data!.userInfo!.customerId ?? "0").toString(),
|
|
||||||
"ChannelID": "3",
|
|
||||||
};
|
|
||||||
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await injector.get<ApiClient>().getJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.getAppInvitationHistory,
|
|
||||||
queryParameters: params,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<AppInvitationHistoryModel> list = List.generate(genericRespModel.data.length, (index) => AppInvitationHistoryModel.fromJson(genericRespModel.data[index]));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:mc_common_app/api/api_client.dart';
|
|
||||||
import 'package:mc_common_app/classes/app_state.dart';
|
|
||||||
import 'package:mc_common_app/classes/consts.dart';
|
|
||||||
import 'package:mc_common_app/config/dependency_injection.dart';
|
|
||||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
|
||||||
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
|
||||||
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
|
|
||||||
import 'package:mc_common_app/models/shipping_models/shipping_status_model.dart';
|
|
||||||
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
import 'package:mc_common_app/utils/utils.dart';
|
|
||||||
|
|
||||||
abstract class ShippingRepo {
|
|
||||||
Future<List<ShippingRequestModel>> getSelfPickupRequestListByStatus({SelfPickupRequestStatusEnum? selfPickupRequestStatus, int? requestId});
|
|
||||||
|
|
||||||
Future<List<ShippingRequestModel>> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId});
|
|
||||||
|
|
||||||
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment, String? numberOfShipping});
|
|
||||||
|
|
||||||
Future<GenericRespModel> updateSelfPickupRequestStatus({required SelfPickupRequestStatusEnum selfPickupStatusEnum, required int shippingRequestId, String? comment, String? numberOfShipping});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ShippingRepoImp extends ShippingRepo {
|
|
||||||
ApiClient apiClient = injector.get<ApiClient>();
|
|
||||||
AppState appState = injector.get<AppState>();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ShippingRequestModel>> getShippingRequestListByStatus({ShippingRequestStatusEnum? shippingStatusEnum, int? requestId}) async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
Map queryParameters = {
|
|
||||||
"requestID": "${requestId ?? 0}",
|
|
||||||
"shippingStatus": "${shippingStatusEnum != null ? shippingStatusEnum.getIdFromShippingStatusEnum() : -1}", // -1 to get all requests
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.shippingRequestStatusGet,
|
|
||||||
queryParameters,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ShippingRequestModel> list = List.generate(genericRespModel.data.length, (index) => ShippingRequestModel.fromJsonShipping(genericRespModel.data[index], true));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<ShippingRequestModel>> getSelfPickupRequestListByStatus({SelfPickupRequestStatusEnum? selfPickupRequestStatus, int? requestId}) async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
Map queryParameters = {
|
|
||||||
"requestID": "${requestId ?? 0}",
|
|
||||||
"selfPickUpStatus": "${selfPickupRequestStatus != null ? selfPickupRequestStatus.getIdFromSelfPickupStatusEnum() : -1}", // -1 to get all requests
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.selfPickupRequestStatusGet,
|
|
||||||
queryParameters,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ShippingRequestModel> list = List.generate(genericRespModel.data.length, (index) => ShippingRequestModel.fromJsonShipping(genericRespModel.data[index], false));
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericRespModel> updateShippingRequestStatus({required ShippingRequestStatusEnum shippingStatusEnum, required int shippingRequestId, String? comment, String? numberOfShipping}) async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
Map<String, String> queryParameters = {
|
|
||||||
"id": "$shippingRequestId",
|
|
||||||
"shippingStatus": "${shippingStatusEnum.getIdFromShippingStatusEnum()}",
|
|
||||||
"comment": comment ?? "",
|
|
||||||
"numberOfShipping": numberOfShipping ?? "",
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.shippingRequestStatusUpdate,
|
|
||||||
queryParameters,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
}
|
|
||||||
|
|
||||||
return genericRespModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericRespModel> updateSelfPickupRequestStatus({required SelfPickupRequestStatusEnum selfPickupStatusEnum, required int shippingRequestId, String? comment, String? numberOfShipping}) async {
|
|
||||||
String token = appState.getUser.data!.accessToken ?? "";
|
|
||||||
Map<String, String> queryParameters = {
|
|
||||||
"id": "$shippingRequestId",
|
|
||||||
"selfPickUpStatus": "${selfPickupStatusEnum.getIdFromSelfPickupStatusEnum()}",
|
|
||||||
"comment": comment ?? "",
|
|
||||||
"numberOfShipping": numberOfShipping ?? "",
|
|
||||||
};
|
|
||||||
|
|
||||||
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
|
|
||||||
(json) => GenericRespModel.fromJson(json),
|
|
||||||
ApiConsts.selfPickupRequestStatusUpdate,
|
|
||||||
queryParameters,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (genericRespModel.messageStatus != 1 || genericRespModel.data == null) {
|
|
||||||
Utils.showToast(genericRespModel.message ?? LocaleKeys.somethingWrong.tr());
|
|
||||||
}
|
|
||||||
|
|
||||||
return genericRespModel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,206 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:app_links/app_links.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:mc_common_app/config/routes.dart';
|
|
||||||
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
|
||||||
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
|
|
||||||
import 'package:mc_common_app/utils/utils.dart';
|
|
||||||
import 'package:mc_common_app/view_models/ad_view_model.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
/// Handles opening the ad detail screen directly when the app is launched or
|
|
||||||
/// resumed via a shared ad link, for Android App Links / iOS Universal Links
|
|
||||||
/// (https://ms.hmg.com/ad/1541) AND the custom-scheme fallback used by that
|
|
||||||
/// same web page when App Links verification isn't active
|
|
||||||
/// (mowatercustomer://ad/1541, mowaterprovider://ad/1541).
|
|
||||||
class AdDeepLinkService {
|
|
||||||
static final AppLinks _appLinks = AppLinks();
|
|
||||||
static StreamSubscription<Uri>? _subscription;
|
|
||||||
static Uri? _lastHandledUri;
|
|
||||||
|
|
||||||
// Route name given to the inert, self-removing placeholder pushed from main.dart's
|
|
||||||
// onUnknownRoute for Android's native deep-link route forwarding (see that file for
|
|
||||||
// why it exists). It's pushed and removed again within a frame, so the route-settle
|
|
||||||
// polling below must ignore it rather than treat its brief presence/absence as a real
|
|
||||||
// navigation change -- otherwise the route name never reads as "stable" and _openAd
|
|
||||||
// gives up without ever opening the ad.
|
|
||||||
static const String ignoredPlaceholderRouteName = '__ad_deep_link_ignore__';
|
|
||||||
|
|
||||||
static String? _currentTopRouteName;
|
|
||||||
|
|
||||||
/// Register via MaterialApp(navigatorObservers: [AdDeepLinkService.routeObserver]).
|
|
||||||
/// This is the only reliable way to know the current top route name from here --
|
|
||||||
/// confirmed on a real device: ModalRoute.of(navigatorKey.currentContext) does NOT
|
|
||||||
/// work for this. navigatorKey.currentContext is the Navigator widget's OWN element,
|
|
||||||
/// which sits ABOVE all its routes in the tree, so it has no ModalRoute ancestor and
|
|
||||||
/// that call returns null essentially always. That silently made every route-settle
|
|
||||||
/// poll below read null forever, so _openAd always timed out and gave up without ever
|
|
||||||
/// opening the ad, regardless of login state or what screen was actually showing.
|
|
||||||
static final NavigatorObserver routeObserver = _RouteNameTracker();
|
|
||||||
|
|
||||||
/// Call once, after the app's first frame (so [navigatorKey] is attached to a Navigator).
|
|
||||||
static Future<void> init() async {
|
|
||||||
debugPrint("AdDeepLinkService: init() called");
|
|
||||||
try {
|
|
||||||
final Uri? initialUri = await _appLinks.getInitialLink();
|
|
||||||
debugPrint("AdDeepLinkService: getInitialLink() -> $initialUri");
|
|
||||||
if (initialUri != null) {
|
|
||||||
_handleUri(initialUri);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("AdDeepLinkService: failed to read initial link - $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
_subscription?.cancel();
|
|
||||||
_subscription = _appLinks.uriLinkStream.listen(
|
|
||||||
(uri) {
|
|
||||||
debugPrint("AdDeepLinkService: uriLinkStream emitted $uri");
|
|
||||||
_handleUri(uri);
|
|
||||||
},
|
|
||||||
onError: (Object e) => debugPrint("AdDeepLinkService: stream error - $e"),
|
|
||||||
);
|
|
||||||
debugPrint("AdDeepLinkService: uriLinkStream subscription active");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _handleUri(Uri uri) {
|
|
||||||
debugPrint("AdDeepLinkService: received $uri");
|
|
||||||
|
|
||||||
// https://ms.hmg.com/ad/1541 -> host="ms.hmg.com", pathSegments=["ad", "1541"]
|
|
||||||
// mowatercustomer://ad/1541 -> host="ad" (Dart treats it as the authority), pathSegments=["1541"]
|
|
||||||
int? adId;
|
|
||||||
if (uri.scheme == 'https') {
|
|
||||||
final segments = uri.pathSegments;
|
|
||||||
if (segments.length >= 2 && segments[0] == 'ad') {
|
|
||||||
adId = int.tryParse(segments[1]);
|
|
||||||
}
|
|
||||||
} else if (uri.host == 'ad' && uri.pathSegments.isNotEmpty) {
|
|
||||||
adId = int.tryParse(uri.pathSegments.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adId == null) return;
|
|
||||||
|
|
||||||
// app_links can emit the same initial uri twice (once via getInitialLink, once as
|
|
||||||
// the stream's first event) -- skip the duplicate rather than run _openAd twice.
|
|
||||||
if (uri == _lastHandledUri) {
|
|
||||||
debugPrint("AdDeepLinkService: ignoring duplicate $uri");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_lastHandledUri = uri;
|
|
||||||
_openAd(adId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deliberately no loading dialog anywhere in here: on a cold start (app was killed),
|
|
||||||
// this races the app's own bootstrap navigation -- confirmed on a real device, the
|
|
||||||
// bootstrap chain is SplashPage's hardcoded 5s timer -> pushReplacementNamed to
|
|
||||||
// registerSelection -> (if a saved login exists) a silent auto-login with its own
|
|
||||||
// showLoading/hideLoading dialog -> pushReplacementNamed again towards the dashboard.
|
|
||||||
// Every one of those calls uses a *captured* BuildContext and mutates the same
|
|
||||||
// Navigator via Navigator.of(context), regardless of what else has since been pushed
|
|
||||||
// on top. If we push the ad route while any of that is still in flight, our route gets
|
|
||||||
// clobbered by the next replacement -- in debug this throws "_elements.contains
|
|
||||||
// (element)"; in release (asserts stripped) it silently wedges the Navigator and the
|
|
||||||
// app hangs on a black screen. So: wait for the current route name to stay the same
|
|
||||||
// across two consecutive polls (i.e. bootstrap has actually settled) before touching
|
|
||||||
// the Navigator or fetching anything.
|
|
||||||
static Future<void> _openAd(int adId) async {
|
|
||||||
debugPrint("AdDeepLinkService: _openAd($adId) called");
|
|
||||||
if (!await _waitUntilRouteSettles()) {
|
|
||||||
debugPrint("AdDeepLinkService: app never settled past bootstrap, aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugPrint("AdDeepLinkService: route settled at '$_currentTopRouteName'");
|
|
||||||
|
|
||||||
// Checking for a saved token here is NOT a reliable "is the user ready" signal --
|
|
||||||
// confirmed on a real device: a stale token from before the app was killed survives
|
|
||||||
// on disk through the whole re-login flow (splash -> registerSelection -> silent
|
|
||||||
// relogin -> loginMethodSelection OTP screen), so it reads as "already logged in"
|
|
||||||
// while still sitting on the OTP screen, fetches too early with no real session
|
|
||||||
// ready yet, and silently fails. The dashboard route is the one signal that's only
|
|
||||||
// true once a real session is actually in place, however the user got there.
|
|
||||||
if (_currentTopRouteName != AppRoutes.dashboard) {
|
|
||||||
debugPrint("AdDeepLinkService: not on dashboard yet ('$_currentTopRouteName'), waiting for login to finish");
|
|
||||||
if (!await _waitForRoute(AppRoutes.dashboard)) {
|
|
||||||
debugPrint("AdDeepLinkService: gave up waiting for dashboard, aborting");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugPrint("AdDeepLinkService: reached dashboard");
|
|
||||||
} else {
|
|
||||||
debugPrint("AdDeepLinkService: already on dashboard, proceeding directly");
|
|
||||||
}
|
|
||||||
|
|
||||||
debugPrint("AdDeepLinkService: fetching ad $adId");
|
|
||||||
AdDetailsModel? adDetails;
|
|
||||||
try {
|
|
||||||
adDetails = await navigatorKey.currentContext?.read<AdVM>().getAdDetailsById(adId);
|
|
||||||
debugPrint("AdDeepLinkService: fetch result adDetails=${adDetails != null ? 'FOUND id=${adDetails.id}' : 'NULL'}");
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint("AdDeepLinkService: failed to fetch ad $adId - $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adDetails == null) {
|
|
||||||
Utils.showToast(LocaleKeys.somethingWrong.tr());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigate via the NavigatorState directly (not a captured BuildContext) so this
|
|
||||||
// still works even if the route/context active when we started has since changed.
|
|
||||||
debugPrint("AdDeepLinkService: pushing ${AppRoutes.adsDetailView}");
|
|
||||||
navigatorKey.currentState?.pushNamed(AppRoutes.adsDetailView, arguments: adDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String? _currentRouteNameIgnoringPlaceholder() {
|
|
||||||
final name = _currentTopRouteName;
|
|
||||||
// The placeholder pops itself a frame after being pushed -- if this poll happens to
|
|
||||||
// land exactly then, pretend nothing was read rather than counting it as a route.
|
|
||||||
return name == ignoredPlaceholderRouteName ? null : name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<bool> _waitUntilRouteSettles({int maxAttempts = 60, Duration retryDelay = const Duration(milliseconds: 300)}) async {
|
|
||||||
String? lastRouteName;
|
|
||||||
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
||||||
final currentRouteName = _currentRouteNameIgnoringPlaceholder();
|
|
||||||
|
|
||||||
if (currentRouteName != null && currentRouteName != AppRoutes.splash && currentRouteName == lastRouteName) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (currentRouteName != null) lastRouteName = currentRouteName;
|
|
||||||
if (attempt < maxAttempts) await Future.delayed(retryDelay);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used both for the short "did bootstrap settle" check and for waiting on the
|
|
||||||
// dashboard specifically, which can be user-paced (entering an OTP, picking a login
|
|
||||||
// method) -- default timeout is generous (5 minutes) to cover that case.
|
|
||||||
static Future<bool> _waitForRoute(String targetRoute, {int maxAttempts = 600, Duration retryDelay = const Duration(milliseconds: 500)}) async {
|
|
||||||
for (int attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
||||||
if (_currentRouteNameIgnoringPlaceholder() == targetRoute) return true;
|
|
||||||
if (attempt < maxAttempts) await Future.delayed(retryDelay);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _RouteNameTracker extends NavigatorObserver {
|
|
||||||
@override
|
|
||||||
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
||||||
AdDeepLinkService._currentTopRouteName = route.settings.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
||||||
AdDeepLinkService._currentTopRouteName = previousRoute?.settings.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
|
||||||
AdDeepLinkService._currentTopRouteName = previousRoute?.settings.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
|
|
||||||
AdDeepLinkService._currentTopRouteName = newRoute?.settings.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,180 +0,0 @@
|
|||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:mc_common_app/classes/app_state.dart';
|
|
||||||
import 'package:mc_common_app/config/routes.dart';
|
|
||||||
import 'package:mc_common_app/models/chat_models/buyers_chat_for_ads_model.dart';
|
|
||||||
import 'package:mc_common_app/models/requests_models/request_model.dart';
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
import 'package:mc_common_app/utils/navigator.dart';
|
|
||||||
|
|
||||||
/// Service for handling navigation to different chat types in the app
|
|
||||||
/// Provides centralized navigation logic for Request, Advertisement, and General chats
|
|
||||||
abstract class ChatNavigationService {
|
|
||||||
/// Navigate to Request Offer Chat screen
|
|
||||||
Future<void> navigateToRequestOfferChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required int requestId,
|
|
||||||
required String receiverId,
|
|
||||||
required String senderId,
|
|
||||||
required int requestIndex,
|
|
||||||
required int providerIndex,
|
|
||||||
required RequestModel requestModel,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Navigate to Advertisement Chat screen
|
|
||||||
Future<void> navigateToAdChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required int adsId,
|
|
||||||
required String receiverUserId,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Navigate to General Chat screen
|
|
||||||
Future<void> navigateToGeneralChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required String receiverUserId,
|
|
||||||
Map<String, dynamic>? additionalData,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Navigate to Chat List screen based on chat type
|
|
||||||
Future<void> navigateToChatList({
|
|
||||||
required BuildContext context,
|
|
||||||
required ChatTypeEnum chatType,
|
|
||||||
Map<String, dynamic>? arguments,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChatNavigationServiceImp implements ChatNavigationService {
|
|
||||||
@override
|
|
||||||
Future<void> navigateToRequestOfferChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required int requestId,
|
|
||||||
required String receiverId,
|
|
||||||
required String senderId,
|
|
||||||
required int requestIndex,
|
|
||||||
required int providerIndex,
|
|
||||||
required RequestModel requestModel,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log("Navigating to Request Offer Chat: requestId=$requestId");
|
|
||||||
|
|
||||||
ChatViewArgumentsForRequest args = ChatViewArgumentsForRequest(
|
|
||||||
chatTypeEnum: ChatTypeEnum.requestOffer,
|
|
||||||
requestId: requestId,
|
|
||||||
receiverId: receiverId,
|
|
||||||
senderId: senderId,
|
|
||||||
requestIndex: requestIndex,
|
|
||||||
providerIndex: providerIndex,
|
|
||||||
requestModel: requestModel,
|
|
||||||
);
|
|
||||||
|
|
||||||
ChatViewArguments chatViewArguments = ChatViewArguments(
|
|
||||||
chatTypeEnum: ChatTypeEnum.requestOffer,
|
|
||||||
chatViewArgumentsForRequest: args,
|
|
||||||
);
|
|
||||||
|
|
||||||
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error navigating to request offer chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> navigateToAdChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required int adsId,
|
|
||||||
required String receiverUserId,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log("Navigating to Ad Chat: adsId=$adsId, receiverId=$receiverUserId");
|
|
||||||
|
|
||||||
ChatViewArgumentsForAd args = ChatViewArgumentsForAd(
|
|
||||||
adsID: adsId,
|
|
||||||
receiverUserID: receiverUserId,
|
|
||||||
);
|
|
||||||
|
|
||||||
ChatViewArguments chatViewArguments = ChatViewArguments(
|
|
||||||
chatTypeEnum: ChatTypeEnum.ads,
|
|
||||||
chatViewArgumentsForAd: args,
|
|
||||||
);
|
|
||||||
|
|
||||||
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error navigating to ad chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> navigateToGeneralChat({
|
|
||||||
required BuildContext context,
|
|
||||||
required String receiverUserId,
|
|
||||||
Map<String, dynamic>? additionalData,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log("Navigating to General Chat: receiverId=$receiverUserId");
|
|
||||||
|
|
||||||
// General chat implementation
|
|
||||||
// You can extend this based on your general chat requirements
|
|
||||||
ChatViewArguments chatViewArguments = ChatViewArguments(
|
|
||||||
chatTypeEnum: ChatTypeEnum.general,
|
|
||||||
);
|
|
||||||
|
|
||||||
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
|
|
||||||
} catch (e) {
|
|
||||||
log("Error navigating to general chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> navigateToChatList({
|
|
||||||
required BuildContext context,
|
|
||||||
required ChatTypeEnum chatType,
|
|
||||||
Map<String, dynamic>? arguments,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
log("Navigating to Chat List: type=$chatType");
|
|
||||||
|
|
||||||
switch (chatType) {
|
|
||||||
case ChatTypeEnum.requestOffer:
|
|
||||||
// Navigate to Providers Chat List (for customer side)
|
|
||||||
// or Requests list (for provider side)
|
|
||||||
if (AppState().currentAppType == AppType.customer) {
|
|
||||||
navigateWithName(context, AppRoutes.providersChatListPage);
|
|
||||||
} else {
|
|
||||||
// For provider, navigate to requests page
|
|
||||||
navigateWithName(context, AppRoutes.dashboard);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ChatTypeEnum.ads:
|
|
||||||
// Navigate to Ads Buyers Chat List
|
|
||||||
if (arguments != null && arguments['buyers'] != null) {
|
|
||||||
navigateWithName(
|
|
||||||
context,
|
|
||||||
AppRoutes.adsBuyerChatsListView,
|
|
||||||
arguments: arguments['buyers'] as List<BuyersChatForAdsModel>,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Navigate to ads list if buyers not provided
|
|
||||||
navigateWithName(context, AppRoutes.dashboard);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ChatTypeEnum.general:
|
|
||||||
// Navigate to General Chats List
|
|
||||||
if (arguments != null && arguments['requestId'] != null) {
|
|
||||||
navigateWithName(
|
|
||||||
context,
|
|
||||||
AppRoutes.generalChatsListForProvider,
|
|
||||||
arguments: arguments['requestId'] as int,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
navigateWithName(context, AppRoutes.dashboard);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
log("Error navigating to chat list: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,402 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'dart:developer';
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:mc_common_app/classes/app_state.dart';
|
|
||||||
import 'package:mc_common_app/config/dependency_injection.dart';
|
|
||||||
import 'package:mc_common_app/config/routes.dart';
|
|
||||||
import 'package:mc_common_app/main.dart';
|
|
||||||
import 'package:mc_common_app/services/chat_navigation_service.dart';
|
|
||||||
import 'package:mc_common_app/utils/enums.dart';
|
|
||||||
import 'package:mc_common_app/utils/navigator.dart';
|
|
||||||
import 'package:mc_common_app/view_models/chat_view_model.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
abstract class FirebaseMessagingService {
|
|
||||||
Future<String> getFirebaseToken();
|
|
||||||
|
|
||||||
Future<void> initializeNotifications();
|
|
||||||
}
|
|
||||||
|
|
||||||
class FirebaseMessagingServiceImp implements FirebaseMessagingService {
|
|
||||||
final FlutterLocalNotificationsPlugin flutterLocalNotifications;
|
|
||||||
final FirebaseMessaging firebaseMessaging;
|
|
||||||
|
|
||||||
FirebaseMessagingServiceImp({required this.firebaseMessaging, required this.flutterLocalNotifications});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<String> getFirebaseToken() async => await firebaseMessaging.getToken() ?? "";
|
|
||||||
|
|
||||||
Future<void> requestPermissions() async {
|
|
||||||
if (Platform.isIOS) {
|
|
||||||
await flutterLocalNotifications.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()?.requestPermissions(alert: true, badge: true, sound: true);
|
|
||||||
} else if (Platform.isAndroid) {
|
|
||||||
AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
|
|
||||||
bool? granted = await androidImplementation?.requestNotificationsPermission();
|
|
||||||
if (granted == false) {
|
|
||||||
await Permission.notification.request();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> initializeNotifications() async {
|
|
||||||
await requestPermissions();
|
|
||||||
AppState().setDeviceToken = await getFirebaseToken();
|
|
||||||
AppState().setDeviceType = Platform.isAndroid ? "1" : "2";
|
|
||||||
await Permission.notification.isDenied.then((bool value) {
|
|
||||||
if (value) {
|
|
||||||
Permission.notification.request();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create high-priority notification channel for Android (required for terminated state)
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
const AndroidNotificationChannel channel = AndroidNotificationChannel(
|
|
||||||
'mowater_chat_channel', // More descriptive channel ID
|
|
||||||
'Chat Notifications',
|
|
||||||
description: 'Notifications for chat messages',
|
|
||||||
importance: Importance.max,
|
|
||||||
// Changed to max for Android 14
|
|
||||||
playSound: true,
|
|
||||||
enableVibration: true,
|
|
||||||
showBadge: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
await flutterLocalNotifications.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
|
|
||||||
|
|
||||||
log("✅ Android notification channel created: ${channel.id}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
|
|
||||||
const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('ic_launcher');
|
|
||||||
DarwinInitializationSettings initializationSettingsDarwin = const DarwinInitializationSettings(
|
|
||||||
requestAlertPermission: true,
|
|
||||||
requestBadgePermission: true,
|
|
||||||
requestCriticalPermission: true,
|
|
||||||
requestProvisionalPermission: true,
|
|
||||||
requestSoundPermission: true,
|
|
||||||
defaultPresentAlert: true,
|
|
||||||
defaultPresentBadge: true,
|
|
||||||
defaultPresentBanner: true,
|
|
||||||
defaultPresentList: true,
|
|
||||||
defaultPresentSound: true,
|
|
||||||
);
|
|
||||||
final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsDarwin);
|
|
||||||
await flutterLocalNotifications.initialize(
|
|
||||||
initializationSettings,
|
|
||||||
onDidReceiveNotificationResponse: (NotificationResponse response) async {
|
|
||||||
// Handle notification tap when app is in foreground/background
|
|
||||||
log("========== Notification Tapped ==========");
|
|
||||||
log("Notification ID: ${response.id}");
|
|
||||||
log("Action ID: ${response.actionId}");
|
|
||||||
log("Input: ${response.input}");
|
|
||||||
log("Payload: ${response.payload}");
|
|
||||||
log("=========================================");
|
|
||||||
|
|
||||||
if (response.payload != null && response.payload!.isNotEmpty) {
|
|
||||||
try {
|
|
||||||
Map<String, dynamic> data = jsonDecode(response.payload!);
|
|
||||||
log("Parsed payload data: $data");
|
|
||||||
_handleNotificationNavigation(data);
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("Error parsing notification payload: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
|
|
||||||
|
|
||||||
if (initialMessage != null) _handleNotification(initialMessage);
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
||||||
// Foreground notification - just display, don't navigate automatically
|
|
||||||
if (message.notification != null) _handleForegroundNotification(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationOpenedApp);
|
|
||||||
|
|
||||||
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
|
|
||||||
|
|
||||||
FirebaseMessaging.instance.onTokenRefresh.listen((String token) async {
|
|
||||||
AppState().setDeviceToken = token;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onDidReceiveLocalNotification(int id, String? title, String? body, String? payload) async {
|
|
||||||
logger.i(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleForegroundNotification(RemoteMessage message) async {
|
|
||||||
log("========== Foreground Notification Received ==========");
|
|
||||||
log("Title: ${message.notification!.title}");
|
|
||||||
log("Body: ${message.notification!.body}");
|
|
||||||
log("Data payload: ${message.data}");
|
|
||||||
log("Message ID: ${message.messageId}");
|
|
||||||
log("Sent time: ${message.sentTime}");
|
|
||||||
log("Android config: ${message.toMap()}");
|
|
||||||
log("======================================================");
|
|
||||||
|
|
||||||
// Just display notification - DON'T navigate automatically
|
|
||||||
// Navigation happens only when user taps the notification
|
|
||||||
displayNotification(
|
|
||||||
title: message.notification!.title ?? '',
|
|
||||||
body: message.notification!.body ?? '',
|
|
||||||
payload: jsonEncode(message.data),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleNotificationOpenedApp(RemoteMessage message) async {
|
|
||||||
log("========== Notification Opened App ==========");
|
|
||||||
log("Title: ${message.notification!.title}");
|
|
||||||
log("Body: ${message.notification!.body}");
|
|
||||||
log("Data payload: ${message.data}");
|
|
||||||
log("Message ID: ${message.messageId}");
|
|
||||||
log("Sent time: ${message.sentTime}");
|
|
||||||
log("=============================================");
|
|
||||||
|
|
||||||
// User tapped notification - navigate to chat
|
|
||||||
_handleNotificationNavigation(message.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleNotification(RemoteMessage message) async {
|
|
||||||
// This is called when app is terminated and opened by notification
|
|
||||||
log("========== App Terminated Notification ==========");
|
|
||||||
log("Title: ${message.notification!.title}");
|
|
||||||
log("Body: ${message.notification!.body}");
|
|
||||||
log("Data payload: ${message.data}");
|
|
||||||
log("Message ID: ${message.messageId}");
|
|
||||||
log("Sent time: ${message.sentTime}");
|
|
||||||
log("=================================================");
|
|
||||||
|
|
||||||
// User opened app from notification - navigate to chat
|
|
||||||
_handleNotificationNavigation(message.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleNotificationNavigation(Map<String, dynamic> data) async {
|
|
||||||
log("========== Handling Notification Navigation ==========");
|
|
||||||
log("Full data received: $data");
|
|
||||||
|
|
||||||
if (data.isEmpty) {
|
|
||||||
log("Data is empty, skipping navigation");
|
|
||||||
log("======================================================");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backend sends NotiType as string: "1" = Request, "2" = Ads, "3" = General
|
|
||||||
String? notiTypeStr = data['NotiType'];
|
|
||||||
int? notiType = int.tryParse(notiTypeStr ?? '');
|
|
||||||
|
|
||||||
log("NotiType from backend: $notiTypeStr (parsed: $notiType)");
|
|
||||||
|
|
||||||
if (notiType == null || notiType < 1 || notiType > 3) {
|
|
||||||
log("Invalid or missing NotiType, skipping navigation");
|
|
||||||
log("======================================================");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map NotiType to chat type
|
|
||||||
String chatType;
|
|
||||||
switch (notiType) {
|
|
||||||
case 1:
|
|
||||||
chatType = 'request_offer';
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
chatType = 'ad_chat';
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
chatType = 'general_chat';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log("Unknown NotiType: $notiType");
|
|
||||||
log("======================================================");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log("Mapped chat type: $chatType");
|
|
||||||
|
|
||||||
// Wait a bit for context to be available
|
|
||||||
await Future.delayed(Duration(milliseconds: 300));
|
|
||||||
|
|
||||||
BuildContext? context = navigatorKey.currentContext;
|
|
||||||
if (context == null) {
|
|
||||||
logger.e("Context not available for navigation");
|
|
||||||
log("======================================================");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log all available data fields to understand backend format
|
|
||||||
log("Available data fields:");
|
|
||||||
data.forEach((key, value) {
|
|
||||||
log(" $key: $value");
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (chatType) {
|
|
||||||
case 'request_offer':
|
|
||||||
log("Navigating to Request Offer Chat...");
|
|
||||||
await _navigateToRequestOfferChat(context, data);
|
|
||||||
break;
|
|
||||||
case 'ad_chat':
|
|
||||||
log("Navigating to Advertisement Chat...");
|
|
||||||
await _navigateToAdChat(context, data);
|
|
||||||
break;
|
|
||||||
case 'general':
|
|
||||||
log("Navigating to General Chat...");
|
|
||||||
await _navigateToGeneralChat(context, data);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger.e("Unknown chat type: $chatType");
|
|
||||||
}
|
|
||||||
|
|
||||||
log("======================================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _navigateToRequestOfferChat(
|
|
||||||
BuildContext context,
|
|
||||||
Map<String, dynamic> data,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
// Try both backend format and our format
|
|
||||||
int requestId = int.tryParse(data['RequestID']?.toString() ?? data['request_id']?.toString() ?? '0') ?? 0;
|
|
||||||
|
|
||||||
if (requestId == 0) {
|
|
||||||
log("❌ Invalid or missing RequestID in notification data");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AppState().currentAppType == AppType.customer) {
|
|
||||||
// Customer notification tap: land on this request's offers list, matching
|
|
||||||
// the normal (non-notification) customer flow in requests_fragment.dart.
|
|
||||||
// ProvidersChatListPage below is provider-only (it reads the current
|
|
||||||
// user's own providerId), so routing a customer there fails.
|
|
||||||
log("✅ Navigating to offers list for request: $requestId");
|
|
||||||
navigateWithName(context, AppRoutes.offersListPage, arguments: requestId);
|
|
||||||
} else {
|
|
||||||
log("✅ Navigating to providers chat list for request: $requestId");
|
|
||||||
navigateWithName(context, AppRoutes.providersChatListPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: If backend sends all required data (ProviderId, CustomerId),
|
|
||||||
// we can navigate directly to the chat screen
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("Error navigating to request chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _navigateToAdChat(
|
|
||||||
BuildContext context,
|
|
||||||
Map<String, dynamic> data,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
// Try both backend format and our format
|
|
||||||
int adId = int.tryParse(data['AdID']?.toString() ?? data['ad_id']?.toString() ?? '0') ?? 0;
|
|
||||||
|
|
||||||
// The "other participant" for the chat we're about to open/keep messaging in is
|
|
||||||
// whoever SENT the message that triggered this notification — NOT whatever the
|
|
||||||
// payload calls the "receiver". Backend has repeatedly mixed up sender/receiver
|
|
||||||
// fields in this payload (see firebase_notification_payload_format.md); if we
|
|
||||||
// blindly trust a "receiver" field that turns out to be OUR OWN id, every message
|
|
||||||
// we send afterwards gets addressed back to ourselves, and we start getting
|
|
||||||
// pushed notifications for our own messages. Prefer sender_id; fall back to the
|
|
||||||
// old receiver fields only if sender fields are missing entirely.
|
|
||||||
String? receiverUserId = data['SenderUserID'] ?? data['sender_id'] ?? data['ReceiverUserID'] ?? data['receiver_id'];
|
|
||||||
|
|
||||||
log("📦 Ad chat notification payload fields — "
|
|
||||||
"AdID: ${data['AdID']}, ad_id: ${data['ad_id']}, "
|
|
||||||
"SenderUserID: ${data['SenderUserID']}, sender_id: ${data['sender_id']}, "
|
|
||||||
"ReceiverUserID: ${data['ReceiverUserID']}, receiver_id: ${data['receiver_id']} "
|
|
||||||
"-> resolved receiverUserId: $receiverUserId");
|
|
||||||
|
|
||||||
final String? myUserId = AppState().getUser.data?.userInfo?.userId?.toString();
|
|
||||||
if (myUserId != null && receiverUserId == myUserId) {
|
|
||||||
log("⚠️ Resolved receiverUserId ($receiverUserId) matches the current user's own id — "
|
|
||||||
"the notification payload is likely still misidentifying sender/receiver. "
|
|
||||||
"Proceeding anyway, but messages sent from this chat may misroute.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adId == 0 || receiverUserId == null) {
|
|
||||||
log("❌ Invalid AdID or sender/receiver id in notification data");
|
|
||||||
log(" AdID: ${data['AdID']}, resolved receiverUserId: $receiverUserId");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user is already on this specific chat
|
|
||||||
// Try to get ChatVM from Provider, but don't fail if not available
|
|
||||||
try {
|
|
||||||
final chatVM = Provider.of<ChatVM>(context, listen: false);
|
|
||||||
if (chatVM.isOnAdChat(adId, receiverUserId)) {
|
|
||||||
log("✅ User already on ad chat (adId=$adId), skipping navigation");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// ChatVM not available in context, proceed with navigation
|
|
||||||
log("⚠️ Could not check chat context: $e");
|
|
||||||
}
|
|
||||||
|
|
||||||
log("✅ Navigating to ad chat: adId=$adId");
|
|
||||||
final chatNavService = injector.get<ChatNavigationService>();
|
|
||||||
await chatNavService.navigateToAdChat(
|
|
||||||
context: context,
|
|
||||||
adsId: adId,
|
|
||||||
receiverUserId: receiverUserId,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("Error navigating to ad chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _navigateToGeneralChat(
|
|
||||||
BuildContext context,
|
|
||||||
Map<String, dynamic> data,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
// Try both backend format and our format
|
|
||||||
int requestId = int.tryParse(data['RequestID']?.toString() ?? data['request_id']?.toString() ?? '0') ?? 0;
|
|
||||||
|
|
||||||
if (requestId == 0) {
|
|
||||||
log("❌ Invalid or missing RequestID in notification data");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log("✅ Navigating to general chat list for request: $requestId");
|
|
||||||
navigateWithName(
|
|
||||||
context,
|
|
||||||
AppRoutes.generalChatsListForProvider,
|
|
||||||
arguments: requestId,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
logger.e("Error navigating to general chat: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> displayNotification({
|
|
||||||
required String title,
|
|
||||||
required String body,
|
|
||||||
String? payload,
|
|
||||||
}) async {
|
|
||||||
const AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
|
|
||||||
'mowater_chat_channel', // Match the channel ID we created
|
|
||||||
'Chat Notifications',
|
|
||||||
channelDescription: 'Notifications for chat messages',
|
|
||||||
importance: Importance.max,
|
|
||||||
priority: Priority.high,
|
|
||||||
ticker: 'ticker',
|
|
||||||
);
|
|
||||||
|
|
||||||
const DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(threadIdentifier: 'thread_id');
|
|
||||||
const NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails, iOS: iOSPlatformChannelSpecifics);
|
|
||||||
await flutterLocalNotifications.show(0, title, body, notificationDetails, payload: payload ?? 'item x');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
|
||||||
Future<dynamic> backgroundMessageHandler(RemoteMessage message) async {
|
|
||||||
await Firebase.initializeApp();
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue