This commit is contained in:
Hamza-Ayed
2024-11-09 10:49:04 +02:00
parent fc81405b7a
commit 213c2724aa
44 changed files with 3009 additions and 1130 deletions

View File

@@ -12,6 +12,7 @@ class BoxName {
static const String packagInfo = "packagInfo";
static const String isVerified = 'isVerified';
static const String isFirstTime = 'isFirstTime';
static const String isSavedPhones = 'isSavedPhones';
static const String statusDriverLocation = "statusDriverLocation";
static const String isTest = "isTest";
static const String password = "password";

View File

@@ -57,17 +57,21 @@ class AppLink {
static String deletePassengersPromo = "$promo/delete.php";
static String updatePassengersPromo = "$promo/update.php";
//===============contact==========================
static String savePhones = "$server/ride/egyptPhones/add.php";
static String getPhones = "$server/ride/egyptPhones/get.php";
////=======================cancelRide===================
static String ride = '$server/ride';
static String addCancelRideFromPassenger = "$server/ride/cancelRide/add.php";
static String cancelRide = "$server/ride/cancelRide/get.php";
//-----------------ridessss------------------
static String addRides = "$ride/rides/add.php";
static String getRides = "$server/ride/rides/get.php";
static String getRideOrderID = "$server/ride/rides/getRideOrderID.php";
static String getRideStatus = "$server/ride/rides/getRideStatus.php";
static String getRides = "$endPoint/ride/rides/get.php";
static String getRideOrderID = "$endPoint/ride/rides/getRideOrderID.php";
static String getRideStatus = "$endPoint/ride/rides/getRideStatus.php";
static String getRideStatusBegin =
"$server/ride/rides/getRideStatusBegin.php";
"$endPoint/ride/rides/getRideStatusBegin.php";
static String getRideStatusFromStartApp =
"$ride/rides/getRideStatusFromStartApp.php";
static String updateRides = "$server/ride/rides/update.php";
@@ -116,6 +120,17 @@ class AppLink {
"$ride/notificationCaptain/update.php";
static String deleteNotificationCaptain =
"$ride/notificationCaptain/delete.php";
//-----------------invitor------------------
static String addInviteDriver = "$server/ride/invitor/add.php";
static String addInvitationPassenger =
"$server/ride/invitor/addInvitationPassenger.php";
static String getInviteDriver = "$server/ride/invitor/get.php";
static String getDriverInvitationToPassengers =
"$server/ride/invitor/getDriverInvitationToPassengers.php";
static String updateInviteDriver = "$server/ride/invitor/update.php";
static String updatePassengerGift =
"$server/ride/invitor/updatePassengerGift.php";
//-----------------Api Key------------------
static String addApiKey = "$ride/apiKey/add.php";
static String getApiKey = "$ride/apiKey/get.php";
@@ -125,6 +140,7 @@ class AppLink {
//-----------------Feed Back------------------
static String addFeedBack = "$ride/feedBack/add.php";
static String uploadAudio = "$ride/feedBack/upload_audio.php";
static String getFeedBack = "$ride/feedBack/get.php";
static String updateFeedBack = "$ride/feedBack/updateFeedBack.php";

View File

@@ -0,0 +1,22 @@
List<String> messages = [
"🚗 عروض مميزة: استمتع بأقل الأسعار وأفضل العروض! افتح تطبيق سفر الآن لتحصل على المزيد من الخيارات. 🌟",
"💸 وفر الآن: وفر مع تطبيق سفر! عروض مستمرة وخيارات متعددة تناسب احتياجاتك. 🔥",
"🔒 أمان وراحة: مع تطبيق سفر، احصل على أمان وراحة بأفضل الأسعار! 🚕",
"💼 خيارات متنوعة: استفد من خيارات متنوعة وأسعار تنافسية على تطبيق سفر، الأفضل دائماً. 🌐",
"💵 توفير مضمون: حافظ على ميزانيتك وسافر بأمان مع تطبيق سفر العروض لا تتوقف! 🎉",
"🌍 وجهات مميزة: أفضل وجهات السفر، بأقل الأسعار مع تطبيق سفر تابعنا الآن! 🛤️",
"🛣️ سهولة وراحة: رحلاتك أصبحت أسهل وأرخص سافر معنا وتمتع بأفضل التجارب. 🎊",
"📲 حجز سهل: احجز رحلتك بسهولة وأمان مع سفر المزيد من الخصومات في انتظارك! 🎁",
"👑 فئة مميزة: خليك من الفئة المميزة واستفد بأفضل الأسعار مع تطبيق سفر. 💯",
"💡 خيارات متعددة: نوفر لك خيارات متعددة وسعر مناسب جرب تطبيق سفر الآن! 🚖",
"✨ عروض متجددة: العروض لا تتوقف على تطبيق سفر احجز رحلتك الآن وتمتع بالمزيد! 📅",
"🚀 سهولة الوصول: السفر أصبح أسهل وأسرع مع تطبيق سفر كن مستعدًا لأفضل التجارب! 🌠",
"🧳 راحة وأمان: تطبيق سفر يقدم لك أمان وراحة بأقل الأسعار! 📉",
"🔥 عروض فورية: احجز الآن واستمتع بعروض لا تُفوّت على تطبيق سفر! 🚘",
"🚖 أسعار تنافسية: اختر رحلتك الآن بأسعار تنافسية وتمتع بالراحة والأمان مع تطبيق سفر. ✅",
"💥 أسعار خاصة: أسعار خاصة بانتظارك على تطبيق سفر! افتح التطبيق الآن واحجز رحلتك. 🌐",
"🌟 راحة البال: انطلق بأمان وراحة مع تطبيق سفر استمتع بأفضل الأسعار. 💸",
"📍 خصومات حصرية: استفد من الخصومات الحصرية والعروض المستمرة على تطبيق سفر! 🛤️",
"🛫 تجربة سهلة: رحلاتك أصبحت أفضل وأسهل مع تطبيق سفر افتح التطبيق واستمتع بالتجربة. ✨",
"🔔 عروض لا مثيل لها: كن جاهزًا لعروض لا مثيل لها! تطبيق سفر يقدم لك أفضل الخيارات بأقل الأسعار. 🎉",
];

View File

@@ -43,7 +43,9 @@ class AppStyle {
static BoxDecoration boxDecoration = const BoxDecoration(
boxShadow: [
BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(2, 4)),
color: Color.fromARGB(255, 218, 218, 255),
blurRadius: 5,
offset: Offset(2, 4)),
BoxShadow(
color: AppColor.accentColor, blurRadius: 5, offset: Offset(-2, -2))
],

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:SEFER/constant/info.dart';
import 'package:SEFER/controller/firebase/firbase_messge.dart';
import 'package:SEFER/controller/functions/add_error.dart';
import 'package:SEFER/views/auth/login_page.dart';
import 'package:SEFER/views/auth/sms_verfy_page.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
@@ -15,6 +16,7 @@ import 'package:SEFER/main.dart';
import 'package:SEFER/views/home/map_page_passenger.dart';
import 'package:location/location.dart';
import '../../print.dart';
import '../functions/package_info.dart';
class LoginController extends GetxController {
@@ -90,23 +92,22 @@ class LoginController extends GetxController {
} else {
var jsonDecoeded = jsonDecode(res);
if (jsonDecoeded.isNotEmpty) {
var d = jsonDecoeded['data'][0];
if (jsonDecoeded['status'] == 'success' &&
jsonDecoeded['data'][0]['verified'].toString() == '1') {
d['verified'].toString() == '1') {
//
box.write(BoxName.isVerified, '1');
box.write(BoxName.email, jsonDecoeded['data'][0]['email']);
box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']);
box.write(BoxName.email, d['email']);
box.write(BoxName.phone, d['phone']);
box.write(BoxName.isTest, '1');
box.write(BoxName.package, jsonDecoeded['data'][0]['package']);
box.write(BoxName.promo, jsonDecoeded['data'][0]['promo']);
box.write(BoxName.discount, jsonDecoeded['data'][0]['discount']);
box.write(BoxName.validity, jsonDecoeded['data'][0]['validity']);
box.write(BoxName.isInstall,
jsonDecoeded['data'][0]['isInstall'] ?? 'none');
box.write(BoxName.isGiftToken,
jsonDecoeded['data'][0]['isGiftToken'] ?? 'none');
box.write(BoxName.inviteCode,
jsonDecoeded['data'][0]['inviteCode'] ?? 'none');
box.write(BoxName.package, d['package']);
box.write(BoxName.promo, d['promo']);
box.write(BoxName.discount, d['discount']);
box.write(BoxName.validity, d['validity']);
box.write(BoxName.isInstall, d['isInstall'] ?? 'none');
box.write(BoxName.isGiftToken, d['isGiftToken'] ?? 'none');
box.write(BoxName.inviteCode, d['inviteCode'] ?? 'none');
var token = await CRUD().get(link: AppLink.getTokens, payload: {
'passengerID': box.read(BoxName.passengerID).toString()
@@ -114,11 +115,11 @@ class LoginController extends GetxController {
if (token != 'failure') {
if (jsonDecode(token)['data'][0]['token'] !=
box.read(BoxName.tokenFCM)) {
Get.put(FirebaseMessagesController())
.sendNotificationToAnyWithoutData(
Get.put(FirebaseMessagesController()).sendNotificationToDriverMAP(
'token change'.tr,
'change device'.tr,
jsonDecode(token)['data'][0]['token'].toString(),
[],
'cancel.wav',
);
Future.delayed(const Duration(seconds: 1));
@@ -153,10 +154,10 @@ class LoginController extends GetxController {
Get.offAll(() => const MapPagePassenger());
},
);
} else {
print('same');
}
} // Logging to check if inviteCode is written correctly
print("Invite Code in Box: ${box.read(BoxName.inviteCode)}");
print("Is Install: ${box.read(BoxName.isInstall)}");
if (box.read(BoxName.inviteCode).toString() != 'none' &&
box.read(BoxName.isInstall).toString() != '1') {
@@ -171,15 +172,22 @@ class LoginController extends GetxController {
middleText: "Your invite code was successfully applied!"
.tr, // Automatically translates based on the current locale
onConfirm: () {
CRUD().post(link: AppLink.addPassengersPromo, payload: {
"promoCode":
'S-${box.read(BoxName.name).toString().split(' ')[0]}',
"amount": '25',
"passengerID": box.read(BoxName.passengerID).toString(),
"description": 'promo first'
});
Get.offAll(() =>
const MapPagePassenger()); // Navigate to MapPagePassenger after confirmation
try {
CRUD().post(link: AppLink.addPassengersPromo, payload: {
"promoCode":
'S-${box.read(BoxName.name).toString().split(' ')[0]}',
"amount": '25',
"passengerID": box.read(BoxName.passengerID).toString(),
"description": 'promo first'
});
} catch (e) {
addError(e.toString(),
'passenger Invitation Used dialogu as promo line 185 login_controller');
} finally {
// Continue with the rest of your flow, regardless of errors
// For example, navigate to the next page
Get.offAll(() => const MapPagePassenger());
}
},
textConfirm: "OK".tr, // Confirm button text
);
@@ -200,34 +208,6 @@ class LoginController extends GetxController {
}
}
// void adminDashboardOpen() async {
// if (formKeyAdmin.currentState!.validate()) {
// await DeviceInfoPlus.getDeviceInfo();
// if (Platform.isAndroid) {
// // var res = await CRUD().get(link: AppLink.getAdminUser, payload: {
// // // 'device_number': DeviceInfoPlus.deviceData['serialNumber'].toString(),
// // });
// // var d = jsonDecode(res);
// // // if (DeviceInfoPlus.deviceData['serialNumber'] ==
// // d['message']['device_number']) {
// Get.back();
// Get.to(() => const AdminHomePage());
// // }
// }
// if (Platform.isIOS) {
// // var res = await CRUD().get(link: AppLink.getAdminUser, payload: {
// // 'device_number': DeviceInfoPlus.deviceData['identifierForVendor'].toString(),
// // });
// // var d = jsonDecode(res);
// // if (DeviceInfoPlus.deviceData['serialNumber'] ==
// // d['message']['device_number']) {
// Get.back();
// Get.to(() => const AdminHomePage());
// // }
// }
// }
// }
void login() async {
isloading = true;
update();

View File

@@ -4,6 +4,7 @@ import 'dart:math';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/controller/auth/login_controller.dart';
import 'package:SEFER/controller/functions/add_error.dart';
import 'package:SEFER/controller/local/phone_intel/phone_number.dart';
import 'package:SEFER/views/home/map_page_passenger.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
@@ -134,66 +135,51 @@ class RegisterController extends GetxController {
String phoneNumber = phoneController.text;
// Check if the phone number is from Egypt (Assuming Egyptian numbers start with +20)
bool isEgyptianNumber = phoneNumber.startsWith('+20');
// print('dfdf${phoneNumber.toString().split('+2')[1]}');
if (isEgyptianNumber && phoneNumber.length == 13) {
// Check if the phone number is already verified
var responseChecker = await CRUD().post(
link: AppLink.checkPhoneNumberISVerfiedPassenger,
payload: {
'phone_number': phoneNumber,
'email': box.read(BoxName.email),
},
);
if (responseChecker != 'failure') {
var data = jsonDecode(responseChecker);
if (phoneController.text.isNotEmpty) {
bool isEgyptianNumber = phoneNumber.startsWith('+20');
if (isEgyptianNumber && phoneNumber.length == 13) {
// Check if the phone number is already verified
var responseChecker = await CRUD().post(
link: AppLink.checkPhoneNumberISVerfiedPassenger,
payload: {
'phone_number': phoneNumber,
'email': box.read(BoxName.email),
},
);
// If the phone number is already verified
if (data['message'][0]['verified'].toString() == '1') {
Get.snackbar('Phone number is verified before'.tr, '',
backgroundColor: AppColor.greenColor);
box.write(BoxName.isVerified, '1');
box.write(BoxName.phone, phoneNumber);
Get.offAll(const MapPagePassenger());
if (responseChecker != 'failure') {
var data = jsonDecode(responseChecker);
// If the phone number is already verified
if (data['message'][0]['verified'].toString() == '1') {
Get.snackbar('Phone number is verified before'.tr, '',
backgroundColor: AppColor.greenColor);
box.write(BoxName.isVerified, '1');
box.write(BoxName.phone, phoneNumber);
Get.offAll(const MapPagePassenger());
} else {
await sendOtp(phoneNumber, randomNumber, isEgyptianNumber,
smsEgyptController);
}
} else {
// If the phone number is not verified, send OTP
// if (isEgyptianNumber) {
// if (isValidEgyptianPhoneNumber(
// phoneNumber.toString().split('+2')[1])) {
await sendOtp(phoneNumber, randomNumber, isEgyptianNumber,
smsEgyptController);
// }
// }
}
} else {
// If verification check fails, still send OTP
// if (isEgyptianNumber) {
// if (isValidEgyptianPhoneNumber(
// phoneNumber.toString().split('+2')[1])) {
await sendOtp(
sendOtp(
phoneNumber, randomNumber, isEgyptianNumber, smsEgyptController);
// } else {
// MyDialog().getDialog(
// 'Error'.tr, "Phone number isn't an Egyptian phone number".tr,
// () {
// Get.back();
// });
// }
// }
}
} else {
// MyDialog().getDialog(
// 'Error'.tr, 'Phone number must be exactly 11 digits long'.tr, () {
// Get.back();
// });
sendOtp(
phoneNumber, randomNumber, isEgyptianNumber, smsEgyptController);
print(phoneNumber);
MyDialog().getDialog(
'Error'.tr, 'Phone number must be exactly 11 digits long'.tr, () {
Get.back();
});
// sendOtp(
// phoneNumber, randomNumber, isEgyptianNumber, smsEgyptController);
}
} catch (e) {
// Handle error
print('Error: $e');
} finally {
isLoading = false;
update();
@@ -205,34 +191,14 @@ class RegisterController extends GetxController {
SmsEgyptController controller) async {
// Trim any leading or trailing whitespace from the phone number
phoneNumber = phoneNumber.trim();
Log.print('phoneNumber: ${phoneNumber}');
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
'phone_number': phoneNumber,
'token': otp.toString(),
});
if (isEgyptian) {
// // Check if the phone number has exactly 11 digits
// if (phoneNumber.length == 11 &&
// RegExp(r'^\d{11}$').hasMatch(phoneNumber)) {
// Send SMS for Egyptian phone numbers
await CRUD().post(link: AppLink.sendVerifyOtpMessage, payload: {
'phone_number': phoneNumber,
'token': otp.toString(),
// 'urlImage': box.read(BoxName.passengerPhotoUrl),
// 'name': box.read(BoxName.name),
});
await controller.sendSmsEgypt(phoneNumber, otp.toString());
print('SMS sent to Egyptian phone number: $phoneNumber');
} else {
// // Show error dialog if phone number is invalid
// MyDialog().getDialog('Invalid Phone Number',
// 'The phone number must be exactly 11 digits long.', () {
// Get.back();
// });
// }
// else {
// Send WhatsApp message for non-Egyptian phone numbers
await CRUD().sendWhatsAppAuth(phoneNumber, otp.toString());
print('WhatsApp message sent to non-Egyptian phone number: $phoneNumber');
}
isLoading = false;
@@ -243,98 +209,72 @@ class RegisterController extends GetxController {
}
verifySMSCode() async {
// if (formKey3.currentState!.validate()) {
Log.print('phoneController.text: ${phoneController.text}');
// if (isValidEgyptianPhoneNumber(phoneController.text)) {
var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: {
'phone_number': phoneController.text,
'token': verifyCode.text.toString(),
});
if (res != 'failure') {
// var dec = jsonDecode(res);
box.write(BoxName.phoneDriver, phoneController.text);
var payload = {
'id': box.read(BoxName.passengerID),
'phone': phoneController.text,
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown',
'first_name': box.read(BoxName.name).toString().split(' ')[0],
'last_name': box.read(BoxName.name).toString().split(' ')[1],
};
try {
if (formKey3.currentState!.validate()) {
var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: {
'phone_number': phoneController.text,
'token': verifyCode.text.toString(),
});
var res1 = await CRUD().post(
link: AppLink.signUp,
payload: payload,
);
if (res1 != 'failure') {
CRUD().post(
link: '${AppLink.seferAlexandriaServer}/auth/signup.php',
payload: payload,
);
CRUD().post(
link: '${AppLink.seferGizaServer}/auth/signup.php',
payload: payload,
);
box.write(BoxName.isVerified, '1');
box.write(BoxName.isFirstTime, '0');
box.write(BoxName.phone, phoneController.text);
// Get.offAll(const MapPagePassenger());
Get.put(LoginController()).loginUsingCredentials(
box.read(BoxName.passengerID).toString(),
box.read(BoxName.email).toString(),
);
if (res != 'failure') {
box.write(BoxName.phoneDriver, phoneController.text);
var nameParts = box.read(BoxName.name).toString().split(' ');
var firstName = nameParts.isNotEmpty ? nameParts[0] : 'unknown';
var lastName = nameParts.length > 1 ? nameParts[1] : 'unknown';
var payload = {
'id': box.read(BoxName.passengerID),
'phone': phoneController.text,
'email': box.read(BoxName.email),
'password': 'unknown',
'gender': 'unknown',
'birthdate': '2002-01-01',
'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown',
'first_name': firstName,
'last_name': lastName,
};
var res1 = await CRUD().post(
link: AppLink.signUp,
payload: payload,
);
if (res1 != 'failure') {
await CRUD().post(
link: '${AppLink.seferAlexandriaServer}/auth/signup.php',
payload: payload,
);
await CRUD().post(
link: '${AppLink.seferGizaServer}/auth/signup.php',
payload: payload,
);
box.write(BoxName.isVerified, '1');
box.write(BoxName.isFirstTime, '0');
box.write(BoxName.phone, phoneController.text);
Get.put(LoginController()).loginUsingCredentials(
box.read(BoxName.passengerID).toString(),
box.read(BoxName.email).toString(),
);
} else {
Get.snackbar('Error'.tr,
"The email or phone number is already registered.".tr,
backgroundColor: Colors.redAccent);
}
} else {
Get.snackbar('Error'.tr, "phone not verified".tr,
backgroundColor: Colors.redAccent);
}
} else {
Get.snackbar('Error'.tr, "you must insert token code".tr,
backgroundColor: AppColor.redColor);
}
} else {
Get.snackbar(
'Error'.tr, "The email or phone number is already registered.".tr,
} catch (e) {
addError(e.toString(), 'passenger sign up ');
Get.snackbar('Error'.tr, "Something went wrong. Please try again.".tr,
backgroundColor: Colors.redAccent);
}
// } else {
// var res = await CRUD().post(link: AppLink.verifyOtpMessage, payload: {
// 'phone_number': phoneController.text,
// 'token': verifyCode.text.toString(),
// });
// if (res != 'failure') {
// // var dec = jsonDecode(res);
// box.write(BoxName.phoneDriver, '+${phoneController.text}');
// var payload = {
// 'id': box.read(BoxName.passengerID),
// 'phone': phoneController.text,
// 'email': box.read(BoxName.email),
// 'password': 'unknown',
// 'gender': 'unknown',
// 'birthdate': '2002-01-01',
// 'site': 'unknown',
// 'first_name': box.read(BoxName.name).toString().split(' ')[0],
// 'last_name': box.read(BoxName.name).toString().split(' ')[1],
// };
// var res1 = await CRUD().post(
// link: AppLink.signUp,
// payload: payload,
// );
// if (res1 != 'failure') {
// CRUD().post(
// link: '${AppLink.seferAlexandriaServer}/auth/signup.php',
// payload: payload,
// );
// CRUD().post(
// link: '${AppLink.seferGizaServer}/auth/signup.php',
// payload: payload,
// );
// box.write(BoxName.isVerified, '1');
// box.write(BoxName.phone, '+${phoneController.text}');
// Get.offAll(const MapPagePassenger());
// }
// } else {
// Get.snackbar(
// 'Error'.tr, "The email or phone number is already registered.".tr,
// backgroundColor: Colors.redAccent);
// }
// }
}
sendVerifications() async {

View File

@@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -65,6 +66,9 @@ class FirebaseMessagesController extends GetxController {
}
}
NotificationController notificationController =
Get.find<NotificationController>();
Future getTokens() async {
String? basicAuthCredentials =
await storage.read(key: BoxName.basicAuthCredentials);
@@ -118,6 +122,10 @@ class FirebaseMessagesController extends GetxController {
void fireBaseTitles(RemoteMessage message) {
if (message.notification!.title! == 'Order'.tr) {
if (Platform.isAndroid) {
notificationController.showNotification(
'Order', message.notification!.body!, 'Order');
}
} else if (message.notification!.title! == 'Apply Ride'.tr) {
var passengerList = message.data['passengerList'];
@@ -128,20 +136,22 @@ class FirebaseMessagesController extends GetxController {
Get.find<MapPassengerController>().isSearchingWindow == false;
Get.find<MapPassengerController>().update();
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Apply Order'.tr, 'Driver Applied the Ride for You'.tr, 'order1');
//notificationController.showNotification(
// 'Apply Order'.tr, 'Driver Applied the Ride for You'.tr, 'order1');
}
// driverAppliedTripSnakBar();
} else if (message.notification!.title! == 'Promo'.tr) {
if (Platform.isAndroid) {
NotificationController()
.showNotification('Promo', 'Show latest promo'.tr, 'promo');
notificationController.showNotification(
'Promo', 'Show latest promo'.tr, 'promo');
}
Get.to(const PromosPassengerPage());
} else if (message.notification!.title! == 'Trip Monitoring'.tr) {
if (Platform.isAndroid) {
NotificationController()
.showNotification('Trip Monitoring'.tr, '', 'iphone_ringtone');
notificationController.showNotification(
'Trip Monitoring'.tr, '', 'iphone_ringtone');
}
var myListString = message.data['passengerList'];
var myList = jsonDecode(myListString) as List<dynamic>;
@@ -151,65 +161,67 @@ class FirebaseMessagesController extends GetxController {
});
} else if (message.notification!.title! == 'token change'.tr) {
if (Platform.isAndroid) {
NotificationController()
.showNotification('token change'.tr, 'token change'.tr, 'cancel');
notificationController.showNotification(
'token change'.tr, 'token change'.tr, 'cancel');
}
GoogleSignInHelper.signOut();
} else if (message.notification!.title! == 'DriverIsGoingToPassenger'.tr) {
Get.find<MapPassengerController>().isDriverInPassengerWay = true;
Get.find<MapPassengerController>().update();
if (Platform.isAndroid) {
NotificationController().showNotification('Driver is Going To You'.tr,
notificationController.showNotification('Driver is Going To You'.tr,
'Please stay on the picked point.'.tr, 'tone1');
}
// Get.snackbar('Driver is Going To Passenger', '',
// backgroundColor: AppColor.greenColor);
} else if (message.notification!.title! == 'message From passenger') {
if (Platform.isAndroid) {
NotificationController()
.showNotification('message From passenger'.tr, ''.tr, 'tone2');
notificationController.showNotification(
'message From passenger'.tr, ''.tr, 'tone2');
}
passengerDialog(message.notification!.body!);
update();
} else if (message.notification!.title! == 'message From Driver') {
passengerDialog(message.notification!.body!);
if (Platform.isAndroid) {
NotificationController()
.showNotification('message From passenger'.tr, ''.tr, 'tone2');
notificationController.showNotification(
'message From passenger'.tr, ''.tr, 'tone2');
}
passengerDialog(message.notification!.body!);
update();
} else if (message.notification!.title! == 'RideIsBegin'.tr) {
if (Platform.isAndroid) {
notificationController.showNotification(
'Trip is Begin'.tr, ''.tr, 'start');
}
Get.find<MapPassengerController>().getBeginRideFromDriver();
// Get.snackbar('RideIsBegin', '', backgroundColor: AppColor.greenColor);
box.write(BoxName.passengerWalletTotal, '0');
update();
if (Platform.isAndroid) {
NotificationController()
.showNotification('Trip is Begin'.tr, ''.tr, 'start');
}
} else if (message.notification!.title! == 'Hi ,I will go now'.tr) {
// Get.snackbar('Hi ,I will go now', '',
// backgroundColor: AppColor.greenColor);
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Passenger come to you'.tr, 'Hi ,I will go now'.tr, 'tone2');
}
update();
} else if (message.notification!.title! == 'Hi ,I Arrive your site'.tr) {
driverArrivePassengerDialoge();
if (Platform.isAndroid) {
NotificationController()
.showNotification('Hi ,I Arrive your site'.tr, ''.tr, 'tone2');
notificationController.showNotification(
'Hi ,I Arrive your site'.tr, ''.tr, 'tone2');
}
update();
} else if (message.notification!.title! == "Cancel Trip from driver".tr) {
Get.back();
Get.defaultDialog(
title: "The driver canceled your ride.".tr,
middleText: "We will look for a new driver.\\nPlease wait.".tr,
middleText: "We will look for a new driver.\nPlease wait.".tr,
confirm: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Ok'.tr,
onPressed: () async {
Get.back();
@@ -219,6 +231,7 @@ class FirebaseMessagesController extends GetxController {
),
cancel: MyElevatedButton(
title: 'Cancel'.tr,
kolor: AppColor.redColor,
onPressed: () {
Get.offAll(const MapPagePassenger());
},
@@ -230,7 +243,7 @@ class FirebaseMessagesController extends GetxController {
var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>;
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Driver Finish Trip'.tr,
'you will pay to Driver'.tr + ' ${driverList[3].toString()} \$'.tr,
'tone1');
@@ -267,7 +280,7 @@ class FirebaseMessagesController extends GetxController {
var driverList = jsonDecode(myListString) as List<dynamic>;
// if (Platform.isAndroid) {
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Call Income'.tr,
message.notification!.body!,
'iphone_ringtone',
@@ -287,7 +300,7 @@ class FirebaseMessagesController extends GetxController {
var driverList = jsonDecode(myListString) as List<dynamic>;
// if (Platform.isAndroid) {
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Call Income'.tr,
message.notification!.body!,
'iphone_ringtone',
@@ -305,7 +318,7 @@ class FirebaseMessagesController extends GetxController {
var myListString = message.data['passengerList'];
var driverList = jsonDecode(myListString) as List<dynamic>;
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Call End'.tr,
message.notification!.body!,
'tone2',
@@ -321,7 +334,7 @@ class FirebaseMessagesController extends GetxController {
// 'message',
// backgroundColor: AppColor.redColor);
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'Driver Cancel Your Trip'.tr,
'you will pay to Driver you will be pay the cost of driver time look to your SEFER Wallet'
.tr,
@@ -346,7 +359,7 @@ class FirebaseMessagesController extends GetxController {
else if (message.notification!.title! == 'Order Applied'.tr) {
if (Platform.isAndroid) {
NotificationController().showNotification(
notificationController.showNotification(
'The order Accepted by another Driver'.tr,
'We regret to inform you that another driver has accepted this order.'
.tr,
@@ -604,120 +617,6 @@ class FirebaseMessagesController extends GetxController {
}
}
void sendNotificationToAnyWithoutData(
String title, String body, String token, String tone) async {
try {
String serviceAccountKeyJson = '''{
"type": "service_account",
"project_id": "ride-b1bd8",
"private_key_id": "75e817c0b902db2ef35edf2c2bd159dec1f13249",
"private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD0zH9TQGDQHUv3\\na3/JAD1UKPwAp3wNKT0a6fxiIzjI3JxQWI30QvZCcfl6CdMhIcydX1ncSaYTcEeC\\n/AdPVCPkqyJx1YIGGg6P/mRzCWeaN8fsp6z250m5vcObDCZc3dbJEkepbep+6FPY\\n21m3KO+AHh1glgsTGZOTm5xiU8NGXpdk2QEh8wpiIIlR/HuKwVw9g8urNe3Sno+U\\nDm3z37iFqvZdmpqO8aWTJu6beb3hsREK9XK2I9JqC2JUwiGQRo3idOvPP6hkqrWx\\nKSX96vglQFYfakvJdDp2ZATOlpBYPMtS/IWhJ985u58TSS+Kl8qpnpaZBSxgJirf\\nhWzhnKLfAgMBAAECggEAJP785SePGhS7ZN6ltspm+l+hSjYFrPWFCxq+rlQ1YkHZ\\nC9l+RqKSFhOkiPmQI2s4wbXl3kFxLHHlFNoi/q2wKQBmGb8TQfnRJpjjNHGA61Ev\\n0Ue7/6qPvVb9B2MsLw/FxKiTFPuMG3bgKR9pbSFuJLYoaW7zqITOhVnYphGTqwAY\\nBVVcvISSLvELDmH9VZcv/9DVqVlqbbESHWh1Z4W6XGPoEqeDH/upNTyQQ/46Msgm\\nTGE6VqLHpWuSf6SqHp+r0Y0lI3vIPM1vz5FAJDJbOE/enHa0fSup0OHSMxl0HVMn\\nnO1yrGF3vsIPOej5HKr5d71bEIckzk73/yjNC1/mDQKBgQD7RtUvc9omsSsFMJ6e\\nBASAn6Dktx/QY/XNJjFzHQj69cywLDe5t5AL2gUi3phQ2oqB5XJdwnd5bTIEPEPZ\\nDOuOai2802p6FJk6kjmZAMVGx5JtXBH+vs6jrmQQSMiKbjwN1TT6xIWakvLOonUi\\nX6ZvjYYjU/E0YJU3jSiXWEr76wKBgQD5Zn4SouJ6BCDZMbausJVMBkk3qxsYooip\\np89WakC6e7AZinpkRcqjGGV9GOvc8crJs6fyXAA9ORepGP47Mc0ZrDssOkstznsM\\npr8R0S6MKwEZaT9ixOHdOcLZ47ps+JzA2Wr4KN2OvFHksUkB/46ATD1j9WZVgB8M\\namsYp/Y73QKBgHOo+PvsoZ9psVmkNX6abtAdqdtdB0HOoRea2uwXk0ig12TIFaZg\\nfedWpUKVnxqoXVTJHklV99RmlL0qWDiSH+LfsMnXro0e6iDxqZ1po2Se/CFmXcoa\\nXdctsFVmixhdATuExewfhTfPKABA+xWlXWC/jdy5CK+JPWXijaqMM4edAoGAE5Bj\\nsWiPpYyvWvpYX0nA3G7dzX0hqgQN/mkIjbnWDArp3IcNZNJIvBSM2Yxb7EAXbU0n\\njo6DAkp5Pa2VO+WDNlFZbvW/sf8xjeOCt44WPa6d7nVgIIpbQXRngZoopKW3/jTP\\n/FmQT8McFXmGxZ5belsAsdetSGW9icbLUerTGQ0CgYEAmf/G8Ag3XxmqTXvvHuv2\\n14OP7WnrVqkEMnydrftEwn4peXd/Lz+/GYX5Zc4ZoNgbN8IvZ5z0+OmRsallsbiW\\nBw0/tc68CjzxXOvReWxDluUopqWVGj5tlGqE5xUDku9SWJSxbkiQ3rqutzBdPXpr\\noqHwPyDrmK/Zgqn+uiIm4Ck=\\n-----END PRIVATE KEY-----\\n",
"client_email": "firebase-adminsdk-o2wqi@ride-b1bd8.iam.gserviceaccount.com",
"client_id": "111210077025005706623",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o2wqi%40ride-b1bd8.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
'''; // As defined above
// Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken();
// Log.print('accessToken: ${accessToken}');
// Send the notification
final response = await http.post(
Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'message': {
'token': token,
'notification': {
'title': title,
'body': body,
},
'android': {
'priority': 'high', // Set priority to high
'notification': {
'sound': tone,
},
},
'apns': {
'headers': {
'apns-priority': '10', // Set APNs priority to 10
},
'payload': {
'aps': {
'sound': tone,
},
},
},
},
}),
);
if (response.statusCode == 200) {
print(
'Notification sent successfully. Status code: ${response.statusCode}');
print('Response token: ${token}');
} else {
print(
'Failed to send notification. Status code: ${response.statusCode}');
print('Response body: ${response.body}');
}
} catch (e) {
print('Error sending notification: $e');
}
}
// void sendNotificationToDriverMAP(String title, String body, String token,
// List<String> data, String tone) async {
// try {
// final response = await http.post(
// // Uri.parse(
// // 'https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send'),
// Uri.parse('https://fcm.googleapis.com/fcm/send'),
// headers: <String, String>{
// 'Content-Type': 'application/json',
// // 'Authorization': 'Bearer 104815009508844392546'
// 'Authorization': 'key=${AK.serverAPI}'
// },
// body: jsonEncode({
// 'notification': <String, dynamic>{
// 'title': title,
// 'body': body,
// 'sound': tone
// },
// 'data': {
// 'DriverList': data,
// },
// 'priority': 'high',
// 'to': token,
// }),
// );
// if (response.statusCode == 200) {
// Log.print(
// 'Notification sent successfully. Status code: ${response.statusCode}');
// Log.print('Response body: ${response.body}');
// } else {
// Log.print(
// 'Failed to send notification. Status code: ${response.statusCode}');
// Log.print('Response body: ${response.body}');
// }
// } catch (e) {
// Log.print('Error sending notification: $e');
// }
// }
Future<void> sendNotificationToDriverMAP(
String title, String body, String token, List<String> data, String tone,
{int retryCount = 2}) async {
@@ -809,79 +708,6 @@ class FirebaseMessagesController extends GetxController {
}
}
}
void sendNotificationToDriverMapPolyline(String title, String body,
String token, List<String> data, String polylineJson, String tone) async {
try {
String serviceAccountKeyJson = '''{
"type": "service_account",
"project_id": "ride-b1bd8",
"private_key_id": "75e817c0b902db2ef35edf2c2bd159dec1f13249",
"private_key": "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQD0zH9TQGDQHUv3\\na3/JAD1UKPwAp3wNKT0a6fxiIzjI3JxQWI30QvZCcfl6CdMhIcydX1ncSaYTcEeC\\n/AdPVCPkqyJx1YIGGg6P/mRzCWeaN8fsp6z250m5vcObDCZc3dbJEkepbep+6FPY\\n21m3KO+AHh1glgsTGZOTm5xiU8NGXpdk2QEh8wpiIIlR/HuKwVw9g8urNe3Sno+U\\nDm3z37iFqvZdmpqO8aWTJu6beb3hsREK9XK2I9JqC2JUwiGQRo3idOvPP6hkqrWx\\nKSX96vglQFYfakvJdDp2ZATOlpBYPMtS/IWhJ985u58TSS+Kl8qpnpaZBSxgJirf\\nhWzhnKLfAgMBAAECggEAJP785SePGhS7ZN6ltspm+l+hSjYFrPWFCxq+rlQ1YkHZ\\nC9l+RqKSFhOkiPmQI2s4wbXl3kFxLHHlFNoi/q2wKQBmGb8TQfnRJpjjNHGA61Ev\\n0Ue7/6qPvVb9B2MsLw/FxKiTFPuMG3bgKR9pbSFuJLYoaW7zqITOhVnYphGTqwAY\\nBVVcvISSLvELDmH9VZcv/9DVqVlqbbESHWh1Z4W6XGPoEqeDH/upNTyQQ/46Msgm\\nTGE6VqLHpWuSf6SqHp+r0Y0lI3vIPM1vz5FAJDJbOE/enHa0fSup0OHSMxl0HVMn\\nnO1yrGF3vsIPOej5HKr5d71bEIckzk73/yjNC1/mDQKBgQD7RtUvc9omsSsFMJ6e\\nBASAn6Dktx/QY/XNJjFzHQj69cywLDe5t5AL2gUi3phQ2oqB5XJdwnd5bTIEPEPZ\\nDOuOai2802p6FJk6kjmZAMVGx5JtXBH+vs6jrmQQSMiKbjwN1TT6xIWakvLOonUi\\nX6ZvjYYjU/E0YJU3jSiXWEr76wKBgQD5Zn4SouJ6BCDZMbausJVMBkk3qxsYooip\\np89WakC6e7AZinpkRcqjGGV9GOvc8crJs6fyXAA9ORepGP47Mc0ZrDssOkstznsM\\npr8R0S6MKwEZaT9ixOHdOcLZ47ps+JzA2Wr4KN2OvFHksUkB/46ATD1j9WZVgB8M\\namsYp/Y73QKBgHOo+PvsoZ9psVmkNX6abtAdqdtdB0HOoRea2uwXk0ig12TIFaZg\\nfedWpUKVnxqoXVTJHklV99RmlL0qWDiSH+LfsMnXro0e6iDxqZ1po2Se/CFmXcoa\\nXdctsFVmixhdATuExewfhTfPKABA+xWlXWC/jdy5CK+JPWXijaqMM4edAoGAE5Bj\\nsWiPpYyvWvpYX0nA3G7dzX0hqgQN/mkIjbnWDArp3IcNZNJIvBSM2Yxb7EAXbU0n\\njo6DAkp5Pa2VO+WDNlFZbvW/sf8xjeOCt44WPa6d7nVgIIpbQXRngZoopKW3/jTP\\n/FmQT8McFXmGxZ5belsAsdetSGW9icbLUerTGQ0CgYEAmf/G8Ag3XxmqTXvvHuv2\\n14OP7WnrVqkEMnydrftEwn4peXd/Lz+/GYX5Zc4ZoNgbN8IvZ5z0+OmRsallsbiW\\nBw0/tc68CjzxXOvReWxDluUopqWVGj5tlGqE5xUDku9SWJSxbkiQ3rqutzBdPXpr\\noqHwPyDrmK/Zgqn+uiIm4Ck=\\n-----END PRIVATE KEY-----\\n",
"client_email": "firebase-adminsdk-o2wqi@ride-b1bd8.iam.gserviceaccount.com",
"client_id": "111210077025005706623",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o2wqi%40ride-b1bd8.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
'''; // As defined above
// Initialize AccessTokenManager
final accessTokenManager = AccessTokenManager(serviceAccountKeyJson);
// Obtain an OAuth 2.0 access token
final accessToken = await accessTokenManager.getAccessToken();
// Send the notification
final response = await http.post(
Uri.parse(
'https://fcm.googleapis.com/v1/projects/ride-b1bd8/messages:send'),
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'Bearer $accessToken',
},
body: jsonEncode({
'message': {
'token': token,
'notification': {
'title': title,
'body': body,
},
'data': {
'DriverList': jsonEncode(data),
},
'android': {
'priority': 'high', // Set priority to high
'notification': {
'sound': tone,
},
},
'apns': {
'headers': {
'apns-priority': '10', // Set APNs priority to 10
},
'payload': {
'aps': {
'sound': tone,
},
},
},
},
}),
);
if (response.statusCode == 200) {
// Notification sent successfully
} else {
// Handle error response
'Failed to send notification. Status code: ${response.statusCode}';
}
} catch (e) {
// Handle other exceptions
}
}
}
class DriverTipWidget extends StatelessWidget {

View File

@@ -1,29 +1,219 @@
// import 'package:flutter_local_notifications/flutter_local_notifications.dart';
// import 'package:get/get.dart';
// import 'package:timezone/data/latest.dart' as tz;
// import 'package:timezone/timezone.dart' as tz;
// class NotificationController extends GetxController {
// final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
// FlutterLocalNotificationsPlugin();
// // Initializes the local notifications plugin
// Future<void> initNotifications() async {
// const AndroidInitializationSettings android =
// AndroidInitializationSettings('@mipmap/launcher_icon');
// const InitializationSettings initializationSettings =
// InitializationSettings(android: android);
// await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
// }
// // Displays a notification with the given title and message
// void showNotification(String title, String message, String tone) async {
// AndroidNotificationDetails android = AndroidNotificationDetails(
// 'high_importance_channel',
// 'High Importance Notifications',
// importance: Importance.max,
// priority: Priority.high,
// showWhen: false,
// sound: RawResourceAndroidNotificationSound(tone),
// );
// DarwinNotificationDetails ios = const DarwinNotificationDetails(
// sound: 'default',
// presentAlert: true,
// presentBadge: true,
// presentSound: true,
// );
// NotificationDetails details =
// NotificationDetails(android: android, iOS: ios);
// await _flutterLocalNotificationsPlugin.show(0, title, message, details);
// }
// // Schedules a notification after 1 minute
// void scheduleNotificationAfter1Minute(
// String title, String message, String tone) async {
// AndroidNotificationDetails android = AndroidNotificationDetails(
// 'high_importance_channel', 'High Importance Notifications',
// importance: Importance.max,
// priority: Priority.high,
// showWhen: false,
// sound: RawResourceAndroidNotificationSound(tone));
// DarwinNotificationDetails ios = const DarwinNotificationDetails(
// sound: 'default',
// presentAlert: true,
// presentBadge: true,
// presentSound: true,
// );
// NotificationDetails details =
// NotificationDetails(android: android, iOS: ios);
// // Schedule the notification to be shown after 1 minute
// final now = tz.TZDateTime.now(tz.local);
// final scheduledTime = now.add(const Duration(minutes: 1));
// await _flutterLocalNotificationsPlugin.zonedSchedule(
// 0,
// title,
// message,
// scheduledTime,
// details,
// androidAllowWhileIdle: true,
// uiLocalNotificationDateInterpretation:
// UILocalNotificationDateInterpretation.absoluteTime,
// matchDateTimeComponents: DateTimeComponents.time,
// );
// }
// }
import 'dart:async';
import 'dart:io';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
class NotificationController extends GetxController {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
@override
void onInit() {
super.onInit();
initNotifications();
}
// Initializes the local notifications plugin
Future<void> initNotifications() async {
const AndroidInitializationSettings android =
AndroidInitializationSettings('@mipmap/launcher_icon');
const InitializationSettings initializationSettings =
InitializationSettings(android: android);
DarwinInitializationSettings ios = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
onDidReceiveLocalNotification:
(int id, String? title, String? body, String? payload) async {},
);
InitializationSettings initializationSettings =
InitializationSettings(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
tz.initializeTimeZones();
print('Notifications initialized');
}
// Displays a notification with the given title and message
void showNotification(String title, String message, String tone) async {
AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel', 'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
sound: RawResourceAndroidNotificationSound(tone));
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
showWhen: false,
sound: RawResourceAndroidNotificationSound(tone),
);
NotificationDetails details = NotificationDetails(android: android);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
await _flutterLocalNotificationsPlugin.show(0, title, message, details);
print('Notification shown: $title - $message');
}
void scheduleDailyNotifications(
String title, String message, String tone) async {
final AndroidNotificationDetails android = AndroidNotificationDetails(
'high_importance_channel',
'High Importance Notifications',
importance: Importance.max,
priority: Priority.high,
sound: RawResourceAndroidNotificationSound(tone),
);
const DarwinNotificationDetails ios = DarwinNotificationDetails(
sound: 'default',
presentAlert: true,
presentBadge: true,
presentSound: true,
);
final NotificationDetails details =
NotificationDetails(android: android, iOS: ios);
// Check for the exact alarm permission on Android 12 and above
if (Platform.isAndroid) {
if (await Permission.scheduleExactAlarm.isDenied) {
if (await Permission.scheduleExactAlarm.request().isGranted) {
print('SCHEDULE_EXACT_ALARM permission granted');
} else {
print('SCHEDULE_EXACT_ALARM permission denied');
return;
}
}
}
// Schedule notifications for 10:00 AM and 3:00 PM daily
await _scheduleNotificationForTime(8, 0, title, message, details);
await _scheduleNotificationForTime(15, 0, title, message, details);
await _scheduleNotificationForTime(20, 0, title, message, details);
// await _scheduleNotificationForTime(0, 22, title, message, details);
print('Daily notifications scheduled successfully');
}
// Helper function to get the next instance of a specific hour and minute
Future<void> _scheduleNotificationForTime(int hour, int minute, String title,
String message, NotificationDetails details) async {
// Initialize and set Cairo timezone
tz.initializeTimeZones();
var cairoLocation;
if (box.read(BoxName.countryCode).toString() == 'Egypt') {
cairoLocation = tz.getLocation('Africa/Cairo');
} else {} // todo get for location country
// Set Cairo timezone
final now = tz.TZDateTime.now(
cairoLocation); // Use Cairo timezone for the current time
tz.TZDateTime scheduledDate = tz.TZDateTime(
cairoLocation, now.year, now.month, now.day, hour, minute);
// If scheduled time is already past today, schedule it for the next day
if (scheduledDate.isBefore(now)) {
scheduledDate = scheduledDate.add(const Duration(days: 1));
}
print('Current time (Cairo): $now');
print('Scheduling notification for: $scheduledDate');
await _flutterLocalNotificationsPlugin.zonedSchedule(
0, // Use unique IDs if you want to manage each notification separately
title,
message,
scheduledDate,
details,
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
print('Notification scheduled successfully for Cairo timezone');
}
}

View File

@@ -0,0 +1,19 @@
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import 'crud.dart';
addError(String error, where) async {
CRUD().post(link: AppLink.addError, payload: {
'error': error.toString(), // Example error description
'userId': box.read(BoxName.driverID) ??
box.read(BoxName.passengerID), // Example user ID
'userType': box.read(BoxName.driverID) != null
? 'Driver'
: 'passenger', // Example user type
'phone': box.read(BoxName.phone) ??
box.read(BoxName.phoneDriver), // Example phone number
'device': where
});
}

View File

@@ -1,15 +1,21 @@
import 'dart:convert';
import 'dart:io';
import 'dart:ui';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/main.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:SEFER/env/env.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import '../../constant/api_key.dart';
import '../../constant/colors.dart';
import '../../print.dart';
import '../../views/widgets/elevated_btn.dart';
import 'add_error.dart';
import 'upload_image.dart';
class CRUD {
@@ -29,8 +35,9 @@ class CRUD {
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
},
);
Log.print('payload: ${payload}');
Log.print('response.request: ${response.request}');
Log.print('payload: ${payload}');
Log.print('response.reasonPhrase: ${response.reasonPhrase}');
Log.print('response.body: ${response.body}');
@@ -44,7 +51,6 @@ class CRUD {
return jsonData['status'];
}
// }
Future<dynamic> getTokenParent({
required String link,
Map<String, dynamic>? payload,
@@ -264,36 +270,83 @@ class CRUD {
} else {}
}
// Future<dynamic> post({
// required String link,
// Map<String, dynamic>? payload,
// }) async {
// // String? basicAuthCredentials =
// // await storage.read(key: BoxName.basicAuthCredentials);
// var url = Uri.parse(
// link,
// );
// var response = await http.post(
// url,
// body: payload,
// headers: {
// "Content-Type": "application/x-www-form-urlencoded",
// 'Authorization':
// 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
// },
// );
// Log.print('payload: ${payload}');
// Log.print('response.request: ${response.request}');
// Log.print('response.body: ${response.body}');
// var jsonData = jsonDecode(response.body);
// if (response.statusCode == 200) {
// if (jsonData['status'] == 'success') {
// return response.body;
// } else {
// return (jsonData['status']);
// }
// } else {
// return response.statusCode;
// }
// }
Future<dynamic> post({
required String link,
Map<String, dynamic>? payload,
}) async {
// String? basicAuthCredentials =
// await storage.read(key: BoxName.basicAuthCredentials);
var url = Uri.parse(
link,
);
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
Log.print('payload: ${payload}');
Log.print('response.request: ${response.request}');
Log.print('response.body: ${response.body}');
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {
if (jsonData['status'] == 'success') {
return response.body;
var url = Uri.parse(link);
try {
var response = await http.post(
url,
body: payload,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}',
},
);
Log.print('Response.request: ${response.request}');
Log.print('Payload: $payload');
// Log.print('Response.statusCode: ${response.statusCode}');
Log.print('Response.body: ${response.body}');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return jsonData;
} else {
return jsonData['status'];
}
} catch (e) {
Log.print('JSON parsing error: $e');
addError(e.toString(), 'crud().post');
return 'failure'; // Return a recognizable failure string for JSON errors
}
} else {
return (jsonData['status']);
Log.print('Non-200 response code: ${response.statusCode}');
addError(
'Non-200 response code: ${response.statusCode}', 'crud().post');
return 'failure'; // Handle unexpected status codes as failures
}
} else {
return response.statusCode;
} catch (e) {
Log.print('HTTP request error: $e');
addError('HTTP request error: $e', 'crud().post');
return 'failure'; // Handle HTTP request errors as failures
}
}

View File

@@ -29,7 +29,7 @@ class SmsEgyptController extends GetxController {
Future<dynamic> sendSmsEgypt(String phone, otp) async {
String sender = await getSender();
var body = jsonEncode({
"username": AppInformation.appName,
"username": 'Sefer',
"password": AK.smsPasswordEgypt,
"message": "${AppInformation.appName} app code is $otp\ncopy it to app",
"language": box.read(BoxName.lang) == 'en' ? "e" : 'r',

View File

@@ -1,49 +1,50 @@
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:get/get.dart';
import '../../main.dart';
class TextToSpeechController extends GetxController {
final flutterTts = FlutterTts();
// Initialize TTS in initState
@override
void onInit() {
super.onInit();
initTts();
}
// Dispose of TTS when controller is closed
@override
void onClose() {
flutterTts.stop(); // Stop any ongoing TTS
super.onClose();
flutterTts.completionHandler;
}
// Function to initialize TTS engine
// Initialize TTS engine with language check
Future<void> initTts() async {
String? lang =
WidgetsBinding.instance.platformDispatcher.locale.countryCode;
await flutterTts
.setLanguage(box.read(BoxName.lang).toString()); //'en-US' Set language
// await flutterTts.setLanguage('ar-SA'); //'en-US' Set language
// await flutterTts.setLanguage(lang!); //'en-US' Set language
await flutterTts.setSpeechRate(0.5); // Adjust speech rate
await flutterTts.setVolume(1.0); // Set volume
try {
String langCode = box.read(BoxName.lang) ?? 'en-US';
bool isAvailable = await flutterTts.isLanguageAvailable(langCode);
// If language is unavailable, default to 'en-US'
if (!isAvailable) {
langCode = 'en-US';
}
await flutterTts.setLanguage(langCode);
await flutterTts.setSpeechRate(0.5); // Adjust speech rate
await flutterTts.setVolume(1.0); // Set volume
} catch (error) {
Get.snackbar('Error', 'Failed to initialize TTS: $error');
}
}
// Function to speak the given text
Future<void> speakText(String text) async {
try {
await flutterTts.awaitSpeakCompletion(true);
var result = await flutterTts.speak(text);
if (result == 1) {
// TTS operation has started
// You can perform additional operations here, if needed
}
await flutterTts.speak(text);
} catch (error) {
// Handle error gracefully, e.g., show a message
Get.snackbar('Error', 'Failed to speak text: $error');
}
}

View File

@@ -769,7 +769,8 @@ class MapPassengerController extends GetxController {
if (res != 'failure') {
var decode = jsonDecode(res);
if (decode['data']['status'] != 'Apply') {
// if (decode['data']['status'] != 'Apply') {
if (decode['data']['status'] == 'Begin') {
timeToPassengerFromDriverAfterApplied = 0;
remainingTime = 0;
remainingTimeToPassengerFromDriverAfterApplied = 0;
@@ -1077,7 +1078,7 @@ class MapPassengerController extends GetxController {
// licensePlate = nearestDriverData['car_plate'].toString();
// startCarLocationSearch(box.read(BoxName.carType));
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 4500);
box.read(BoxName.carType), 3000);
// await getCarsLocationByPassengerAndReloadMarker(
// box.read(BoxName.carType), 7000);
// await getNearestDriverByPassengerLocation();
@@ -1112,26 +1113,28 @@ class MapPassengerController extends GetxController {
isDriversTokensSend = false;
update();
await CRUD()
.post(link: "${AppLink.endPoint}/ride/rides/add.php", payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
}).then((value) {
await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/add.php",
payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
}).then((value) {
// List<String> body = [
rideId = jsonDecode(value)['message'];
List<String> body = [
@@ -1188,6 +1191,29 @@ class MapPassengerController extends GetxController {
Log.print(
'dataCarsLocationByPassenger[data]: ${dataCarsLocationByPassenger['data'][carsOrder]['token']}');
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: "${AppLink.endPoint}/ride/rides/add.php", payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
});
}
delayAndFetchRideStatus(rideId, box.read(BoxName.carType));
if (shouldFetch == false) {
startTimer();
@@ -1211,10 +1237,11 @@ class MapPassengerController extends GetxController {
}
}
String driverOrderStatus = 'yet';
bool isDriversTokensSend = false;
confirmRideForAllDriverAvailable() async {
confirmRideForAllDriverAvailable0() async {
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 4500);
box.read(BoxName.carType), 3500);
if (dataCarsLocationByPassenger != 'failure') {
// driversToken.remove(driverToken);
PaymentController paymentController = Get.find<PaymentController>();
@@ -1291,33 +1318,38 @@ class MapPassengerController extends GetxController {
// Log.print('body: ${body}');
FirebaseMessagesController().sendNotificationToDriverMAP(
'OrderSpeed',
rideId.toString(),
dataCarsLocationByPassenger['data'][i]['token'].toString(),
body,
'order.wav');
'OrderSpeed',
rideId.toString(),
dataCarsLocationByPassenger['data'][i]['token'].toString(),
body,
'order.wav',
);
driverOrderStatus = 'recive';
}
});
(rideId); //
CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
});
}
delayAndFetchRideStatusForAllDriverAvailable(rideId);
update();
@@ -1330,6 +1362,344 @@ class MapPassengerController extends GetxController {
}
}
Set<String> notifiedDrivers = {};
confirmRideForAllDriverAvailable() async {
// Fetch car locations
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
// Ensure dataCarsLocationByPassenger is not 'failure' or null
if (dataCarsLocationByPassenger != 'failure' &&
dataCarsLocationByPassenger != null) {
// Check if 'data' key exists and is not null
if (dataCarsLocationByPassenger.containsKey('data') &&
dataCarsLocationByPassenger['data'] != null) {
PaymentController paymentController = Get.find<PaymentController>();
rideConfirm = true;
shouldFetch = true;
isBottomSheetShown = false;
timeToPassengerFromDriverAfterApplied = 60;
// Add ride to database
await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/add.php",
payload: {
"start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location":
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
}).then((value) {
if (value is String) {
final parsedValue = jsonDecode(value);
rideId = parsedValue['message'];
} else if (value is Map) {
rideId = value['message'];
} else {
Log.print('Unexpected response type: ${value.runtimeType}');
}
// Log.print('value: ${value}');
// rideId = jsonDecode(value)['message'];
// rideId = jsonDecode(value)['message'].toString();
// Timer for 5 iterations, runs every 2 seconds
int iteration = 0;
Timer.periodic(const Duration(seconds: 2), (timer) async {
if (iteration >= 5) {
timer.cancel();
return;
}
iteration++;
// Reload driver locations
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
// Ensure dataCarsLocationByPassenger and data key are still valid
if (dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger.containsKey('data') &&
dataCarsLocationByPassenger['data'] != null) {
// Notify only new drivers
for (var driverData in dataCarsLocationByPassenger['data']) {
String driverId = driverData['driver_id'].toString();
if (!notifiedDrivers.contains(driverId)) {
notifiedDrivers.add(driverId);
// Prepare body payload for notification
List<String> body = [
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
totalPassenger.toStringAsFixed(2),
totalDriver.toStringAsFixed(2),
durationToRide.toString(),
distance.toStringAsFixed(2),
driverId,
box.read(BoxName.passengerID).toString(),
box.read(BoxName.name).toString(),
box.read(BoxName.tokenFCM).toString(),
box.read(BoxName.phone).toString(),
durationByPassenger.toString(),
distanceByPassenger.toString(),
paymentController.isWalletChecked.toString(),
driverData['token'].toString(),
durationToPassenger.toString(),
rideId,
rideTimerBegin.toString(),
driverId,
durationToRide.toString(),
Get.find<WayPointController>().wayPoints.length > 1
? 'haveSteps'
: 'startEnd',
placesCoordinate[0],
placesCoordinate[1],
placesCoordinate[2],
placesCoordinate[3],
placesCoordinate[4],
costForDriver.toStringAsFixed(2),
(double.parse(box.read(BoxName.passengerWalletTotal)) < 0
? double.parse(box.read(BoxName.passengerWalletTotal))
.toStringAsFixed(2)
: '0'),
box.read(BoxName.email).toString(),
data[0]['start_address'],
data[0]['end_address'],
box.read(BoxName.carType),
kazan.toStringAsFixed(0),
passengerRate.toStringAsFixed(2),
];
// Send notification to the driver
FirebaseMessagesController().sendNotificationToDriverMAP(
'OrderSpeed',
rideId,
driverData['token'].toString(),
body,
'order.wav',
);
}
}
}
});
});
// Check for additional server endpoint
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
"start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location":
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
});
}
delayAndFetchRideStatusForAllDriverAvailable(rideId);
update();
} else {
// Show dialog if no drivers found in data key
MyDialog().getDialog("No Car or Driver Found in your area.".tr,
"No Car or Driver Found in your area.".tr, () {
Get.back();
Get.offAll(const MapPagePassenger());
});
}
} else {
// Show dialog if dataCarsLocationByPassenger is 'failure' or null
MyDialog().getDialog("No Car or Driver Found in your area.".tr,
"No Car or Driver Found in your area.".tr, () {
Get.back();
Get.offAll(const MapPagePassenger());
});
}
}
confirmRideForAllDriverAvailable1() async {
int attempts = 0;
const int maxAttempts = 4;
const Duration delayDuration = Duration(seconds: 2);
// Initial data fetch
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
if (dataCarsLocationByPassenger != null &&
dataCarsLocationByPassenger != 'failure') {
PaymentController paymentController = Get.find<PaymentController>();
rideConfirm = true;
shouldFetch = true;
isBottomSheetShown = false;
timeToPassengerFromDriverAfterApplied = 60;
// Create a set to keep track of notified driver IDs
Set<String> notifiedDriverIds = {};
// Send the initial ride request once
rideId = await CRUD().post(
link: "${AppLink.seferCairoServer}/ride/rides/add.php",
payload: {
"start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location":
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
}).then((value) => jsonDecode(value)['message']);
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(link: '${AppLink.endPoint}/ride/rides/add.php', payload: {
"start_location": //'${data[0]['start_address']}',
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
"end_location": //'${data[0]['end_address']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
"date": DateTime.now().toString(),
"time": DateTime.now().toString(),
"endtime": durationToAdd.toString(),
"price": totalPassenger.toStringAsFixed(2),
"passenger_id": box.read(BoxName.passengerID).toString(),
"driver_id": dataCarsLocationByPassenger['data'][carsOrder]
['driver_id']
.toString(),
"status": "waiting",
'carType': box.read(BoxName.carType),
"price_for_driver": totalPassenger.toString(),
"price_for_passenger": totalME.toString(),
"distance": distance.toString(),
"paymentMethod": paymentController.isWalletChecked.toString(),
});
}
// Add the initially available drivers to the notified set
for (var driver in dataCarsLocationByPassenger['data']) {
notifiedDriverIds.add(driver['driver_id'].toString());
}
// Periodically check for new drivers
Timer.periodic(delayDuration, (Timer timer) async {
attempts++;
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 3000);
if (dataCarsLocationByPassenger != 'failure') {
// Check for new drivers and notify them
for (var driver in dataCarsLocationByPassenger['data']) {
String driverId = driver['driver_id'].toString();
// Only notify new drivers
if (!notifiedDriverIds.contains(driverId)) {
notifiedDriverIds.add(driverId);
// Prepare notification body
List<String> body = [
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}',
'${data[0]["end_location"]['lat']},${data[0]["end_location"]['lng']}',
totalPassenger.toStringAsFixed(2),
totalDriver.toStringAsFixed(2),
durationToRide.toString(),
distance.toStringAsFixed(2),
driverId,
box.read(BoxName.passengerID).toString(),
box.read(BoxName.name).toString(),
box.read(BoxName.tokenFCM).toString(),
box.read(BoxName.phone).toString(),
durationByPassenger.toString(),
distanceByPassenger.toString(),
paymentController.isWalletChecked.toString(),
driver['token'].toString(),
durationToPassenger.toString(),
rideId,
rideTimerBegin.toString(),
durationToRide.toString(),
Get.find<WayPointController>().wayPoints.length > 1
? 'haveSteps'
: 'startEnd',
placesCoordinate[0],
placesCoordinate[1],
placesCoordinate[2],
placesCoordinate[3],
placesCoordinate[4],
costForDriver.toStringAsFixed(2),
double.parse(box.read(BoxName.passengerWalletTotal)) < 0
? double.parse(box.read(BoxName.passengerWalletTotal))
.toStringAsFixed(2)
: '0',
box.read(BoxName.email).toString(),
data[0]['start_address'],
data[0]['end_address'],
box.read(BoxName.carType),
kazan.toStringAsFixed(0),
passengerRate.toStringAsFixed(2),
];
// Send notification to the new driver
FirebaseMessagesController().sendNotificationToDriverMAP(
'OrderSpeed',
rideId.toString(),
driver['token'].toString(),
body,
'order.wav',
);
}
}
} else {
MyDialog().getDialog("No Car or Driver Found in your area.".tr,
"No Car or Driver Found in your area.".tr, () {
Get.back();
Get.offAll(const MapPagePassenger());
});
}
// Stop after max attempts
if (attempts >= maxAttempts) {
timer.cancel();
}
});
} else {
MyDialog().getDialog("No Car or Driver Found in your area.".tr,
"No Car or Driver Found in your area.".tr, () {
Get.back();
Get.offAll(const MapPagePassenger());
});
}
}
icreaseForSameRideAndDelay() {
PaymentController paymentController = Get.find<PaymentController>();
rideConfirm = true;
@@ -1398,19 +1768,20 @@ class MapPassengerController extends GetxController {
String res = await getRideStatus(rideId);
Log.print('tick: $tick');
if ((res.toString() == 'waiting' || res.toString() == 'Refused') &&
String rideStatusDelayed = res.toString();
if ((rideStatusDelayed == 'waiting' ||
rideStatusDelayed == 'Refused') &&
tick >= 15) {
timer.cancel(); // Stop the current timer
showAndResearchForCaptain();
//TODO add to wait
await getCarsLocationByPassengerAndReloadMarker(carType, 4500);
await getCarsLocationByPassengerAndReloadMarker(carType, 3000);
// await getNearestDriverByPassengerLocationAPIGOOGLE();
// getCarForFirstConfirm(carType);
confirmRideForAllDriverAvailable();
// delayAndFetchRideStatusForAllDriverAvailable(rideId);
} else if (res.toString() == 'Apply') {
Log.print('res.toString() == Apply: ${res.toString()}');
} else if (rideStatusDelayed == 'Apply') {
Log.print('rideStatusDelayed == Apply: ${rideStatusDelayed}');
// todo play sound
Get.find<AudioRecorderController>()
.playSoundFromAssets('assets/start.wav');
@@ -1422,7 +1793,7 @@ class MapPassengerController extends GetxController {
isSearchingWindow = false;
update();
startTimerFromDriverToPassengerAfterApplied();
} else if (res.toString() == 'Refused') {
} else if (rideStatusDelayed == 'Refused') {
statusRide = 'Refused';
if (isDriversTokensSend == false) {
confirmRideForAllDriverAvailable();
@@ -1473,17 +1844,22 @@ class MapPassengerController extends GetxController {
attemptCounter++;
tick++;
var res = await getRideStatus(rideId);
String rideStatusDelayed = res.toString();
if (res.toString() == 'Apply' || res.toString() == 'Applied') {
if (rideStatusDelayed == 'Apply' || rideStatusDelayed == 'Applied') {
await getUpdatedRideForDriverApply(rideId);
isApplied = true;
shouldFetch = false;
statusRide = 'Apply';
rideConfirm = false;
isSearchingWindow = false;
startTimer();
update();
startTimerFromDriverToPassengerAfterApplied();
} else if (attemptCounter >= maxAttempts && statusRide != 'Cancel') {
} else if (attemptCounter >= maxAttempts &&
rideStatusDelayed != 'Cancel') {
shouldFetch = false;
// If the status is still not "Apply" after 15 attempts
MyDialog().getDialog('upgrade price'.tr,
@@ -1573,7 +1949,7 @@ class MapPassengerController extends GetxController {
reSearchAfterCanceledFromDriver() async {
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 4500);
box.read(BoxName.carType), 3000);
confirmRideForAllDriverAvailable();
shouldFetch = true; // Stop further fetches
@@ -1616,8 +1992,9 @@ class MapPassengerController extends GetxController {
}
Future<String> getRideStatus(String rideId) async {
final response =
await CRUD().get(link: AppLink.getRideStatus, payload: {'id': rideId});
final response = await CRUD().get(
link: "${AppLink.endPoint}/ride/rides/getRideStatus.php",
payload: {'id': rideId});
return jsonDecode(response)['data'];
}
@@ -1638,7 +2015,7 @@ class MapPassengerController extends GetxController {
make = response['data']['make'];
licensePlate = response['data']['car_plate'];
firstName = response['data']['first_name'];
driverName = response['data']['driverName'];
driverName = response['data']['driverName'].toString().split(' ')[0];
driverToken = response['data']['token'];
Log.print('driverToken updated: $driverToken');
carYear = response['data']['year'];
@@ -1646,10 +2023,11 @@ class MapPassengerController extends GetxController {
}
// driversToken.remove(driverToken);
// for (var i = 1; i < driversToken.length; i++) {
FirebaseMessagesController().sendNotificationToAnyWithoutData(
FirebaseMessagesController().sendNotificationToDriverMAP(
'Order Applied'.tr,
'$driverName Apply order\nTake attention in other order'.tr,
driverToken,
[],
'start.wav',
);
// }
@@ -1871,6 +2249,7 @@ class MapPassengerController extends GetxController {
longitude >= 31.215009 &&
longitude <= 31.532186) {
box.write(BoxName.serverChosen, AppLink.seferCairoServer);
return 'Cairo';
} else if (latitude >= 29.904975 &&
latitude <= 30.143372 &&
@@ -2380,12 +2759,13 @@ class MapPassengerController extends GetxController {
double tripDurationInMinutes = durationToRide / 6;
int loopCount = tripDurationInMinutes.ceil();
// If the trip duration is less than or equal to 50 minutes, then break the loop.
clearMarkersExceptStartEnd();
for (var i = 0; i < loopCount; i++) {
// Wait for 50 seconds.
await Future.delayed(const Duration(seconds: 4));
if (rideTimerBegin == true && statusRide == 'Apply') {
await getDriverCarsLocationToPassengerAfterApplied();
}
// if (rideTimerBegin == true && statusRide == 'Apply') {
await getDriverCarsLocationToPassengerAfterApplied();
// }
reloadMarkerDriverCarsLocationToPassengerAfterApplied();
}
}
@@ -2517,79 +2897,66 @@ class MapPassengerController extends GetxController {
}
Future cancelRide() async {
if (rideConfirm == false && statusRide == 'Apply' ||
statusRide == 'Applied' ||
statusRide == 'waiting') {
clearPlacesDestination();
clearPolyline();
// clearPolylineAll();
data = [];
changeCancelRidePageShow();
if (rideId != 'yet') {
await CRUD().post(link: AppLink.updateDriverOrder, payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
FirebaseMessagesController().sendNotificationToDriverMAP(
'Cancel Trip',
'Trip Cancelled'.tr,
driverToken,
[],
'cancel.wav',
);
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "$AppLink.endPoint/ride/driver_order/update.php",
payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
}
await CRUD().post(link: AppLink.updateRides, payload: {
// if (rideConfirm == true ||
// statusRide == 'Apply' ||
// statusRide == 'Applied' ||
// statusRide == 'wait' ||
// statusRide == 'waiting') {
clearPlacesDestination();
clearPolyline();
// clearPolylineAll();
data = [];
changeCancelRidePageShow();
if (rideId != 'yet') {
Log.print('cancelRide: 1');
FirebaseMessagesController().sendNotificationToDriverMAP(
'Cancel Trip'.tr,
'Trip Cancelled'.tr,
driverToken,
[],
'cancel.wav',
);
await Future.wait([
CRUD().post(link: AppLink.updateRides, payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rides/update.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
}
}),
CRUD().post(link: AppLink.updateDriverOrder, payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
}),
CRUD().post(link: AppLink.updateWaitingTrip, payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
}),
]);
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/update.php",
payload: {
"order_id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
CRUD()
.post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
}
print('Cancel');
CRUD().post(
link:
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
payload: {
"id": rideId.toString(), // Convert to String
"status": 'Cancel'
});
}
Get.offAll(const MapPagePassenger());
} else {
clearPlacesDestination();
clearPolyline();
data = [];
// await CRUD().post(link: AppLink.updateDriverOrder, payload: {
// "order_id": rideId.toString(), // Convert to String
// "status": 'Cancel'
// });
// await CRUD().post(link: AppLink.updateRides, payload: {
// "id": rideId.toString(), // Convert to String
// "status": 'Cancel'
// });
Get.offAll(const MapPagePassenger());
print('Cancel');
// }
}
Future.delayed(const Duration(seconds: 1));
Get.offAll(() => const MapPagePassenger());
}
void changePickerShown() {
@@ -2942,28 +3309,28 @@ class MapPassengerController extends GetxController {
? LatLng(_locationData.latitude!, _locationData.longitude!)
: null)!;
getLocationArea(passengerLocation.latitude, passengerLocation.longitude);
Log.print('AppLink.endPoint: ${AppLink.endPoint}');
// Log.print('BoxName.serverChosen: ${box.read(BoxName.serverChosen)}');
newStartPointLocation = passengerLocation;
Log.print('passengerLocation: ${passengerLocation}');
speed = _locationData.speed!;
// //print location details
isLoading = false;
update();
}
LatLngBounds calculateBounds(
double centerLat, double centerLng, double radius) {
// double radius = 4000; // 10 km in meters
LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
const double earthRadius = 6378137.0; // Earth's radius in meters
southwest = LatLng(
centerLat - (radius / 111000),
centerLng - (radius / (111000 * cos(centerLat))),
double latDelta = radiusInMeters / earthRadius * (180 / pi);
double lngDelta =
radiusInMeters / (earthRadius * cos(pi * lat / 180)) * (180 / pi);
return LatLngBounds(
southwest: LatLng(lat - latDelta, lng - lngDelta),
northeast: LatLng(lat + latDelta, lng + lngDelta),
);
northeast = LatLng(
centerLat + (radius / 111000),
centerLng + (radius / (111000 * cos(centerLat))),
);
return LatLngBounds(southwest: southwest, northeast: northeast);
}
GoogleMapController? mapController;
@@ -2992,8 +3359,6 @@ class MapPassengerController extends GetxController {
bool reloadStartApp = false;
int reloadCount = 0;
startMarkerReloading() async {
Log.print('AppLink.endPoint: ${AppLink.endPoint}');
if (reloadStartApp == false) {
Timer.periodic(const Duration(seconds: 5), (timer) async {
reloadCount++;
@@ -3233,7 +3598,7 @@ class MapPassengerController extends GetxController {
remainingTime = 25; //to make cancel every call
// startCarLocationSearch(box.read(BoxName.carType));
await getCarsLocationByPassengerAndReloadMarker(
box.read(BoxName.carType), 7000);
box.read(BoxName.carType), 5000);
// await getCarsLocationByPassengerAndReloadMarker();
var coordDestination = destination.split(',');
double latPassengerDestination = double.parse(coordDestination[0]);
@@ -4093,55 +4458,77 @@ class MapPassengerController extends GetxController {
try {
// Prepare trip data
Map<String, dynamic> tripData = {
'id': driver['id'],
'id': driver['id'].toString(), // Ensure the id is a string
'phone': driver['phone'],
'gender': driver['gender'],
'name': driver['name'],
'name': driver['NAME'],
'name_english': driver['name_english'],
'address': driver['address'],
'religion': driver['religion'],
'age': driver['age'],
'age': driver['age'].toString(), // Convert age to String
'education': driver['education'],
'license_type': driver['license_type'],
'national_number': driver['national_number'],
'car_plate': driver['car_plate'],
'make': driver['make'],
'model': driver['model'],
'year': driver['year'],
'year': driver['year'].toString(), // Convert year to String
'color': driver['color'],
'color_hex': driver['color_hex'],
'displacement': driver['displacement'],
'fuel': driver['fuel'],
'token': driver['token'],
'rating': driver['rating'],
'countRide': driver['countRide'],
'rating': driver['rating'].toString(), // Convert rating to String
'countRide':
driver['countRide'].toString(), // Convert countRide to String
'passengerId': box.read(BoxName.passengerID),
'timeSelected': tripDateTime.toIso8601String(),
'status': 'pending', // Or other appropriate status
'status': 'pending',
};
// Log.print('tripData: $tripData');
// Send data to server
var response =
await CRUD().post(link: AppLink.addMishwari, payload: tripData);
// Log.print('response: $response');
if (response != 'failure') {
// Trip saved successfully
Get.snackbar('Success'.tr, 'Trip booked successfully'.tr);
var id = response['message'].toString();
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
await CRUD().post(
link: "${AppLink.endPoint}/ride/mishwari/add.php",
payload: tripData);
}
// Set up local notification
// Optionally, set up local notification or send a push notification
// await setLocalNotification(tripDateTime);
// Send notification to driver
// await FirebaseMessagesController().sendNotificationToDriverMAP();
await FirebaseMessagesController().sendNotificationToDriverMAP(
'OrderVIP',
rideId.toString(),
driver['token'].toString(),
[
id,
driver['id'],
passengerLocation.latitude.toString(),
passengerLocation.longitude.toString(),
box.read(BoxName.name).toString(),
box.read(BoxName.passengerID).toString(),
box.read(BoxName.phone).toString(),
box.read(BoxName.email).toString(),
box.read(BoxName.passengerPhotoUrl).toString(),
box.read(BoxName.tokenFCM).toString(),
driver['token'].toString(),
],
'order.wav');
} else {
throw Exception('Failed to save trip');
}
} catch (e) {
Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr);
// Show error message with more details for debugging
Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr,
backgroundColor: AppColor.redColor);
Log.print('Error: $e');
}
}
@@ -4253,8 +4640,8 @@ class MapPassengerController extends GetxController {
addCustomStepIcon();
addCustomStartIcon();
addCustomEndIcon();
addToken();
getLocation();
// addToken();
await getLocation();
getPassengerLocationUniversity();
_initializePolygons();
// await addToken();

View File

@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/links.dart';
@@ -9,11 +12,38 @@ import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import '../../../constant/api_key.dart';
import '../../../print.dart';
class ComplaintController extends GetxController {
bool isLoading = false;
final formKey = GlobalKey<FormState>();
final complaintController = TextEditingController();
final suggestionController = TextEditingController();
List feedBack = [];
@override
void onInit() {
super.onInit();
getLatestRidesForPassengers();
}
getLatestRidesForPassengers() async {
isLoading = true;
update();
var res = await CRUD().get(link: AppLink.getFeedBack, payload: {
'passengerId': box.read(BoxName.passengerID).toString(),
});
if (res != 'failure') {
var d = jsonDecode(res)['message'];
feedBack = d;
}
isLoading = false;
update();
}
void addComplaint() async {
isLoading = true;
@@ -34,11 +64,175 @@ class ComplaintController extends GetxController {
title: 'Ok'.tr,
onPressed: () {
Get.back();
Get.back();
// Get.back();
}));
}
isLoading = false;
update();
}
var isUploading = false.obs;
var uploadSuccess = false.obs;
late String audioLink = '';
Future<void> uploadAudioFile(File audioFile) async {
try {
isUploading.value = true;
// Prepare the file upload
var uri = Uri.parse('${AppLink.seferCairoServer}/upload_audio.php');
var request = http.MultipartRequest('POST', uri);
// Add the file to the request with MIME type
var mimeType = lookupMimeType(audioFile.path);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
request.files.add(
await http.MultipartFile.fromPath(
'audio',
audioFile.path,
contentType: mimeType != null ? MediaType.parse(mimeType) : null,
),
);
// Send the request
var response = await request.send();
// Convert response to string for parsing
var responseBody = await http.Response.fromStream(response);
// After the upload request
if (response.statusCode == 200) {
var jsonResponse = jsonDecode(responseBody.body);
if (jsonResponse['status'] == 'Audio file uploaded successfully.') {
uploadSuccess.value = true;
audioLink = jsonResponse['link']; // Get the audio link
Get.back();
Get.snackbar('Success'.tr, 'Audio uploaded successfully.'.tr,
backgroundColor: const Color.fromARGB(255, 89, 185, 115));
} else {
uploadSuccess.value = false;
}
} else {
uploadSuccess.value = false;
}
} catch (e) {
uploadSuccess.value = false;
} finally {
isUploading.value = false;
}
}
var customerServiceSolutions;
var passengerReport;
var driverReport;
var isloading = false;
Future<void> geminiAudio(payload, String audioLink, String complain) async {
String prompt = '''
Analyze the following complaint between a passenger and driver in a ride-hailing service. The complaint includes an audio link for reference. Provide two possible solutions for customer service to resolve the issue, and generate a detailed report for both the passenger and the driver.
Complaint details:
- Passenger: $complain
- Driver: [Driver's complaint]
- Ride Information: {ride details such as start_location, end_location, date, price, status, and rating details}
- Audio Link: [$audioLink]
Output the result in JSON format with the following structure:
{
"customerServiceSolutions": [
"solution1",
"solution2"
],
"passengerReport": {
"solution": "Passenger's solution" if passenger right,
"complaint": "Passenger's complaint",
"rideDetails": {detailed ride info}
},
"driverReport": {
"complaint": "Driver's complaint",
"rideDetails": {detailed ride info}
}
} the response in arabic language with egypt
''';
var requestBody = jsonEncode({
"contents": [
{
"parts": [
{"text": "$payload $prompt"}
]
}
],
"generationConfig": {
"temperature": 1,
"topK": 64,
"topP": 0.95,
"maxOutputTokens": 8192,
"stopSequences": []
},
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE"
}
]
});
final response = await http.post(
Uri.parse(
'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'),
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
var responseData = jsonDecode(response.body);
var result = responseData['candidates'][0]['content']['parts'][0]['text'];
Log.print('result: ${result}');
// Clean up the result by removing surrounding backticks if they exist
result = result.replaceAll(RegExp(r'^```json\s*|\s*```$'), '');
// Attempt to decode the cleaned result as JSON
try {
var jsonResult = jsonDecode(result);
// Access customer service solutions and reports for both passenger and driver
customerServiceSolutions = jsonResult['customerServiceSolutions'];
passengerReport = jsonResult['passengerReport'];
driverReport = jsonResult['driverReport'];
update();
// Use the data accordingly
// For example, log the reports or display them in a UI dialog
update();
} catch (e) {
MyDialog().getDialog(
'Error'.tr,
'Unable to parse the response as JSON. Please check the format and try again.'
.tr, () {
Get.back();
});
}
} else {
Get.snackbar(
'Error', "Request failed with status: ${response.statusCode}",
backgroundColor: AppColor.redColor);
}
}
}

View File

@@ -0,0 +1,280 @@
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/controller/home/payment/captain_wallet_controller.dart';
import 'package:SEFER/controller/payment/payment_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_contacts/contact.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:get/get.dart';
import 'package:share/share.dart';
import '../../../main.dart';
import '../../../views/widgets/my_dialog.dart';
import '../../functions/launch.dart';
import '../../notification/notification_captain_controller.dart';
class InviteController extends GetxController {
final TextEditingController invitePhoneController = TextEditingController();
List driverInvitationData = [];
List driverInvitationDataToPassengers = [];
String? couponCode;
String? driverCouponCode;
int selectedTab = 0;
PassengerStats passengerStats = PassengerStats();
void updateSelectedTab(int index) {
selectedTab = index;
update();
}
Future<void> shareCouponCode() async {
// TODO: Implement sharing functionality
// You can use share_plus package to share the coupon code
}
Future<void> shareDriverCode() async {
if (driverCouponCode != null) {
final String shareText = '''
Join SEFER as a driver using my referral code!
Use code: $driverCouponCode
Download the SEFER Driver app now and earn rewards!
''';
await Share.share(shareText);
}
}
Future<void> sharePassengerCode() async {
if (couponCode != null) {
final String shareText = '''
Get a discount on your first SEFER ride!
Use my referral code: $couponCode
Download the SEFER app now and enjoy your ride!
''';
await Share.share(shareText);
}
}
@override
void onInit() {
super.onInit();
// fetchDriverStats();
}
void fetchDriverStats() async {
try {
var response = await CRUD().get(link: AppLink.getInviteDriver, payload: {
"driverId": box.read(BoxName.driverID),
});
if (response != 'failure') {
var data = jsonDecode(response);
driverInvitationData = data['message'];
update();
}
} catch (e) {}
}
void fetchDriverStatsPassengers() async {
try {
var response = await CRUD()
.get(link: AppLink.getDriverInvitationToPassengers, payload: {
"driverId": box.read(BoxName.passengerID),
});
if (response != 'failure') {
var data = jsonDecode(response);
driverInvitationDataToPassengers = data['message'];
update();
}
} catch (e) {}
}
void selectPhone(String phone) {
if (box.read(BoxName.countryCode) == 'Egypt') {
invitePhoneController.text = phone;
update();
Get.back();
}
}
Future<void> saveContactsToServer() async {
try {
// TODO: Implement the actual server upload logic here
// Simulating a server request
await Future.delayed(Duration(seconds: 2));
Get.snackbar('Success'.tr,
'${selectedContacts.length} contacts saved to server'.tr);
} catch (e) {
Get.snackbar('Error'.tr,
'An error occurred while saving contacts to server: $e'.tr);
}
}
List<Contact> contacts = <Contact>[];
List<Contact> selectedContacts = <Contact>[];
RxList<Map<String, dynamic>> contactMaps = <Map<String, dynamic>>[].obs;
Future<void> pickContacts() async {
try {
if (await FlutterContacts.requestPermission(readonly: true)) {
final List<Contact> fetchedContacts =
await FlutterContacts.getContacts(withProperties: true);
contacts = fetchedContacts;
// Convert contacts to a list of maps
contactMaps.value = fetchedContacts.map((contact) {
return {
'name': contact.displayName,
'phones':
contact.phones.map((phone) => phone.normalizedNumber).toList(),
'emails': contact.emails.map((email) => email.address).toList(),
};
}).toList();
update();
if (contacts.isEmpty) {
Get.snackbar('No contacts available'.tr,
'Please add contacts to your phone.'.tr);
}
} else {
Get.snackbar('Permission denied'.tr,
'Contact permission is required to pick contacts'.tr);
}
} catch (e) {
Get.snackbar(
'Error'.tr, 'An error occurred while picking contacts: $e'.tr);
}
}
void onSelectPassengerInvitation(int index) async {
MyDialog().getDialog(
driverInvitationDataToPassengers[index]['countOfInvitDriver'] < 2
? '${'When'.tr} ${driverInvitationDataToPassengers[index]['passengerName']} ${"complete, you can claim your gift".tr} '
: 'You deserve the gift'.tr,
'${driverInvitationDataToPassengers[index]['passengerName']} ${driverInvitationDataToPassengers[index]['countOfInvitDriver']} / 2 ${'Trip'.tr}',
() async {
if (driverInvitationDataToPassengers[index]['countOfInvitDriver'] < 2) {
Get.back();
} else {
// Claim the gift if 100 trips are completed
if (driverInvitationDataToPassengers[index]['isGiftToken']
.toString() ==
'0') {
Get.back();
// Add wallet to the inviter
await Get.find<PaymentController>().addPassengersWallet('20');
// add for invitor too
// await Get.find<CaptainWalletController>().addDriverWalletToInvitor(
// 'paymentMethod',
// driverInvitationData[index]['driverInviterId'],
// '50');
// Update invitation as claimed
await CRUD().post(
link: AppLink.updatePassengerGift,
payload: {'id': driverInvitationDataToPassengers[index]['id']},
);
// Notify the inviter
NotificationCaptainController().addNotificationCaptain(
driverInvitationDataToPassengers[index]['passengerInviterId']
.toString(),
"You have got a gift for invitation".tr,
'${"You have 20".tr} ${'LE'}',
false,
);
} else {
Get.back();
MyDialog().getDialog(
"You have got a gift".tr,
"Share the app with another new passenger".tr,
() {
Get.back();
},
);
}
}
},
);
}
savePhoneToServer() async {
for (var i = 0; i < contactMaps.length; i++) {
var phones = contactMaps[i]['phones'];
if (phones != null && phones.isNotEmpty && phones[0].isNotEmpty) {
var res = await CRUD().post(link: AppLink.savePhones, payload: {
"name": contactMaps[i]['name'] ?? 'none',
"phones": phones[0] ?? 'none',
"phones2": phones.join(', ') ??
'none', // Convert List<String> to a comma-separated string
});
if (res != 'failure') {}
} else {}
}
}
String formatPhoneNumber(String input) {
// Remove any non-digit characters
String digitsOnly = input.replaceAll(RegExp(r'\D'), '');
// Ensure the number starts with the country code
if (digitsOnly.startsWith('20')) {
digitsOnly = digitsOnly.substring(1);
}
return digitsOnly;
}
void sendInviteToPassenger() async {
if (invitePhoneController.text.isEmpty) {
Get.snackbar('Error', 'Please enter an phone address'.tr);
return;
}
// try {
String phoneNumber = formatPhoneNumber(invitePhoneController.text);
var response =
await CRUD().post(link: AppLink.addInvitationPassenger, payload: {
"driverId": box.read(BoxName.passengerID),
"inviterPassengerPhone": '+2$phoneNumber'
});
if (response != 'failure') {
var d = jsonDecode(response);
Get.snackbar('Success', 'Invite sent successfully'.tr);
String message = '${'*SEFER APP CODE*'.tr}\n\n'
'${"Use this code in registration".tr}\n'
'${"To get a gift for both".tr}\n\n'
'${"The period of this code is 1 hour".tr}\n\n'
'${'before'.tr} *${d['message']['expirationTime'].toString()}*\n\n'
'_*${d['message']['inviteCode'].toString()}*_\n\n'
'${"Install our app:".tr}\n'
'*Android:* https://play.google.com/store/apps/details?id=com.mobileapp.store.ride\n\n\n'
'*iOS:* https://apps.apple.com/us/app/sefer/id6458734951';
launchCommunication('whatsapp', '+2$phoneNumber', message);
invitePhoneController.clear();
} else {
Get.snackbar('Error'.tr, "Invite code already used".tr,
backgroundColor: AppColor.redColor,
duration: const Duration(seconds: 4));
}
// } catch (e) {
// Get.snackbar('Error', 'An error occurred'.tr);
// }
}
}
class PassengerStats {
final int totalInvites;
final int activeUsers;
final double totalEarnings;
PassengerStats({
this.totalInvites = 0,
this.activeUsers = 0,
this.totalEarnings = 0.0,
});
}

View File

@@ -1,10 +1,11 @@
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:get/get.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import '../../../main.dart';
class PromosController extends GetxController {
List<dynamic> promoList = [];
@@ -17,7 +18,9 @@ class PromosController extends GetxController {
}
Future getPromoByToday() async {
var res = await CRUD().get(link: AppLink.getPromoBytody, payload: {});
var res = await CRUD().get(link: AppLink.getPromoBytody, payload: {
'passengerID': box.read(BoxName.passengerID).toString(),
});
if (res.toString() == 'failure') {
// Get.defaultDialog(
// title: 'No Promo for today .'.tr,

View File

@@ -94,7 +94,7 @@ class LocaleController extends GetxController {
@override
void onInit() {
String storedLang = box.read(BoxName.lang) ?? "";
String storedLang = box.read(BoxName.lang) ?? "ar";
switch (storedLang) {
case "ar":
language = const Locale("ar");

View File

@@ -56,8 +56,7 @@ class MyTranslation extends Translations {
"I want to order for someone else": "أريد أن أطلب لشخص آخر",
"Cancel Trip from driver": "إلغاء الرحلة من السائق",
"If you want order to another person": "إذا كنت تريد الطلب لشخص آخر",
"We will look for a new driver.\nPlease wait.":
"سنبحث عن سائق جديد.\nمن فضلك انتظر.",
"upgrade price": "رفع السعر",
'airport': 'مطار',
"Best choice for a comfortable car with a flexible route and stop points. This airport offers visa entry at this price.":
@@ -234,15 +233,76 @@ iOS [https://getapp.cc/app/6458734951]
"Capture an Image of Your ID Document front":
"التقط صورة للواجهة الأمامية لوثيقة هويتك",
"NationalID": "الرقم القومي",
'You can share the SEFER App with your friends and earn rewards for rides they take using your code':
'يمكنك مشاركة تطبيق SEFER مع أصدقائك وكسب مكافآت من الرحلات التي يقومون بها باستخدام كودك.',
"FullName": "الاسم الكامل",
"No invitation found yet!": "لم يتم العثور على دعوات حتى الآن!",
"InspectionResult": "نتيجة الفحص",
"Criminal Record": "السجل الجنائي",
"Criminal Record": "السجل الجنائي", 'Share App': 'شارك التطبيق',
"The email or phone number is already registered.":
"البريد الإلكتروني أو رقم الهاتف مسجل بالفعل.",
'To become a ride-sharing driver on the Sefer app, you need to upload your driver\'s license, ID document, and car registration document. Our AI system will instantly review and verify their authenticity in just 2-3 minutes. If your documents are approved, you can start working as a driver on the Sefer app. Please note, submitting fraudulent documents is a serious offense and may result in immediate termination and legal consequences.':
'لِتُصْبِحَ سَائِقَاً لِلرُّكوبِ المُشْتَرَكِ عَلَى تَطْبِيق سَفَر، يَجِبُ عَلَيْكَ تَحْمِيل رُخْصَةِ القِيَادَةِ، وَثِيقَةِ الهُوِيَّةِ، وَوَثِيقَةَ تَسْجِيل السَّيَّارَةِ. سَيَقُومُ نِظَامُ الذَّكَاءِ الاِصْطِنَاعِيِّ لَدَيْنَا بِمُرَاجَعَةِ وَتَحْقِيقِ صِحَّةِ الوَثَائِقِ فِي غُضُونِ ٢-٣ دَقَائِقَ فَقَطْ. إِذَا تَمَّتْ المُوَافَقَةُ عَلَى وَثَائِقِكَ، يُمْكِنُكَ البَدْءُ فِي العَمَلِ كَسَائِقٍ عَلَى تَطْبِيق سَفَر. يُرْجَى مُلَاحَظَةُ، تَقْدِيمُ وَثَائِقَ مُزَورَةٍ يُعَدُّ جَرِيمَةً خَطِيرَةً وَقَدْ يَتَرَتَّبُ عَلَيْهِ اِنهَاءُ الحِسَابِ فَوْرِيَّاً وَعَوَاقِبُ قَانُونِيَّة.',
"Documents check": "فحص الوثائق",
"Driver's License": "رخصة القيادة",
"for your first registration!": "للتسجيل الأول!",
"Get it Now!": "احصل عليه الآن!",
"before": "قبل",
"Code not approved": "الرمز غير موافق عليه",
"3000 LE": "3000 جنيه مصري",
"Do you have an invitation code from another driver?":
"هل لديك كود دعوة من سائق آخر؟",
"Paste the code here": "الصق الكود هنا",
"No, I don't have a code": "لا، لا أملك كودا",
"Code approved": "تمت الموافقة على الكود",
"Install our app:": "قم بتثبيت تطبيقنا:",
"Invite another driver and both get a gift after he completes 100 trips!":
"ادع صديقًا ليكون سائقًا واحصلا على هدية بعد إكماله 100 مشوار!",
"Share App": "شارك التطبيق",
"Invite": "دعوة", "Are you sure?": "هل أنت متأكد؟",
"This will delete all recorded files from your device.":
"سيؤدي هذا إلى حذف جميع الملفات المسجلة من جهازك.",
"Select a file": "اختر ملفاً",
"Select a File": "اختر ملفاً", "Delete": "حذف",
'attach audio of complain': 'إرفاق صوت للشكوى',
"Phone Number Check": "فحص رقم الهاتف",
"Drivers received orders": "السائقون استقبلوا الطلبات",
'No audio files recorded.': 'لا توجد ملفات صوتية مسجلة.',
'This is for delivery or a motorcycle.':
"هذا للتوصيل أو للدراجة النارية.",
"We will look for a new driver.\nPlease wait.":
"سوف نبحث عن سائق جديد.\nيرجى الانتظار",
"Sefer Reminder": "تطبيق سفر",
"It's time to check the Sefer app!": "حان وقت استخدام تطبيق سفر",
"The email or phone number is already registered.":
"البريد الإلكتروني أو رقم الهاتف مسجل بالفعل.",
"you must insert token code": "يجب إدخال رمز التحقق.",
"Something went wrong. Please try again.":
"حدث خطأ ما. يرجى المحاولة مرة أخرى.",
"This is for delivery or a motorcycle.":
"هذا للتوصيل أو للدراجة النارية.",
"Trip Details": "تفاصيل الرحلة",
'The context does not provide any complaint details, so I cannot provide a solution to this issue. Please provide the necessary information, and I will be happy to assist you.':
"لا تتوفر تفاصيل الشكوى في السياق، لذا لا أستطيع تقديم حل لهذه المشكلة. يرجى تقديم المعلومات اللازمة، وسأكون سعيدًا بمساعدتك",
'Submit Your Complaint': "أرسل شكواك",
"Date": "التاريخ",
"Price": "السعر",
"Status": "الحالة",
"Choose from contact": "اختر من جهات الاتصال",
'attach correct audio': "إرفاق صوت للشكوى",
'be sure': 'كن متأكدًا',
'Audio uploaded successfully.': 'تم رفع الصوت بنجاح',
"Perfect for passengers seeking the latest car models with the freedom to choose any route they desire":
"مثالي للركاب الذين يبحثون عن أحدث موديلات السيارات مع حرية اختيار أي طريق يرغبون به",
"Share this code with your friends and earn rewards when they use it!":
"شارك هذا الرمز مع أصدقائك واحصل على مكافآت عند استخدامهم له!",
"Enter phone": "أدخل رقم الهاتف",
'You deserve the gift': "أنت تستحق الهدية",
"complete, you can claim your gift": " يمكنك المطالبة بهديتك",
"When": "‏عندما يكمل",
"Enter driver's phone": "أدخل رقم هاتف السائق",
"Send Invite": "أرسل الدعوة", "Show Invitations": "عرض الدعوات",
"License Type": "نوع الرخصة",
"National Number": "الرقم الوطني",
"Name (Arabic)": "الاسم بالعربي",

View File

@@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:SEFER/constant/colors.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:SEFER/constant/box_name.dart';
@@ -48,40 +50,41 @@ class ProfileController extends GetxController {
}
updatField(String columnName, TextInputType type) async {
Get.defaultDialog(
title: '${'Update'.tr} $columnName',
content: Column(
children: [
SizedBox(
width: Get.width * .7,
child: MyTextForm(
controller: txtController,
label: 'type here'.tr,
hint: 'type here',
type: type)
// TextField(
// controller: txtController,
// decoration: const InputDecoration(
// border: OutlineInputBorder(), hintText: 'type here'),
// ),
Get.dialog(
CupertinoAlertDialog(
title: Text('${'Update'.tr} $columnName'),
content: Column(
children: [
const SizedBox(height: 16), // Add spacing between title and input
CupertinoTextField(
controller: txtController,
placeholder: 'type here'.tr,
keyboardType: type,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: BoxDecoration(
border: Border.all(color: CupertinoColors.lightBackgroundGray),
borderRadius: BorderRadius.circular(8),
),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () async {
Get.back();
await updateColumn({
'id': box.read(BoxName.passengerID),
columnName: txtController.text,
});
if (columnName == 'first_name') {
box.write(BoxName.name, txtController.text);
}
),
const SizedBox(height: 20),
CupertinoButton(
color: AppColor.blueColor,
onPressed: () async {
Get.back();
await updateColumn({
'id': box.read(BoxName.passengerID),
columnName: txtController.text,
});
if (columnName == 'first_name') {
box.write(BoxName.name, txtController.text);
}
txtController.clear();
},
)
],
txtController.clear();
},
child: Text('Update'.tr),
),
],
),
),
);
}

View File

@@ -70,10 +70,11 @@ class RateController extends GetxController {
'token': token1,
});
if (res != 'failure') {
FirebaseMessagesController().sendNotificationToAnyWithoutData(
FirebaseMessagesController().sendNotificationToDriverMAP(
'You Have Tips'.tr,
'${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + (Get.find<MapPassengerController>().totalPassenger)}',
Get.find<MapPassengerController>().driverToken.toString(),
[],
'ding.wav',
);
}

View File

@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:math';
import 'package:SEFER/controller/payment/paymob/paymob_response.dart';
import 'package:firebase_core/firebase_core.dart';
@@ -12,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'constant/api_key.dart';
import 'constant/info.dart';
import 'constant/notification.dart';
import 'controller/firebase/firbase_messge.dart';
import 'controller/firebase/local_notification.dart';
import 'controller/local/local_controller.dart';
@@ -40,10 +42,24 @@ void main() async {
WidgetsFlutterBinding.ensureInitialized();
WakelockPlus.enable();
// await LocationController().startLocationUpdates();
if (Platform.isAndroid) {
await NotificationController().initNotifications();
}
// if (Platform.isAndroid) {
NotificationController notificationController =
Get.put(NotificationController());
await notificationController.initNotifications();
// Generate a random index to pick a message
final random = Random();
final randomMessage = messages[random.nextInt(messages.length)];
// Schedule the notification with the random message
notificationController.scheduleDailyNotifications(
randomMessage.split(':')[0],
randomMessage.split(':')[1],
"ding",
);
// await NotificationController().initNotifications();
// }
await GetStorage.init();
// Get.put(DriverCallController());
// await AC().gAK();

View File

@@ -19,7 +19,7 @@ class SmsSignupEgypt extends StatelessWidget {
Widget build(BuildContext context) {
Get.put(RegisterController());
return MyScafolld(
title: 'Phone Check'.tr,
title: "Phone Number Check".tr,
body: [
GetBuilder<RegisterController>(builder: (registerController) {
return ListView(
@@ -99,13 +99,16 @@ class SmsSignupEgypt extends StatelessWidget {
// Submit button
registerController.isLoading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () async {
!registerController.isSent
? await registerController.sendOtpMessage()
: await registerController.verifySMSCode();
},
title: 'Submit'.tr,
: Padding(
padding: const EdgeInsets.all(16.0),
child: MyElevatedButton(
onPressed: () async {
!registerController.isSent
? await registerController.sendOtpMessage()
: await registerController.verifySMSCode();
},
title: 'Submit'.tr,
),
),
],
);

View File

@@ -1,101 +1,153 @@
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/main.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter/cupertino.dart';
class AboutPage extends StatelessWidget {
const AboutPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'About Us'.tr,
body: [
// Company Logo (consider adding an image asset)
ListView(
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('About Us'.tr),
),
child: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Company Logo
Center(
child: Image.asset(
'assets/images/logo.png', // Replace with your logo image asset path
height: 100.0,
width: 100.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Image.asset(
'assets/images/logo.png', // Replace with your logo image asset path
height: 100.0,
width: 100.0,
),
),
), // Company Name and Location
),
// Company Name and Location
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}',
style: AppStyle.headTitle2,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
// About Us Description
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
.tr,
style: AppStyle.title,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16.0,
),
textAlign: TextAlign.center,
),
), // Security Features List
const SizedBox(
height: 20,
),
const SizedBox(height: 20),
// Security Features
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
children: [
Row(
children: [
const Icon(Icons.lock, color: Colors.blue),
const Icon(CupertinoIcons.lock_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0),
Text(
'Most Secure Methods'.tr,
style: AppStyle.title,
Expanded(
child: Text(
'Most Secure Methods'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 8.0),
Row(
children: [
const Icon(Icons.phone, color: Colors.blue),
const Icon(CupertinoIcons.phone_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0),
Text(
'In-App VOIP Calls'.tr,
style: AppStyle.title,
Expanded(
child: Text(
'In-App VOIP Calls'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
),
],
),
const SizedBox(height: 8.0),
Row(
children: [
const Icon(Icons.videocam, color: Colors.blue),
const Icon(CupertinoIcons.videocam_fill,
color: CupertinoColors.activeBlue),
const SizedBox(width: 8.0),
Text(
'Recorded Trips for Safety'.tr,
style: AppStyle.title,
Expanded(
child: Text(
'Recorded Trips for Safety'.tr,
style: CupertinoTheme.of(context)
.textTheme
.textStyle
.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
),
),
],
),
],
),
), // Affordability Highlight
),
// Affordability Highlight
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.'
.tr,
style: AppStyle.title,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16.0,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 20),
],
),
// About Us Text
],
isleading: true);
),
),
);
}
}

View File

@@ -0,0 +1,437 @@
import 'package:SEFER/constant/style.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/home/profile/invit_controller.dart';
import '../../../main.dart';
class ShareAppPage extends StatelessWidget {
final InviteController controller = Get.put(InviteController());
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.systemBackground,
appBar: AppBar(
backgroundColor: CupertinoColors.systemBackground,
elevation: 0,
title: Text(
'Invite'.tr,
style: const TextStyle(color: CupertinoColors.label),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios, color: AppColor.blueColor),
onPressed: () => Get.back(),
),
),
body: SafeArea(
child: GetBuilder<InviteController>(
builder: (controller) {
return Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: _buildPassengerTab(context),
),
),
],
);
},
),
),
);
}
Widget _buildPassengerTab(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: [
Text(
"Share this code with your friends and earn rewards when they use it!"
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 13,
),
),
],
),
),
const SizedBox(height: 20),
_buildPhoneInput(),
const SizedBox(height: 20),
_buildActionButtonsPassengers(),
const SizedBox(height: 20),
const SizedBox(height: 20),
_buildInvitationsListPassengers(context),
],
);
}
Widget _buildPhoneInput() {
return Container(
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Expanded(
child: CupertinoTextField.borderless(
controller: controller.invitePhoneController,
placeholder: 'Enter phone'.tr,
padding: const EdgeInsets.all(12),
keyboardType: TextInputType.phone,
),
),
CupertinoButton(
child: const Icon(CupertinoIcons.person_badge_plus,
color: AppColor.blueColor),
onPressed: () async {
await controller.pickContacts();
if (controller.contacts.isNotEmpty) {
if (box.read(BoxName.isSavedPhones) == null) {
controller.savePhoneToServer();
box.write(BoxName.isSavedPhones, true);
}
_showContactsDialog(Get.context!);
}
},
),
],
),
);
}
Widget _buildActionButtonsPassengers() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
onPressed: controller.sendInviteToPassenger,
child: Text(
'Send Invite'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 6,
offset: const Offset(0, 3),
),
],
),
child: CupertinoButton(
color: AppColor.blueColor,
borderRadius: BorderRadius.circular(10),
padding: const EdgeInsets.symmetric(vertical: 14),
child: Text(
'Show Invitations'.tr,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CupertinoColors.white,
),
),
onPressed: () async {
controller.fetchDriverStatsPassengers();
},
),
),
),
],
),
);
}
Widget _buildInvitationsListPassengers(BuildContext context) {
return SizedBox(
height: Get.height * .4,
child: controller.driverInvitationDataToPassengers.isEmpty
? Center(
child: Text(
"No invitation found yet!".tr,
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 17,
),
),
)
: ListView.builder(
itemCount: controller.driverInvitationDataToPassengers.length,
itemBuilder: (context, index) {
return _buildInvitationItemPassengers(context, index);
},
),
);
}
Widget _buildInvitationItemPassengers(BuildContext context, int index) {
// Extracting the data from the sample JSON-like structure
var invitation = controller.driverInvitationDataToPassengers[index];
int countOfInvitDriver =
int.tryParse(invitation['countOfInvitDriver']?.toString() ?? '0') ?? 0;
double progressValue = (countOfInvitDriver / 10.0).clamp(0.0, 1.0);
return GestureDetector(
onTap: () {
controller.onSelectPassengerInvitation(index);
},
child: Container(
margin: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
invitation['passengerName']
.toString(), // Handle null or missing data
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: progressValue,
backgroundColor: CupertinoColors.systemGrey4,
valueColor:
const AlwaysStoppedAnimation<Color>(AppColor.blueColor),
minHeight: 6,
),
),
const SizedBox(height: 4),
Text(
'$countOfInvitDriver / 2 ${'Trip'.tr}', // Show trips completed
style: const TextStyle(
fontSize: 13,
color: CupertinoColors.secondaryLabel,
),
),
],
),
),
);
}
Widget _buildPassengerStats(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: CupertinoColors.systemGrey6,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Your Rewards".tr,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
color: CupertinoColors.label,
),
),
const SizedBox(height: 16),
_buildStatItem(
context,
"Total Invites".tr,
controller.driverInvitationDataToPassengers[0]['countOfInvitDriver']
.toString(),
),
_buildStatItem(
context,
"Active Users".tr,
controller.driverInvitationDataToPassengers[0]['passengerName']
.toString(),
),
],
),
);
}
Widget _buildStatItem(BuildContext context, String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 15,
),
),
Text(
value,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: AppColor.blueColor,
fontSize: 15,
),
),
],
),
);
}
void _showContactsDialog(BuildContext context) {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => Container(
height: 400,
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
borderRadius: const BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [
BoxShadow(
color: CupertinoColors.black.withOpacity(0.2),
offset: const Offset(0, -4),
blurRadius: 10,
),
],
),
child: Column(
children: [
// Header with cancel and title
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
color: CupertinoColors.systemGrey6,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CupertinoButton(
padding: EdgeInsets.zero,
child: Text(
'Cancel'.tr,
style: const TextStyle(color: CupertinoColors.systemBlue),
),
onPressed: () => Navigator.pop(context),
),
Container(
child: Text('Choose from contact'.tr,
style: AppStyle.title)),
const SizedBox(width: 60), // Balance for Cancel button
],
),
),
// Contact list
Expanded(
child: ListView.builder(
itemCount: controller.contactMaps.length,
itemBuilder: (context, index) {
final contact = controller.contactMaps[index];
return CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () {
controller.selectPhone(contact['phones'].toString());
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
border: Border(
bottom: BorderSide(
color: CupertinoColors.separator.withOpacity(0.5),
),
),
),
child: Row(
children: [
// Display contact name and phone number
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact['name'],
style: const TextStyle(
color: CupertinoColors.label,
fontSize: 17,
fontWeight: FontWeight.w500,
),
),
Text(
controller.formatPhoneNumber(
contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 15,
),
),
],
),
),
// Chevron icon for selection
const Icon(
CupertinoIcons.chevron_forward,
color: CupertinoColors.systemGrey2,
),
],
),
),
);
},
),
),
],
),
),
);
}
}

