This commit is contained in:
Hamza-Ayed
2024-08-27 10:49:43 +03:00
parent 2bc71355c3
commit d23020188e
48 changed files with 1872 additions and 432 deletions

View File

@@ -35,44 +35,64 @@ class InviteController extends GetxController {
var data = jsonDecode(response);
driverInvitationData = data['message'];
update();
// print('driverInitationData: $driverInitationData');
}
} catch (e) {
print('Error fetching driver stats: $e');
} catch (e) {}
}
void selectPhone(String phone) {
if (box.read(BoxName.countryCode) == 'Egypt') {
invitePhoneController.text = phone;
update();
Get.back();
}
}
Future<void> pickContact() async {
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 {
print('Requesting contact permission...');
if (await FlutterContacts.requestPermission(readonly: true)) {
print('Permission granted. Opening external contact picker...');
final Contact? contact = await FlutterContacts.openExternalPick();
if (contact != null) {
print('Contact picked: ${contact.displayName}');
if (contact.phones.isNotEmpty) {
print('Phone number found: ${contact.phones.first.number}');
invitePhoneController.text = contact.phones.first.number;
update();
} else {
print('Selected contact has no phone number.');
Get.snackbar('No phone number'.tr,
'The selected contact does not have a phone number.'.tr);
}
} else {
print('No contact selected or picker was cancelled.');
Get.snackbar('No contact selected'.tr, 'Please select a contact'.tr);
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 {
print('Permission denied by user or system.');
Get.snackbar('Permission denied'.tr,
'Contact permission is required to pick a contact'.tr);
'Contact permission is required to pick contacts'.tr);
}
} catch (e) {
print('Error picking contact: $e');
print('Stack trace: ${StackTrace.current}');
Get.snackbar(
'Error'.tr, 'An error occurred while picking a contact: $e'.tr);
'Error'.tr, 'An error occurred while picking contacts: $e'.tr);
}
}
@@ -116,6 +136,33 @@ class InviteController extends GetxController {
);
}
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 sendInvite() async {
if (invitePhoneController.text.isEmpty) {
Get.snackbar('Error', 'Please enter an phone address'.tr);
@@ -123,11 +170,13 @@ class InviteController extends GetxController {
}
// try {
String phoneNumber = formatPhoneNumber(invitePhoneController.text);
var response = await CRUD().post(link: AppLink.addInviteDriver, payload: {
"driverId": box.read(BoxName.driverID),
"inviterDriverPhone": '+2${invitePhoneController.text}'
"inviterDriverPhone": phoneNumber
});
Log.print('response: ${response}');
if (response != 'failure') {
var d = jsonDecode(response);
Get.snackbar('Success', 'Invite sent successfully'.tr);
@@ -142,8 +191,7 @@ class InviteController extends GetxController {
'*Android:* https://play.google.com/store/apps/details?id=com.sefer_driver\n\n\n'
'*iOS:* https://apps.apple.com/ae/app/sefer-driver/id6502189302';
launchCommunication(
'whatsapp', '+2${invitePhoneController.text}', message);
launchCommunication('whatsapp', '+2$phoneNumber', message);
invitePhoneController.clear();
} else {
@@ -152,7 +200,6 @@ class InviteController extends GetxController {
duration: const Duration(seconds: 4));
}
// } catch (e) {
// print('Error sending invite: $e');
// Get.snackbar('Error', 'An error occurred'.tr);
// }
}

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/controller/functions/location_background_controller.dart';
import 'package:SEFER/print.dart';
import 'package:SEFER/views/auth/captin/cards/sms_signup.dart';
import 'package:SEFER/views/widgets/elevated_btn.dart';
import 'package:flutter/material.dart';
@@ -54,6 +55,7 @@ class LoginDriverController extends GetxController {
'email': email,
'id': driverID,
});
print(res);
if (res == 'failure') {
//Failure

View File

@@ -80,8 +80,8 @@ class RegisterCaptainController extends GetxController {
}
bool isValidEgyptianPhoneNumber(String phoneNumber) {
// Remove any whitespace from the phone number
phoneNumber = phoneNumber.replaceAll(RegExp(r'\s+'), '');
// Remove any non-digit characters (spaces, dashes, etc.)
phoneNumber = phoneNumber.replaceAll(RegExp(r'\D+'), '');
// Check if the phone number has exactly 11 digits
if (phoneNumber.length != 11) {
@@ -89,7 +89,7 @@ class RegisterCaptainController extends GetxController {
}
// Check if the phone number starts with 010, 011, 012, or 015
RegExp validPrefixes = RegExp(r'^01[0125]');
RegExp validPrefixes = RegExp(r'^01[0125]\d{8}$');
return validPrefixes.hasMatch(phoneNumber);
}

View File

@@ -86,18 +86,26 @@ class GoogleSignInHelper {
static Future<GoogleSignInAccount?> signInFromLogin() async {
try {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
if (googleUser != null) {
await _handleSignUp(googleUser);
await Get.find<LoginDriverController>().loginUsingCredentials(
box.read(BoxName.driverID).toString(),
box.read(BoxName.emailDriver).toString(),
);
// Add detailed debug print statements
final driverID = box.read(BoxName.driverID)?.toString() ?? 'Unknown ID';
final emailDriver =
box.read(BoxName.emailDriver)?.toString() ?? 'Unknown Email';
// print('Driver ID: $driverID');
// print('Email Driver: $emailDriver');
await Get.find<LoginDriverController>()
.loginUsingCredentials(driverID, emailDriver);
}
return googleUser;
} catch (error) {
Get.snackbar('Google Sign-In error', '$error',
backgroundColor: AppColor.redColor);
// Log error details
print('Google Sign-In error: $error');
return null;
}

View File

@@ -136,6 +136,8 @@ class FirebaseMessagesController extends GetxController {
// 'Cancel Trip'.tr, 'Passenger Cancel Trip'.tr, 'cancel', '');
// }
cancelTripDialog();
} else if (message.notification!.title == 'Cancel') {
cancelTripDialog1();
} else if (message.notification!.title! == 'token change') {
// NotificationController()
// .showNotification('token change'.tr, 'token change', 'cancel');
@@ -316,6 +318,21 @@ class FirebaseMessagesController extends GetxController {
}));
}
Future<dynamic> cancelTripDialog1() {
return Get.defaultDialog(
barrierDismissible: false,
title: 'Passenger Cancel Trip'.tr,
middleText:
'Trip Cancelled. The cost of the trip will be added to your wallet.'
.tr,
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () {
box.write(BoxName.rideStatus, 'Cancel');
Get.offAll(HomeCaptain());
}));
}
// Future<dynamic> driverArrivePassengerDialoge() {
// return Get.defaultDialog(
// barrierDismissible: false,

View File

@@ -1,14 +1,15 @@
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/colors.dart';
import 'package:SEFER/views/home/Captin/orderCaptin/order_request_page.dart';
import 'package:SEFER/views/home/my_wallet/walet_captain.dart';
import 'package:SEFER/views/home/Captin/orderCaptin/order_speed_request.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/notification/notification_captain.dart';
import '../home/captin/home_captain_controller.dart';
class NotificationController extends GetxController {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
@@ -31,10 +32,10 @@ class NotificationController extends GetxController {
// Create a notification channel
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'order_channel', // Channel ID
'Order Notifications', // Channel name
'dynamic_channel', // Channel ID
'Dynamic Notifications', // Channel name
description:
'This channel is used for order notifications.', // Channel description
'This channel is used for various types of notifications.', // Channel description
importance: Importance.max,
);
@@ -56,7 +57,7 @@ class NotificationController extends GetxController {
);
AndroidNotificationDetails android =
AndroidNotificationDetails('order_channel', 'Order Notifications',
AndroidNotificationDetails('dynamic_channel', 'Dynamic Notifications',
importance: Importance.max,
priority: Priority.high,
styleInformation: bigTextStyleInformation,
@@ -68,6 +69,7 @@ class NotificationController extends GetxController {
color: AppColor.primaryColor,
showProgress: true,
showWhen: true,
timeoutAfter: title == 'Order' ? 14500 : 6000,
subText: message,
actions: [
AndroidNotificationAction(
@@ -83,42 +85,93 @@ class NotificationController extends GetxController {
NotificationDetails details = NotificationDetails(android: android);
await _flutterLocalNotificationsPlugin.show(0, title, message, details,
payload: payLoad);
// payload: 'order_page_payload');
payload: jsonEncode({'title': title, 'data': payLoad}));
}
// Callback when the notification is tapped
void onDidReceiveNotificationResponse(NotificationResponse response) {
// jsonDecode(response.payload);
_handleNotificationResponse(response);
}
// Callback when the notification is tapped while the app is in the background
void onDidReceiveBackgroundNotificationResponse(
NotificationResponse response) {
_handleNotificationResponse(response);
}
// Handle notification response for both foreground and background
void _handleNotificationResponse(NotificationResponse response) {
print('Notification tapped!');
Log.print('response.payload: ${response.payload}');
if (response.payload != null) {
print('Notification payload: ${response.payload}');
// if (response.payload != 'order_page_payload') {
// Log.print('arguments: ${box.read(BoxName.rideArguments)}');
closeOverLay();
Get.to(() => OrderRequestPage(),
arguments: {'myListString': response.payload});
// }
var payloadData = jsonDecode(response.payload.toString());
if (payloadData is Map<String, dynamic>) {
String title = payloadData['title'];
var data = payloadData['data'];
switch (title) {
case 'Order':
_handleOrderNotification(data);
break;
case 'OrderSpeed':
_handleOrderSpeedNotification(data);
break;
case 'ADS':
_handleADSNotification();
break;
default:
Log.print('Unknown notification type');
}
} else {
Log.print('Invalid payload format');
}
} else {
Log.print('Payload is null');
}
}
void _handleOrderNotification(dynamic data) {
if (data is String) {
var orderData = jsonDecode(data);
if (orderData is List && orderData.length == 34) {
closeOverLay();
Get.put(HomeCaptainController()).changeRideId();
Get.to(() => OrderRequestPage(), arguments: {'myListString': data});
} else {
Log.print('Invalid order data');
}
} else {
Log.print('Invalid order payload');
}
}
void _handleOrderSpeedNotification(dynamic data) {
if (data is String) {
var orderData = jsonDecode(data);
if (orderData is List && orderData.length == 34) {
closeOverLay();
Get.put(HomeCaptainController()).changeRideId();
Get.to(() => OrderSpeedRequest(), arguments: {'myListString': data});
} else {
Log.print('Invalid order data');
}
} else {
Log.print('Invalid order payload');
}
}
void _handleADSNotification() {
// var orderData = jsonDecode(data);
closeOverLay();
Get.to(
() => const NotificationCaptain(),
);
}
void onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) async {
// display a dialog with the notification details, tap ok to go to another page
}
// Callback when the notification is tapped while the app is in the background
void onDidReceiveBackgroundNotificationResponse(
NotificationResponse response) {
print('Notification tapped while app is in background!');
if (response.payload != null) {
print('Notification payload: ${response.payload}');
if (response.payload == 'order') {
Get.to(() => OrderRequestPage(),
arguments: box.read(BoxName.rideArguments));
closeOverLay();
Log.print('arguments: ${box.read(BoxName.rideArguments)}');
}
}
}
}

View File

@@ -29,9 +29,9 @@ class CRUD {
},
);
// if (response.statusCode == 200) {
// Log.print('response: ${response.request}');
// Log.print('response: ${response.body}');
// Log.print('response: ${payload}');
Log.print('response: ${response.request}');
Log.print('response: ${response.body}');
Log.print('response: ${payload}');
var jsonData = jsonDecode(response.body);
if (jsonData['status'] == 'success') {
return response.body;
@@ -101,7 +101,9 @@ class CRUD {
var extractedString =
await arabicTextExtractByVisionAndAI(imagePath: imagePath);
var json = jsonDecode(extractedString);
var textValues = extractTextFromLines(json);
Log.print('textValues: ${textValues}');
// await Get.put(AI()).geminiAiExtraction(prompt, textValues);
await Get.put(AI()).anthropicAI(textValues, prompt, imagePath);
}
@@ -145,6 +147,8 @@ class CRUD {
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
// Log.print(
// 'response.stream.bytesToString(): ${await response.stream.bytesToString()}');
return await response.stream.bytesToString();
} else {}
}
@@ -227,6 +231,8 @@ class CRUD {
);
Log.print('response: ${response.request}');
Log.print('response: ${response.body}');
Log.print('response: ${response.statusCode}');
Log.print('response: ${response.reasonPhrase}');
Log.print('response: ${payload}');
var jsonData = jsonDecode(response.body);
if (response.statusCode == 200) {

View File

@@ -250,6 +250,39 @@ class AI extends GetxController {
}
}
String extractDOB(String nationalNumber) {
if (nationalNumber.length != 14) {
throw ArgumentError('National number must be 14 digits long.');
}
// Extract the first digit to determine the century
String firstDigit = nationalNumber[0];
// Extract year, month, and day parts
String yearPart = nationalNumber.substring(1, 3);
String monthPart = nationalNumber.substring(3, 5);
String dayPart = nationalNumber.substring(5, 7);
// Determine the year based on the first digit
int yearPrefix;
if (firstDigit == '2') {
yearPrefix = 1900;
} else if (firstDigit == '3') {
yearPrefix = 2000;
} else {
throw ArgumentError('Invalid first digit in national number.');
}
// Construct the full year
int year = yearPrefix + int.parse(yearPart);
// Format the date as YYYY-MM-DD
String dob =
'$year-${monthPart.padLeft(2, '0')}-${dayPart.padLeft(2, '0')}';
return dob;
}
Future<void> addDriverEgypt() async {
isLoading = true;
update();
@@ -295,7 +328,8 @@ class AI extends GetxController {
'religion':
responseIdEgyptBack['religion']?.toString() ?? 'Not specified',
'status': 'yet',
'birthdate': responseIdEgyptFront['dob']?.toString() ?? 'Not specified',
'birthdate': extractDOB(
responseIdEgyptDriverLicense['national_number'].toString()),
'maritalStatus':
responseIdEgyptBack['maritalStatus']?.toString() ?? 'Not specified',
'site': responseIdEgyptDriverLicense['address']?.toString() ??
@@ -321,6 +355,12 @@ class AI extends GetxController {
if (status1['status'] == 'success') {
isDriverSaved = true;
// CRUD().post(
// link: '${AppLink.seferGizaServer}/auth/captin/register.php',
// payload: payload);
// CRUD().post(
// link: '${AppLink.seferAlexandriaServer}/auth/captin/register.php',
// payload: payload);
Get.snackbar('Success', 'Driver data saved successfully',
backgroundColor: AppColor.greenColor);
} else {
@@ -376,6 +416,55 @@ class AI extends GetxController {
isCarSaved = true;
Get.snackbar('Success', 'message',
backgroundColor: AppColor.greenColor);
// CRUD().post(
// link:
// '${AppLink.seferAlexandriaServer}/ride/RegisrationCar/add.php',
// payload: {
// 'driverID': box.read(BoxName.driverID),
// 'vin': responseIdCardDriverEgyptBack['chassis'].toString(),
// 'car_plate':
// responseIdCardDriverEgyptFront['car_plate'].toString(),
// 'make': responseIdCardDriverEgyptBack['make'].toString(),
// 'model': responseIdCardDriverEgyptBack['model'],
// 'year': responseIdCardDriverEgyptBack['year'].toString(),
// 'expiration_date':
// responseIdCardDriverEgyptFront['LicenseExpirationDate']
// .toString(),
// 'color': responseIdCardDriverEgyptBack['color'],
// 'owner': responseIdCardDriverEgyptFront['owner'],
// 'color_hex':
// responseIdCardDriverEgyptBack['color_hex'].toString(),
// 'address': responseIdCardDriverEgyptFront['address'].toString(),
// 'displacement':
// responseIdCardDriverEgyptBack['engine'].toString(),
// 'fuel': responseIdCardDriverEgyptBack['fuel'].toString(),
// 'registration_date':
// '${responseIdCardDriverEgyptBack['inspection_date']}',
// });
// CRUD().post(
// link: '${AppLink.seferGizaServer}/ride/RegisrationCar/add.php',
// payload: {
// 'driverID': box.read(BoxName.driverID),
// 'vin': responseIdCardDriverEgyptBack['chassis'].toString(),
// 'car_plate':
// responseIdCardDriverEgyptFront['car_plate'].toString(),
// 'make': responseIdCardDriverEgyptBack['make'].toString(),
// 'model': responseIdCardDriverEgyptBack['model'],
// 'year': responseIdCardDriverEgyptBack['year'].toString(),
// 'expiration_date':
// responseIdCardDriverEgyptFront['LicenseExpirationDate']
// .toString(),
// 'color': responseIdCardDriverEgyptBack['color'],
// 'owner': responseIdCardDriverEgyptFront['owner'],
// 'color_hex':
// responseIdCardDriverEgyptBack['color_hex'].toString(),
// 'address': responseIdCardDriverEgyptFront['address'].toString(),
// 'displacement':
// responseIdCardDriverEgyptBack['engine'].toString(),
// 'fuel': responseIdCardDriverEgyptBack['fuel'].toString(),
// 'registration_date':
// '${responseIdCardDriverEgyptBack['inspection_date']}',
// });
}
} catch (e) {}
}

View File

@@ -26,9 +26,9 @@ class LocationBackgroundController extends GetxController {
Future<void> configureBackgroundLocation() async {
await BackgroundLocation.setAndroidNotification(
title: "Background Location",
message: "Tracking location...",
icon: "@mipmap/launcher_icon",
title: 'Location Tracking Active'.tr,
message: 'Your location is being tracked in the background.'.tr,
icon: '@mipmap/launcher_icon',
);
// Set the location update interval to 5 seconds

View File

@@ -10,6 +10,8 @@ import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/controller/home/payment/captain_wallet_controller.dart';
import 'package:SEFER/main.dart';
import '../../print.dart';
// LocationController.dart
class LocationController extends GetxController {
LocationData? _currentLocation;
@@ -40,6 +42,36 @@ class LocationController extends GetxController {
totalPoints = Get.put(CaptainWalletController()).totalPoints.toString();
// isActive = Get.put(HomeCaptainController()).isActive;
} // Function to determine which area the coordinates belong to
String getLocationArea(double latitude, double longitude) {
// Giza Boundary Check
if (latitude >= 29.904975 &&
latitude <= 30.143372 &&
longitude >= 30.787030 &&
longitude <= 31.238843) {
// box.write(BoxName.serverChosen, AppLink.seferGizaServer);
return 'Giza';
}
// Cairo Boundary Check
else if (latitude >= 29.918901 &&
latitude <= 30.198857 &&
longitude >= 31.215009 &&
longitude <= 31.532186) {
// box.write(BoxName.serverChosen, AppLink.seferCairoServer);
return 'Cairo';
}
// Alexandria Boundary Check
else if (latitude >= 30.396286 &&
latitude <= 31.654458 &&
longitude >= 29.041139 &&
longitude <= 32.626259) {
// box.write(BoxName.serverChosen, AppLink.seferAlexandriaServer);
return 'Alexandria';
}
// Return 'Unknown' if outside defined areas
return 'Unknown';
}
Future<void> startLocationUpdates() async {
@@ -53,53 +85,69 @@ class LocationController extends GetxController {
if (isActive) {
if (double.parse(totalPoints) > -3000) {
await getLocation();
String endpoint;
// if (box.read(BoxName.driverID) != null) {
await CRUD()
.post(link: AppLink.addCarsLocationByPassenger, payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'latitude': myLocation.latitude.toString(),
'longitude': myLocation.longitude.toString(),
'heading': heading.toString(),
'speed': (speed * 3.6).toStringAsFixed(1),
'distance': totalDistance == 0
? '0'
: totalDistance < 1
? totalDistance.toStringAsFixed(3)
: totalDistance.toStringAsFixed(1),
'status': box.read(BoxName.statusDriverLocation).toString()
});
// Animate camera to user location (optional)
// if (Get.find<HomeCaptainController>().rideId == 'rideId') {
// Get.find<MapDriverController>()
// .mapController!
// .animateCamera(CameraUpdate.newLatLng(LatLng(
// Get.find<LocationController>().myLocation.latitude,
// Get.find<LocationController>().myLocation.longitude,
// )));
switch (
getLocationArea(myLocation.latitude, myLocation.longitude)) {
case 'Cairo':
endpoint = AppLink.addCarsLocationCairoEndpoint;
break;
case 'Giza':
endpoint = AppLink.addCarsLocationGizaEndpoint;
break;
case 'Alexandria':
endpoint = AppLink.addCarsLocationAlexandriaEndpoint;
break;
default:
print('Location outside Cairo, Giza, or Alexandria');
endpoint = AppLink.addCarsLocationByPassenger;
return;
}
if (box.read(BoxName.driverID) != null) {
await CRUD().post(link: endpoint, payload: {
'driver_id': box.read(BoxName.driverID).toString(),
'latitude': myLocation.latitude.toString(),
'longitude': myLocation.longitude.toString(),
'heading': heading.toString(),
'speed': (speed * 3.6).toStringAsFixed(1),
'distance': totalDistance == 0
? '0.0'
: totalDistance < 1
? totalDistance.toStringAsFixed(3)
: totalDistance.toStringAsFixed(1),
'status': box.read(BoxName.statusDriverLocation).toString()
});
// Animate camera to user location (optional)
// if (Get.find<HomeCaptainController>().rideId == 'rideId') {
// Get.find<MapDriverController>()
// .mapController!
// .animateCamera(CameraUpdate.newLatLng(LatLng(
// Get.find<LocationController>().myLocation.latitude,
// Get.find<LocationController>().myLocation.longitude,
// )));
}
Get.find<HomeCaptainController>()
.mapHomeCaptainController!
.animateCamera(CameraUpdate.newLatLng(LatLng(
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
)));
// if (Get.find<HomeCaptainController>().rideId == '0') {
// await sql.insertData({
// 'driver_id': box.read(BoxName.driverID),
// 'latitude': myLocation.latitude.toString(),
// 'longitude': myLocation.longitude.toString(),
// 'created_at': DateTime.now().toString(),
// }, TableName.carLocations);
// } else {
// await sql.insertData({
// 'order_id': Get.find<MapDriverController>().rideId,
// 'created_at': DateTime.now().toString(),
// 'lat': myLocation.latitude.toString(),
// 'lng': myLocation.longitude.toString(),
// }, TableName.rideLocation);
// }
}
Get.find<HomeCaptainController>()
.mapHomeCaptainController!
.animateCamera(CameraUpdate.newLatLng(LatLng(
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
)));
// if (Get.find<HomeCaptainController>().rideId == '0') {
// await sql.insertData({
// 'driver_id': box.read(BoxName.driverID),
// 'latitude': myLocation.latitude.toString(),
// 'longitude': myLocation.longitude.toString(),
// 'created_at': DateTime.now().toString(),
// }, TableName.carLocations);
// } else {
// await sql.insertData({
// 'order_id': Get.find<MapDriverController>().rideId,
// 'created_at': DateTime.now().toString(),
// 'lat': myLocation.latitude.toString(),
// 'lng': myLocation.longitude.toString(),
// }, TableName.rideLocation);
// }
// }
//
}
@@ -151,10 +199,13 @@ class LocationController extends GetxController {
(_locationData.latitude != null && _locationData.longitude != null
? LatLng(_locationData.latitude!, _locationData.longitude!)
: null)!;
getLocationArea(_locationData.latitude!, _locationData.longitude!);
speed = _locationData.speed!;
heading = _locationData.heading!;
// Calculate the distance between the current location and the previous location
if (Get.find<HomeCaptainController>().rideId == 'rideId') {
Log.print(
'Get.find<HomeCaptainController>().rideId: ${Get.find<HomeCaptainController>().rideId}');
if (previousTime > 0) {
double distance = calculateDistanceInKmPerHour(
previousTime, _locationData.time, speed);

View File

@@ -64,6 +64,12 @@ void showUpdateDialog(BuildContext context) {
Navigator.of(context).pop();
},
),
CupertinoDialogAction(
child: Text('Cancel'.tr),
onPressed: () async {
Navigator.of(context).pop();
},
),
],
);
},

View File

@@ -14,6 +14,7 @@ import 'package:path_provider/path_provider.dart' as path_provider;
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../main.dart';
import '../../print.dart';
class ImageController extends GetxController {
File? myImage;
@@ -138,6 +139,7 @@ class ImageController extends GetxController {
File compressedImage = await compressImage(processedImage);
print('link =$link');
Log.print('link: ${link}');
await uploadImage(
compressedImage,
@@ -238,7 +240,7 @@ class ImageController extends GetxController {
uploadImage(File file, Map data, String link) async {
var request = http.MultipartRequest(
'POST',
Uri.parse(link), //'https://ride.mobile-app.store/uploadImage1.php'
Uri.parse(link),
);
var length = await file.length();
@@ -268,6 +270,7 @@ class ImageController extends GetxController {
var myrequest = await request.send();
var res = await http.Response.fromStream(myrequest);
if (res.statusCode == 200) {
Log.print('jsonDecode(res.body): ${jsonDecode(res.body)}');
return jsonDecode(res.body);
} else {
throw Exception(

View File

@@ -0,0 +1,15 @@
import 'package:get/get.dart';
import '../../constant/box_name.dart';
import '../../main.dart';
class HomePageController extends GetxController {
late bool isVibrate = box.read(BoxName.isvibrate) ?? true;
void changeVibrateOption(bool value) {
isVibrate = box.read(BoxName.isvibrate) ?? true;
isVibrate = value;
box.write(BoxName.isvibrate, value);
update();
}
}

View File

@@ -0,0 +1,78 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../functions/launch.dart';
class ContactUsController extends GetxController {
final String phone1 = '+201018805430';
final String phone2 = '+201080182934';
final TimeOfDay workStartTime = const TimeOfDay(hour: 12, minute: 0);
final TimeOfDay workEndTime = const TimeOfDay(hour: 19, minute: 0);
bool _isWithinWorkTime(TimeOfDay now) {
return (now.hour > workStartTime.hour ||
(now.hour == workStartTime.hour &&
now.minute >= workStartTime.minute)) &&
(now.hour < workEndTime.hour ||
(now.hour == workEndTime.hour && now.minute <= workEndTime.minute));
}
void showContactDialog(BuildContext context) {
TimeOfDay now = TimeOfDay.now();
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: Text('Contact Us'.tr),
message: Text('Choose a contact option'.tr),
actions: <Widget>[
if (_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(phone1),
onPressed: () => makePhoneCall(
phone1,
),
),
if (_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(phone2),
onPressed: () => makePhoneCall(phone2),
),
if (!_isWithinWorkTime(now))
CupertinoActionSheetAction(
child: Text(
'Work time is from 12:00 - 19:00.\nYou can send a WhatsApp message or email.'
.tr),
onPressed: () => Navigator.pop(context),
),
CupertinoActionSheetAction(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
const Icon(
FontAwesome.whatsapp,
color: AppColor.greenColor,
),
Text('Send WhatsApp Message'.tr),
],
),
onPressed: () =>
launchCommunication('whatsapp', phone1, 'Hello'.tr),
),
CupertinoActionSheetAction(
child: Text('Send Email'.tr),
onPressed: () =>
launchCommunication('email', 'support@sefer.live', 'Hello'.tr),
),
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () => Navigator.pop(context),
),
),
);
}
}

View File

@@ -0,0 +1,22 @@
import 'dart:convert';
import 'package:SEFER/constant/box_name.dart';
import 'package:SEFER/constant/links.dart';
import 'package:SEFER/controller/functions/crud.dart';
import 'package:SEFER/main.dart';
import 'package:get/get.dart';
class MaintainCenterController extends GetxController {
bool isLoading = false;
Map tripCount = {};
Future getTripCountByCaptain() async {
var res = await CRUD().get(link: AppLink.getTripCountByCaptain, payload: {
"driver_id": box.read(BoxName.driverID).toString(),
});
if (res != 'failure') {
tripCount = jsonDecode(res)['message'];
update();
}
}
}

View File

@@ -219,12 +219,12 @@ class HomeCaptainController extends GetxController {
void onInit() async {
// await locationBackController.requestLocationPermission();
await addToken();
addToken();
await getlocation();
onButtonSelected();
await getDriverRate();
await getKazanPercent();
await getPaymentToday();
getDriverRate();
getKazanPercent();
getPaymentToday();
getCountRideToday();
getAllPayment();
startPeriodicExecution();

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'package:SEFER/controller/home/captin/home_captain_controller.dart';
import 'package:SEFER/controller/home/captin/order_request_controller.dart';
import 'package:SEFER/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
@@ -15,6 +16,7 @@ import '../../../constant/api_key.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../constant/table_names.dart';
import '../../../main.dart';
import '../../../views/Rate/rate_passenger.dart';
import '../../../views/home/Captin/home_captain/home_captin.dart';
@@ -169,15 +171,15 @@ class MapDriverController extends GetxController {
cancelTripFromDriverAfterApplied() async {
if (formKeyCancel.currentState!.validate()) {
await CRUD().post(link: AppLink.updateRides, payload: {
CRUD().post(link: AppLink.updateRides, payload: {
"id": rideId.toString(), // Convert to String
"status": 'CancelFromDriverAfterApply'
});
await CRUD().post(link: AppLink.addCancelRideFromPassenger, payload: {
"rideID": rideId.toString(),
"driverID": box.read(BoxName.driverID).toString(),
"passengerID": passengerId.toString(),
"note": cancelTripCotroller.text.toString()
CRUD().post(link: AppLink.addCancelTripFromDriverAfterApplied, payload: {
"order_id": rideId.toString(),
"driver_id": box.read(BoxName.driverID).toString(),
"status": 'reject After Applied',
"notes": cancelTripCotroller.text.toString()
});
FirebaseMessagesController().sendNotificationToDriverMAP(
"Cancel Trip from driver".tr,
@@ -187,6 +189,12 @@ class MapDriverController extends GetxController {
[],
'cancel.wav',
);
sql.insertData({
'order_id': rideId,
'created_at': DateTime.now().toString(),
'driver_id': box.read(BoxName.driverID).toString(),
}, TableName.driverOrdersRefuse);
Get.put(OrderRequestController()).getRefusedOrderByCaptain();
Get.offAll(HomeCaptain());
}
}
@@ -407,15 +415,29 @@ class MapDriverController extends GetxController {
updateLocation();
}
calculateDistanceBetweenDriverAndPassengerLocation() {
calculateDistanceBetweenDriverAndPassengerLocation() async {
Get.put(LocationController());
double distance2 = Geolocator.distanceBetween(
latLngPassengerLocation.latitude,
latLngPassengerLocation.longitude,
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
return distance2;
var res = await CRUD().get(
link: AppLink.getLatestLocationPassenger, payload: {'rideId': rideId});
if (res != 'failure') {
var passengerLatestLocationString = jsonDecode(res)['message'];
double distance2 = Geolocator.distanceBetween(
double.parse(passengerLatestLocationString[0]['lat'].toString()),
double.parse(passengerLatestLocationString[0]['lng'].toString()),
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
return distance2;
} else {
double distance2 = Geolocator.distanceBetween(
latLngPassengerLocation.latitude,
latLngPassengerLocation.longitude,
Get.find<LocationController>().myLocation.latitude,
Get.find<LocationController>().myLocation.longitude,
);
return distance2;
}
}
addWaitingTimeCostFromPassengerToDriverWallet() async {

View File

@@ -186,14 +186,14 @@ class OrderRequestController extends GetxController {
});
// applied = true;
if (box.read(BoxName.gender).toString() != 'Female') {
sql.insertData({
'order_id': orderID,
'created_at': DateTime.now().toString(),
'driver_id': box.read(BoxName.driverID).toString(),
}, TableName.driverOrdersRefuse);
getRefusedOrderByCaptain();
}
// if (box.read(BoxName.gender).toString() != 'Female') {
// sql.insertData({
// 'order_id': orderID,
// 'created_at': DateTime.now().toString(),
// 'driver_id': box.read(BoxName.driverID).toString(),
// }, TableName.driverOrdersRefuse);
// getRefusedOrderByCaptain();
// }
update();
// Get.back();
// Get.offAll(HomeCaptain());

View File

@@ -111,7 +111,7 @@ class CaptainWalletController extends GetxController {
// isLoading = false;
if (res != 'failure') {
walletDate = jsonDecode(res);
totalAmount = walletDate['message'][0]['total_amount'].toString();
totalAmount = walletDate['message'][0]['total_amount'] ?? '0';
update();
var res1 = await CRUD().get(
link: AppLink.getAllPaymentVisa,

View File

@@ -286,6 +286,134 @@ class MyTranslation extends Translations {
"is reviewing your order. They may need more information or a higher price.":
"يتم مراجعة طلبك. قد يحتاجون إلى مزيد من المعلومات أو سعر أعلى.",
"FullName": "الاسم الكامل",
"Cancel Trip from driver": "إلغاء الرحلة من السائق",
"If you want order to another person": "إذا كنت تريد الطلب لشخص آخر",
"We will look for a new driver.\nPlease wait.":
"سنبحث عن سائق جديد.\nمن فضلك انتظر.",
"No, I want to cancel this trip": "لا، أريد إلغاء هذه الرحلة",
"Attention": "تنبيه",
'Trip Cancelled. The cost of the trip will be added to your wallet.':
"تم إلغاء الرحلة. سيتم إضافة تكلفة الرحلة إلى محفظتك.",
"Trip Cancelled. The cost of the trip will be deducted from your wallet.":
"تم إلغاء الرحلة. سيتم خصم تكلفة الرحلة من محفظتك.",
"You will be charged for the cost of the driver coming to your location.":
"سيتم خصم تكلفة قدوم السائق إلى موقعك.",
"reject your order.": "رفض طلبك.",
"Location Tracking Active": "تعقب الموقع نشط",
"Your location is being tracked in the background.":
"يتم تتبع موقعك في الخلفية.",
"Maintenance Center": "‏مركز الصيانة",
"Order Under Review": "الطلب قيد المراجعة",
"When you complete 600 trips, you will be eligible to receive offers for maintenance of your car.":
"عندما تكمل 600 رحلة، ستكون مؤهلاً للحصول على عروض لصيانة سيارتك.",
"is reviewing your order. They may need more information or a higher price.":
"يتم مراجعة طلبك. قد يحتاجون إلى مزيد من المعلومات أو سعر أعلى.",
"The driver canceled your ride.": "ألغى السائق رحلتك.",
"We haven't found any drivers yet. Consider increasing your trip fee to make your offer more attractive to drivers.":
"لم نجد أي سائقين بعد. ضع في اعتبارك زيادة رسوم رحلتك لجعل عرضك أكثر جاذبية للسائقين.",
"Allow Location Access": "السماح بالوصول إلى الموقع",
"Show My Trip Count": "عرض عدد رحلاتي",
"SEFER is the safest ride-sharing app that introduces many features for both captains and passengers. We offer the lowest commission rate of just 8%, ensuring you get the best value for your rides. Our app includes insurance for the best captains, regular maintenance of cars with top engineers, and on-road services to ensure a respectful and high-quality experience for all users.":
"سفر هو التطبيق الأكثر أمانًا لمشاركة الركوب الذي يقدم العديد من الميزات لكل من السائقين والركاب. نحن نقدم أقل عمولة بنسبة 8% فقط، مما يضمن حصولك على أفضل قيمة لرحلاتك. يتضمن تطبيقنا التأمين لأفضل السائقين، الصيانة المنتظمة للسيارات مع أفضل المهندسين، والخدمات على الطريق لضمان تجربة محترمة وعالية الجودة لجميع المستخدمين.",
"You can contact us during working hours from 12:00 - 19:00.":
"يمكنك الاتصال بنا خلال ساعات العمل من 12:00 - 7:00.",
"Show maintenance center near my location":
"أظهر مركز الصيانة بالقرب من موقعي",
"How do I request a ride?": "كيف أطلب رحلة؟",
"Step-by-step instructions on how to request a ride through the Sefer app.":
"تعليمات خطوة بخطوة حول كيفية طلب رحلة من خلال تطبيق Sefer.",
"What types of vehicles are available?":
"ما هي أنواع المركبات المتاحة؟",
"Sefer offers a variety of vehicle options to suit your needs, including economy, comfort, and luxury. Choose the option that best fits your budget and passenger count.":
"توفر Sefer مجموعة متنوعة من خيارات المركبات لتناسب احتياجاتك، بما في ذلك الاقتصادية والمريحة والفخمة. اختر الخيار الذي يناسب ميزانيتك وعدد الركاب.",
"How can I pay for my ride?": "كيف يمكنني الدفع لرحلتي؟",
"Sefer offers multiple payment methods for your convenience. Choose between cash payment or credit/debit card payment during ride confirmation.":
"توفر Sefer طرق دفع متعددة لراحتك. اختر بين الدفع نقدًا أو بطاقة ائتمان/خصم أثناء تأكيد الرحلة.",
"Can I cancel my ride?": "هل يمكنني إلغاء رحلتي؟",
"Yes, you can cancel your ride under certain conditions (e.g., before driver is assigned). See the Sefer cancellation policy for details.":
"نعم، يمكنك إلغاء رحلتك في ظل ظروف معينة (مثل قبل تعيين السائق). اطلع على سياسة الإلغاء في Sefer للحصول على التفاصيل.",
"Driver Registration & Requirements": "تسجيل السائقين والمتطلبات",
"How can I register as a driver?": "كيف يمكنني التسجيل كسائق؟",
"What are the requirements to become a driver?":
"ما هي المتطلبات للعمل كسائق؟",
'''Types of Trips in Sefer:
Comfort: For cars newer than 2017 with air conditioning.
Lady: For girl drivers.
Speed: For fixed salary and endpoints.
Mashwari: For flexible trips where passengers choose the car and driver with prior arrangements.
Raih Gai: For same-day return trips longer than 50km.
''': '''
أنواع الرحلات في سيفر:
كمفورت: للسيارات الأحدث من 2017 والمكيفة.
ليدي: للسائقات النساء.
سبيد: للرحلات ذات الرواتب الثابتة والنقاط النهائية المحددة.
مشواري: للرحلات المرنة حيث يختار الركاب السيارة والسائق مسبقًا.
رايح جاي: للرحلات ذهاب وعودة في نفس اليوم لمسافات تزيد عن 50 كم.
''',
"What is Types of Trips in Sefer?": "أنواع الرحلات في سفر",
'''Sefer Wallet Features:
Transfer money multiple times.
Transfer to anyone.
Make purchases.
Charge your account.
Charge a friend's Sefer account.
Store your money with us and receive it in your bank as a monthly salary.''':
'''
مميزات محفظة سفر:
تحويل الأموال عدة مرات.
تحويل لأي شخص.
الشراء.
شحن حسابك.
شحن حساب صديقك في سفر.
الاحتفاظ بأموالك معنا واستلامها في حسابك البنكي كراتب شهري.
''',
"No Rides Available": "لا توجد رحلات متاحة حالياً",
"What are the order details we provide to you?":
"ما هي تفاصيل الطلب التي نقدمها لك؟",
"What is the feature of our wallet?": "ما هي ميزة محفظتنا؟",
"How to use SEFER": "كيف تستخدم SEFER",
"Visit our website or contact Sefer support for information on driver registration and requirements.":
"تفضل بزيارة موقعنا الإلكتروني أو اتصل بدعم Sefer للحصول على معلومات حول تسجيل السائقين والمتطلبات.",
"How do I communicate with the other party (passenger/driver)?":
"كيف أتواصل مع الطرف الآخر (الراكب/السائق)؟",
"Sefer provides in-app chat functionality to allow you to communicate with your driver or passenger during your ride.":
"توفر Sefer ميزة الدردشة داخل التطبيق لتتيح لك التواصل مع سائقك أو راكبك أثناء الرحلة.",
"What safety measures does Sefer offer?":
"ما هي تدابير السلامة التي تقدمها Sefer؟",
"Sefer prioritizes your safety. We offer features like driver verification, in-app trip tracking, and emergency contact options.":
"تُولي Sefer أهمية كبيرة لسلامتك. نحن نقدم ميزات مثل التحقق من هوية السائق ، وتتبع الرحلات داخل التطبيق ، وخيارات الاتصال في حالات الطوارئ.",
'Frequently Questions': 'الأسئلة الشائعة',
"Contact Us": "اتصل بنا",
"You can change the vibration feedback for all buttons":
"يمكنك تغيير اهتزاز الرج لجميع الأزرار",
'About Us': "نبذة عنا",
"Most Secure Methods": "أساليب الأمان الأكثر فاعلية",
"In-App VOIP Calls": "مكالمات VOIP داخل التطبيق",
"Recorded Trips for Safety": "تسجيل الرحلات من أجل السلامة",
"\nWe also prioritize affordability, offering competitive pricing to make your rides accessible.":
"\nكما أننا نضع توفير التكاليف في أولوية اهتماماتنا، ونقدم أسعاراً منافسة لجعل رحلاتك في متناول اليد.",
'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:':
"SEFER: تطبيق مشاركة الرحلات يضع سلامتك وادخارك في المقدمة SEFER هو تطبيق مشاركة رحلات مصمم مع وضع سلامتك وتوفيرك في الاعتبار. نربطك بسائقين موثوقين في منطقتك، ونضمن لك تجربة سفر مريحة وخالية من الضغوط.فيما يلي بعض الميزات الرئيسية التي تميزنا:",
"Choose a contact option": "اختر خيار الاتصال",
"Work time is from 12:00 - 19:00.\nYou can send a WhatsApp message or email.":
"وقت العمل من 12:00 - 7:00.\nيمكنك إرسال رسالة واتساب أو بريد إلكتروني.",
"Send WhatsApp Message": "إرسال رسالة واتساب",
"Send Email": "إرسال بريد إلكتروني",
"You should complete 600 trips": "يجب عليك إكمال 600 رحلة",
"We have maintenance offers for your car. You can use them after completing 600 trips to get a 20% discount on car repairs. Enjoy using our SEFER app and be part of our SEFER family.":
"لدينا عروض صيانة لسيارتك. يمكنك استخدامها بعد إكمال 600 رحلة للحصول على خصم 20% على إصلاحات السيارة. استمتع باستخدام تطبيق SEFER وكن جزءًا من عائلتنا.",
"Enable Location Permission": "تمكين إذن الموقع",
'Allowing location access will help us display orders near you. Please enable it now.':
"سيساعدنا السماح بالوصول إلى الموقع في عرض الطلبات القريبة منك. يرجى تمكينه الآن.",

View File

@@ -19,7 +19,7 @@ class RideAvailableController extends GetxController {
update();
} else {
Get.defaultDialog(
title: 'No Rides now!'.tr,
title: "No Rides Available".tr,
middleText: '',
titleStyle: AppStyle.title,
confirm: MyElevatedButton(