View File

@@ -1,14 +1,11 @@
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:share/share.dart';
import 'package:path/path.dart' as path;
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/audio_record1.dart';
import '../../../controller/functions/tts.dart';
import '../../widgets/elevated_btn.dart';
class TripsRecordedPage extends StatelessWidget {
const TripsRecordedPage({
@@ -21,132 +18,193 @@ class TripsRecordedPage extends StatelessWidget {
title: 'Trips recorded'.tr,
body: [
GetBuilder<AudioRecorderController>(builder: (audio) {
return Column(
children: [
FutureBuilder<List<String>>(
future: audio.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
final recordedFiles = snapshot.data!;
return DropdownButton<String>(
value: audio.selectedFilePath,
onChanged: (value) {
audio.selectedFilePath = value;
audio.playRecordedFile(value!);
audio.update();
},
items: recordedFiles
.map((file) => DropdownMenuItem<String>(
value: file,
child: Text(path.basename(file)),
))
.toList(),
);
} else {
return Text('Error: ${snapshot.error}');
}
},
),
Slider(
value: audio.currentPosition,
max: audio.totalDuration,
inactiveColor: AppColor.accentColor,
label: audio.currentPosition.toString(),
onChanged: (value) {
audio.currentPosition = value;
audio.audioPlayer.seek(Duration(seconds: value.toInt()));
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: Icon(
audio.isPlaying ? Icons.pause : Icons.play_arrow),
onPressed: () {
if (audio.isPlaying) {
audio.pausePlayback();
} else {
audio.resumePlayback();
}
audio.update();
},
),
IconButton(
icon: const Icon(Icons.stop),
onPressed: () {
audio.stopPlayback();
audio.update();
},
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () async {
Get.defaultDialog(
title: 'Are you sure to delete recorded files'.tr,
content: Column(
children: [
IconButton(
onPressed: () {
Get.find<TextToSpeechController>().speakText(
'this will delete all files from your device'
.tr);
},
icon: const Icon(Icons.headphones),
),
Text(
'this will delete all files from your device'
.tr,
textAlign: TextAlign.center,
style: AppStyle.title,
),
],
),
titleStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'Delete'.tr,
kolor: AppColor.redColor,
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FutureBuilder<List<String>>(
future: audio.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CupertinoActivityIndicator());
} else if (snapshot.hasData) {
final recordedFiles = snapshot.data!;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () async {
await audio.deleteAllRecordedFiles();
Get.back();
Get.back();
String? selectedFile =
await showCupertinoModalPopup<String>(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Select a File'.tr),
actions: recordedFiles
.map(
(file) => CupertinoActionSheetAction(
child: Text(path.basename(file)),
onPressed: () {
Navigator.of(context).pop(file);
},
),
)
.toList(),
);
},
);
if (selectedFile != null) {
audio.selectedFilePath = selectedFile;
audio.playRecordedFile(selectedFile);
audio.update();
}
},
child: Text(
audio.selectedFilePath != null
? path.basename(audio.selectedFilePath!)
: 'Select a File'.tr,
style: CupertinoTheme.of(context)
.textTheme
.actionTextStyle
.copyWith(color: CupertinoColors.activeBlue),
),
),
);
} else {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text('Error: ${snapshot.error}'),
);
}
},
),
// Cupertino-style slider for seeking audio
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CupertinoSlider(
value: audio.totalDuration > 0
? audio.currentPosition / audio.totalDuration
: 0.0, // Normalize to a value between 0.0 and 1.0
min: 0.0,
max: 1.0, // Maximum value is now 1.0
activeColor: CupertinoColors.activeBlue,
onChanged: (value) {
final newPosition = value * audio.totalDuration;
audio.currentPosition = newPosition;
audio.audioPlayer
.seek(Duration(seconds: newPosition.toInt()));
audio.update();
},
),
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(16.0),
color: Colors.grey[200],
),
// iOS-style playback controls
Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
audio.selectedFilePath != null
? '${'Selected file:'.tr} ${path.basename(audio.selectedFilePath!)}'
: 'No file selected'.tr,
style: AppStyle.subtitle,
),
if (audio.selectedFilePath != null)
IconButton(
icon: const Icon(Icons.share),
onPressed: () {
Share.shareFiles([audio.selectedFilePath!]);
},
CupertinoButton(
padding: EdgeInsets.zero,
child: Icon(
audio.isPlaying
? CupertinoIcons.pause
: CupertinoIcons.play_arrow,
color: CupertinoColors.activeBlue,
),
onPressed: () {
if (audio.isPlaying) {
audio.pausePlayback();
} else {
audio.resumePlayback();
}
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.stop,
color: CupertinoColors.destructiveRed),
onPressed: () {
audio.stopPlayback();
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.delete,
color: CupertinoColors.destructiveRed),
onPressed: () async {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Are you sure?'.tr),
message: Text(
'This will delete all recorded files from your device.'
.tr,
textAlign: TextAlign.center,
),
actions: [
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
await audio.deleteAllRecordedFiles();
Navigator.pop(context);
audio.update();
},
child: Text('Delete'.tr),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel'.tr),
),
);
},
);
},
),
],
),
),
),
],
// File selection and sharing
if (audio.selectedFilePath != null)
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(16.0),
color: CupertinoColors.systemGrey6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Selected file: ${path.basename(audio.selectedFilePath!)}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.share),
onPressed: () {
Share.shareFiles([audio.selectedFilePath!]);
},
),
],
),
),
),
],
),
);
}),
})
],
isleading: true);
}

View File

@@ -8,6 +8,7 @@ import 'package:SEFER/views/widgets/my_scafold.dart';
import 'HomePage/about_page.dart';
import 'HomePage/frequentlyQuestionsPage.dart';
import 'HomePage/share_app_page.dart';
import 'HomePage/trip_record_page.dart';
import 'profile/passenger_profile_page.dart';
@@ -21,7 +22,7 @@ class HomePage extends StatelessWidget {
isleading: true,
title: 'Home Page'.tr,
body: [
Column(
ListView(
children: [
ListTile(
onTap: () {
@@ -116,6 +117,19 @@ class HomePage extends StatelessWidget {
),
onTap: () => Get.to(() => const AboutPage()),
),
ListTile(
leading: const Icon(Icons.share),
title: Text(
'Share App'.tr,
style: AppStyle.headTitle2,
),
subtitle: Text(
'You can share the SEFER App with your friends and earn rewards for rides they take using your code'
.tr,
style: AppStyle.title,
),
onTap: () => Get.to(() => ShareAppPage()),
),
],
),
],

View File

@@ -27,7 +27,7 @@ class ApplyOrderWidget extends StatelessWidget {
right: 0,
child: Container(
decoration: AppStyle.boxDecoration,
height: Get.height * .35,
height: Get.height * .36,
child: ListView(
children: [
InkWell(
@@ -87,11 +87,11 @@ class ApplyOrderWidget extends StatelessWidget {
width: 10,
),
Container(
height: Get.height * .3,
height: Get.height * .34,
width: Get.width * .9,
decoration: AppStyle.boxDecoration,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
@@ -241,12 +241,13 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell(
onTap: () {
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
'message From passenger',
.sendNotificationToDriverMAP(
'message From passenger'.tr,
'Hello, I\'m at the agreed-upon location'
.tr,
controller.driverToken
.toString(),
[],
'ding.wav',
);
Get.back();
@@ -272,11 +273,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell(
onTap: () {
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
.sendNotificationToDriverMAP(
'message From passenger'.tr,
'My location is correct. You can search for me using the navigation app'
.tr,
controller.driverToken,
[],
'ding.wav',
);
Get.back();
@@ -302,11 +304,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell(
onTap: () {
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
.sendNotificationToDriverMAP(
'message From passenger',
'My location is correct. You can search for me using the navigation app'
.tr,
controller.driverToken,
[],
'ding.wav',
);
Get.back();
@@ -331,11 +334,12 @@ class ApplyOrderWidget extends StatelessWidget {
InkWell(
onTap: () {
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
.sendNotificationToDriverMAP(
'message From passenger',
"How much longer will you be?"
.tr,
controller.driverToken,
[],
'ding.wav',
);
Get.back();
@@ -385,13 +389,14 @@ class ApplyOrderWidget extends StatelessWidget {
IconButton(
onPressed: () {
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
.sendNotificationToDriverMAP(
'message From passenger',
controller
.messageToDriver
.text,
controller
.driverToken,
[],
'ding.wav');
controller
.messageToDriver
@@ -466,7 +471,7 @@ class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
color: controller.remainingTimeDriverWaitPassenger5Minute < 60
? AppColor.redColor
: AppColor.greenColor,
minHeight: 25,
minHeight: 15,
borderRadius: BorderRadius.circular(15),
value:
controller.progressTimerDriverWaitPassenger5Minute.toDouble(),
@@ -513,7 +518,7 @@ class TimeDriverToPassenger extends StatelessWidget {
Container(
decoration: AppStyle.boxDecoration,
width: Get.width * .7,
height: 35,
height: 15,
// color: AppColor.yellowColor,
),
Stack(

View File

@@ -3,11 +3,11 @@ import 'package:get/get.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/home/map_passenger_controller.dart';
import '../../widgets/elevated_btn.dart';
GetBuilder<MapPassengerController> cancelRidePage() {
Get.put(MapPassengerController());
final List<String> reasons = [
"I don't need a ride anymore".tr,
"I was just trying the application".tr,
@@ -16,80 +16,74 @@ GetBuilder<MapPassengerController> cancelRidePage() {
"I don't have a reason".tr,
"Other".tr,
];
return GetBuilder<MapPassengerController>(
builder: (controller) => controller.isCancelRidePageShown
? Positioned(
left: Get.width * .1,
top: Get.width * .2,
right: Get.width * .1,
bottom: Get.width * .15,
left: 20,
top: Get.height * 0.15,
right: 20,
bottom: Get.height * 0.15,
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColor.secondaryColor,
color: Colors.white,
boxShadow: [
const BoxShadow(
color: AppColor.accentColor,
offset: Offset(2, 2),
blurRadius: 5),
BoxShadow(
color: AppColor.accentColor.withOpacity(.4),
offset: const Offset(-2, -2),
blurRadius: 5)
color: Colors.black.withOpacity(0.2),
offset: const Offset(0, 8),
blurRadius: 16,
),
],
borderRadius: const BorderRadius.all(Radius.circular(15)),
borderRadius: BorderRadius.circular(20),
),
height: Get.height * .7,
width: Get.width * .7,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Can we know why you want to cancel Ride ?'.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
Text(
'Can we know why you want to cancel Ride ?'.tr,
style: AppStyle.title
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(
height: 380,
width: 300,
child: ListView.builder(
const SizedBox(height: 20),
Expanded(
child: ListView.separated(
itemCount: reasons.length,
separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) {
return ListTile(
title: InkWell(
onTap: () {
controller.selectReason(
index,
reasons[index].toString(),
);
},
child: Text(
reasons[index],
style: AppStyle.title,
)),
title: Text(
reasons[index],
style: AppStyle.title.copyWith(fontSize: 16),
),
leading: Radio(
value: index,
groupValue: controller.selectedReason,
onChanged: (int? value) {
controller.selectReason(
value!,
reasons[index].toString(),
);
controller.selectReason(value!, reasons[index]);
},
activeColor: AppColor.primaryColor,
),
onTap: () {
controller.selectReason(index, reasons[index]);
},
);
},
),
),
const SizedBox(height: 20),
MyElevatedButton(
title: 'Cancel Ride'.tr,
onPressed: () {
if (controller.selectedReason == -1) {
Get.snackbar('You Should be select reason.'.tr, '',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.redColor);
Get.snackbar(
'You Should be select reason.'.tr,
'',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.redColor,
colorText: Colors.white,
);
} else {
controller.cancelRide();
}

View File

@@ -586,7 +586,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
textToSpeechController,
image: 'assets/images/freeRide.png',
text:
'Perfect for adventure seekers who want to experience something new and exciting'
"Perfect for passengers seeking the latest car models with the freedom to choose any route they desire"
.tr),
confirm: MyElevatedButton(
kolor: AppColor.greenColor,
@@ -656,8 +656,10 @@ class CarDetailsTypeToChoose extends StatelessWidget {
title: 'Next'.tr,
onPressed: () {
Get.back();
if (box.read(BoxName.gender) !=
null) {
if (box
.read(BoxName.gender)
.toString() !=
'') {
mapPassengerController
.isBottomSheetShown = false;
mapPassengerController.update();
@@ -710,59 +712,108 @@ class CarDetailsTypeToChoose extends StatelessWidget {
MainAxisAlignment
.spaceBetween,
children: [
Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text('Awfar Car'
.tr),
Text(mapPassengerController
.totalPassengerRayehGaiBalash
.toStringAsFixed(
0)),
],
),
)),
Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text('Speed'.tr),
Text(mapPassengerController
.totalPassengerRayehGai
.toStringAsFixed(
0)),
],
),
)),
Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text(
'Comfort'.tr),
Text(mapPassengerController
.totalPassengerRayehGaiComfort
.toStringAsFixed(
0)),
],
),
))
GestureDetector(
onTap: () {
Get.back();
mapPassengerController
.totalPassenger =
mapPassengerController
.totalPassengerRayehGaiBalash;
mapPassengerController
.isBottomSheetShown =
false;
mapPassengerController
.update();
mapPassengerController
.changeCashConfirmPageShown();
},
child: Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text('Awfar Car'
.tr),
Text(mapPassengerController
.totalPassengerRayehGaiBalash
.toStringAsFixed(
0)),
],
),
)),
),
GestureDetector(
onTap: () {
Get.back();
mapPassengerController
.totalPassenger =
mapPassengerController
.totalPassengerRayehGai;
mapPassengerController
.isBottomSheetShown =
false;
mapPassengerController
.update();
mapPassengerController
.changeCashConfirmPageShown();
},
child: Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text(
'Speed'.tr),
Text(mapPassengerController
.totalPassengerRayehGai
.toStringAsFixed(
0)),
],
),
)),
),
GestureDetector(
onTap: () {
Get.back();
mapPassengerController
.totalPassenger =
mapPassengerController
.totalPassengerRayehGaiComfort;
mapPassengerController
.isBottomSheetShown =
false;
mapPassengerController
.update();
mapPassengerController
.changeCashConfirmPageShown();
},
child: Container(
decoration: AppStyle
.boxDecoration1,
child: Padding(
padding:
const EdgeInsets
.all(8.0),
child: Column(
children: [
Text('Comfort'
.tr),
Text(mapPassengerController
.totalPassengerRayehGaiComfort
.toStringAsFixed(
0)),
],
),
)),
)
],
),
cancel: MyElevatedButton(
@@ -951,9 +1002,16 @@ class BurcMoney extends StatelessWidget {
}
}
class HeaderDestination extends StatelessWidget {
class HeaderDestination extends StatefulWidget {
const HeaderDestination({super.key});
@override
_HeaderDestinationState createState() => _HeaderDestinationState();
}
class _HeaderDestinationState extends State<HeaderDestination> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return GetBuilder<MapPassengerController>(
@@ -965,95 +1023,64 @@ class HeaderDestination extends StatelessWidget {
top: Get.height * .08,
left: 5,
right: 5,
child: Container(
decoration: AppStyle.boxDecoration1,
height: Get.height * .15,
width: Get.width * .8,
child: InkWell(
onTap: () {
// mapPassengerController
// .getDialog('Are you want to change'.tr, '', () {
// Get.back();
// mapPassengerController.cancelRide();
// });
MyDialog().getDialog(
"Change Route".tr,
'You can change the destination by long-pressing any point on the map'
.tr, () {
Get.back();
});
},
child: GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
decoration: AppStyle.boxDecoration1,
height: _isExpanded ? Get.height * .13 : Get.height * .06,
width: Get.width * .9,
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
child: SizedBox(
height: Get.height * .08,
child: ListView(
// crossAxisAlignment: CrossAxisAlignment.start,
//
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'🟢 ',
_isExpanded
? Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('🟢 ', style: AppStyle.subtitle),
Expanded(
child: Text(
mapPassengerController.startNameAddress,
style: AppStyle.subtitle,
overflow: TextOverflow.ellipsis,
),
SizedBox(
// height: Get.height * .03,
width: Get.width * .8,
child: Text(
mapPassengerController.startNameAddress,
style: AppStyle.subtitle,
),
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'🔴 ',
style: AppStyle.subtitle,
),
SizedBox(
// height: Get.height * .03,
width: Get.width * .8,
child: Text(
mapPassengerController.endNameAddress,
style: AppStyle.subtitle,
),
),
],
),
],
),
],
)
: const SizedBox(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('🔴 ', style: AppStyle.subtitle),
Expanded(
child: Text(
mapPassengerController.endNameAddress,
style: AppStyle.subtitle,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
if (_isExpanded)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'📍 ',
style: AppStyle.subtitle,
),
SizedBox(
width: Get.width * .8,
Text('📍', style: AppStyle.subtitle),
Expanded(
child: Text(
'${mapPassengerController.distance} ${'KM'.tr}${mapPassengerController.hours > 0 ? '${'Your Ride Duration is '.tr}${mapPassengerController.hours} ${'H and'.tr} ${mapPassengerController.minutes} ${'m'.tr}' : '${'Your Ride Duration is '.tr} ${mapPassengerController.minutes} ${'m'.tr}'}',
style: AppStyle.subtitle,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
],
),
),

View File

@@ -29,7 +29,8 @@ class CashConfirmPageShown extends StatelessWidget {
? controller.cashConfirmPageShown
: 0,
decoration: BoxDecoration(
color: box.read(BoxName.carType) == 'Lady'
color: box.read(BoxName.carType) == 'Lady' ||
box.read(BoxName.carType) == 'Pink Bike'
? Colors.pink.shade100
: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),

View File

@@ -419,7 +419,7 @@ class GoogleMapPassengerWidget extends StatelessWidget {
},
mapType:
controller.mapType ? MapType.satellite : MapType.normal,
controller.mapType ? MapType.satellite : MapType.terrain,
myLocationButtonEnabled: true,
// liteModeEnabled: true, tiltGesturesEnabled: false,

View File

@@ -1,7 +1,13 @@
import 'dart:math';
import 'package:SEFER/views/auth/login_page.dart';
import 'package:SEFER/views/auth/sms_verfy_page.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../constant/colors.dart';
import '../../../constant/notification.dart';
import '../../../controller/firebase/local_notification.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/map_passenger_controller.dart';
@@ -89,17 +95,14 @@ GetBuilder<MapPassengerController> leftMainMenuIcons() {
// borderRadius: BorderRadius.circular(15)),
// child: IconButton(
// onPressed: () async {
// FirebaseMessagesController().sendNotificationToAnyWithoutData(
// 'Order'.tr,
// 'from: ',
// // jsonDecode(value)['message'].toString(),
// 'dEugS-JOT4Ka5riF4s5TEN:APA91bEDL_W7BuEQGbyL-RMaKiMWDlURXhFuaybe5WurTUV8K5eIooSGe22yY22_U2hEZcfPr46ig1v--l00dbOGiivazxvmTyhUyQQW6lJsuIN-wordGtBxtREyeYtEKvxIa1J4ApEu',
// 'order.wav'
// // polylineCoordinates.toString()
// );
// // print(box.read(BoxName.tokenFCM));
// //
// final random = Random();
// final randomMessage =
// messages[random.nextInt(messages.length)];
// NotificationController().showNotification(
// randomMessage.split(':')[0],
// randomMessage.split(':')[1],
// "ding",
// );
// },
// icon: const Icon(
// Icons.voice_chat,

View File

@@ -30,7 +30,7 @@ class RideBeginPassenger extends StatelessWidget {
return Positioned(
left: 10,
right: 10,
bottom: 4,
bottom: 10,
child: Container(
decoration: AppStyle.boxDecoration,
height: controller.statusRide == 'Begin' ? Get.height * .33 : 0,

View File

@@ -8,6 +8,121 @@ import 'package:SEFER/views/widgets/my_textField.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../constant/links.dart';
// class SearchingCaptainWindow extends StatelessWidget {
// const SearchingCaptainWindow({super.key});
// Widget _buildDriverAvatars(MapPassengerController controller) {
// // If no drivers yet, show loading indicator
// if (controller.isSearchingWindow) {
// // Check if dataCarsLocationByPassenger or its 'data' is null
// if (controller.dataCarsLocationByPassenger == null ||
// controller.dataCarsLocationByPassenger['data'] == null ||
// controller.dataCarsLocationByPassenger['data'].isEmpty) {
// return const SizedBox(
// height: 60,
// child: Center(
// child: CircularProgressIndicator(
// valueColor:
// AlwaysStoppedAnimation<Color>(AppColor.secondaryColor),
// ),
// ),
// );
// }
// }
// return SizedBox(
// height: 60,
// child: ListView.builder(
// scrollDirection: Axis.horizontal,
// itemCount: controller.dataCarsLocationByPassenger['data'].length,
// padding: const EdgeInsets.symmetric(horizontal: 16),
// itemBuilder: (context, index) {
// final driver = controller.dataCarsLocationByPassenger['data'][index];
// return Padding(
// padding: const EdgeInsets.only(right: 8),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// CircleAvatar(
// radius: 25,
// backgroundColor: AppColor.secondaryColor,
// child: ClipOval(
// child: Image.network(
// '${AppLink.server}/portrate_captain_image/${driver['driver_id']}.jpg',
// width: 50,
// height: 50,
// fit: BoxFit.cover,
// errorBuilder: (context, error, stackTrace) {
// return const Icon(
// Icons.person,
// color: Colors.white,
// size: 30,
// );
// },
// ),
// ),
// ),
// ],
// ),
// );
// },
// ),
// );
// }
// @override
// Widget build(BuildContext context) {
// return GetBuilder<MapPassengerController>(
// builder: (mapPassengerController) {
// return mapPassengerController.isSearchingWindow
// ? Positioned(
// bottom: 0,
// left: 0,
// right: 0,
// child: Container(
// decoration: AppStyle.boxDecoration1,
// height: Get.height *
// .3, // Increased height to accommodate avatars
// child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// children: [
// SizedBox(
// width: Get.width * .7,
// child: const LinearProgressIndicator(
// minHeight: 6,
// backgroundColor: AppColor.yellowColor,
// color: AppColor.secondaryColor,
// ),
// ),
// mapPassengerController.driverOrderStatus == 'recive'
// ? Text(
// "Drivers received orders".tr,
// style: AppStyle.title,
// )
// : Text(
// "We are searching for the nearest driver to you"
// .tr,
// style: AppStyle.title,
// ),
// Text(
// 'please wait till driver accept your order'.tr,
// style: AppStyle.title,
// ),
// // New: Driver avatars section
// _buildDriverAvatars(mapPassengerController),
// _buildTimer(mapPassengerController),
// ],
// ),
// ),
// )
// : const SizedBox();
// },
// );
// }
// }
class SearchingCaptainWindow extends StatelessWidget {
const SearchingCaptainWindow({super.key});

View File

@@ -116,9 +116,10 @@ class CupertinoDriverListWidget extends StatelessWidget {
width: 20,
height: 20,
decoration: BoxDecoration(
color: hexToColor(
driver['color_hex'].toString()) ??
Colors.amber,
color: driver['color_hex'].toString() == 'null'
? Colors.amber
: hexToColor(
driver['color_hex'].toString()),
borderRadius: BorderRadius.circular(4),
border: Border.all(),
),
@@ -151,9 +152,11 @@ class CupertinoDriverListWidget extends StatelessWidget {
width: 20,
height: 20,
decoration: BoxDecoration(
color: hexToColor(
driver['color_hex'].toString()) ??
AppColor.bronze,
color:
driver['color_hex'].toString() == 'null'
? Colors.amber
: hexToColor(
driver['color_hex'].toString()),
borderRadius: BorderRadius.circular(4),
border: Border.all(),
),
@@ -204,7 +207,9 @@ class CupertinoDriverListWidget extends StatelessWidget {
width: 20,
height: 20,
decoration: BoxDecoration(
color: hexToColor(driver['color_hex'].toString()),
color: driver['color_hex'].toString() == 'null'
? Colors.amber
: hexToColor(driver['color_hex'].toString()),
borderRadius: BorderRadius.circular(4),
border: Border.all(),
),

View File

@@ -1,64 +1,199 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:SEFER/views/widgets/my_scafold.dart';
import 'package:SEFER/views/widgets/mycircular.dart';
import 'dart:convert';
import '../../../controller/home/profile/complaint_controller.dart';
import '../../widgets/elevated_btn.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/constant/style.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/controller/home/profile/complaint_controller.dart';
import 'package:SEFER/views/widgets/my_dialog.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'dart:io';
import '../../../controller/functions/audio_record1.dart';
class ComplaintPage extends StatelessWidget {
// Rename class
ComplaintPage({super.key});
ComplaintController complaintController =
Get.put(ComplaintController()); // Update controller instance
final ComplaintController complaintController =
Get.put(ComplaintController());
final AudioRecorderController audioRecorderController =
Get.put(AudioRecorderController());
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Complaint'.tr,
body: [
Padding(
padding: const EdgeInsets.all(26),
child: Form(
key: complaintController.formKey,
child: Column(
children: [
TextFormField(
controller: complaintController.complaintController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: 'Enter your complaint here'.tr,
labelText: 'Complaint'.tr, // Update label
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your complaint.'.tr;
}
return null;
},
),
const SizedBox(height: 20),
complaintController.isLoading
? const MyCircularProgressIndicator()
: MyElevatedButton(
onPressed: () {
if (complaintController.formKey.currentState!
.validate()) {
complaintController
.addComplaint(); // Update method name
// Clear the complaint form
complaintController.formKey.currentState!.reset();
}
},
title: 'Submit'.tr,
),
],
),
),
return GetBuilder<ComplaintController>(builder: (complaintController) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Complaint'.tr, style: AppStyle.title),
),
],
isleading: true,
);
child: complaintController.isLoading
? const Center(child: CupertinoActivityIndicator())
: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: complaintController.formKey,
child: ListView(
children: [
// Complaint Text Field
CupertinoFormSection(
header: Text('Submit Your Complaint'.tr),
children: [
CupertinoTextField(
controller:
complaintController.complaintController,
placeholder: 'Enter your complaint here'.tr,
padding: const EdgeInsets.symmetric(
vertical: 12, horizontal: 16),
maxLines: 5,
decoration: BoxDecoration(
border: Border.all(
color: CupertinoColors.systemGrey4),
borderRadius: BorderRadius.circular(10),
color: CupertinoColors.white,
),
style: AppStyle.subtitle,
),
],
),
const SizedBox(height: 24),
// FutureBuilder to load recorded audio files
FutureBuilder<List<String>>(
future: audioRecorderController.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CupertinoActivityIndicator());
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}',
style: AppStyle.subtitle);
} else if (snapshot.hasData &&
snapshot.data!.isEmpty) {
return Text('No audio files recorded.'.tr,
style: AppStyle.subtitle);
}
// List of recorded audio files
return CupertinoFormSection(
header: Text('attach audio of complain'.tr),
children: snapshot.data!.map((audioFilePath) {
final audioFile = File(audioFilePath);
return CupertinoListTile(
title: Text(audioFilePath.split('/').last,
style: AppStyle.title),
trailing: const Icon(
CupertinoIcons.play_arrow,
color: AppColor.accentColor),
onTap: () async {
MyDialogContent().getDialog(
'be sure'.tr,
Text('attach correct audio'.tr),
() async {
await complaintController
.uploadAudioFile(audioFile);
},
);
},
);
}).toList(),
);
},
),
const SizedBox(height: 24),
// Trip Details Section
CupertinoFormSection(
header: Text('Trip Details'.tr),
children: [
CupertinoListTile(
title: complaintController.feedBack.isEmpty
? Text('No Ride found yet'.tr)
: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Date'.tr}: ${complaintController.feedBack[0]['date']}',
style: AppStyle.title),
Text(
'${'Price'.tr}: ${complaintController.feedBack[0]['price']}',
style: AppStyle.title),
],
),
),
],
),
const SizedBox(height: 24),
CupertinoFormSection(
header: Text('SEFER answer'.tr),
children: [
SizedBox(
height: 100,
child: ListView(
children: [
// Check if passengerReport is not null
if (complaintController.passengerReport !=
null)
// Access the 'solution' key safely
Text(
complaintController
.passengerReport!['solution']
?.toString() ??
'No solution available',
style: AppStyle.title,
)
else
const SizedBox(), // Fallback if passengerReport is null
],
),
),
],
),
const SizedBox(height: 24),
// Submit Button
CupertinoButton(
color: AppColor.blueColor,
padding: const EdgeInsets.symmetric(
vertical: 14, horizontal: 30),
onPressed: () async {
if (complaintController.formKey.currentState!
.validate()) {
if (complaintController.audioLink.toString() ==
'') {
MyDialogContent().getDialog(
'title',
Text(
'the audio file not uploaded yet \nDo you want to upload without audio file'
.tr), () async {
await complaintController.geminiAudio(
jsonEncode(complaintController.feedBack),
complaintController.audioLink,
complaintController
.complaintController.text);
complaintController.formKey.currentState!
.reset();
});
Get.back();
} else {
await complaintController.geminiAudio(
jsonEncode(complaintController.feedBack),
complaintController.audioLink,
complaintController
.complaintController.text);
complaintController.formKey.currentState!
.reset();
}
complaintController.addComplaint();
}
},
child: Text('Submit'.tr, style: AppStyle.title),
),
],
),
),
),
),
);
});
}
}

View File

@@ -12,8 +12,8 @@ class MyCircularProgressIndicator extends StatelessWidget {
Widget build(BuildContext context) {
return Center(
child: Container(
width: 110,
height: 110,
width: 140,
height: 140,
decoration: BoxDecoration(
color: backgroundColor,
shape: BoxShape.circle,
@@ -21,13 +21,11 @@ class MyCircularProgressIndicator extends StatelessWidget {
child: Stack(
children: [
const Center(child: CircularProgressIndicator()),
Column(
children: [
Align(
alignment: Alignment.center,
child: Image.asset('assets/images/logo.gif'),
),
],
Image.asset(
'assets/images/logo.gif',
width: 140,
height: 140,
fit: BoxFit.contain,
),
],
),