Initial push to my private server
This commit is contained in:
@@ -426,9 +426,14 @@ class HomeCaptainController extends GetxController {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getAllPaymentFromRide,
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()});
|
||||
data = jsonDecode(res);
|
||||
if (res == 'failure') {
|
||||
totalMoneyInSEFER = '0';
|
||||
} else {
|
||||
data = jsonDecode(res);
|
||||
|
||||
totalMoneyInSEFER = data['message'][0]['total_amount'];
|
||||
}
|
||||
|
||||
totalMoneyInSEFER = data['message'][0]['total_amount'] ?? '0';
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
@@ -331,13 +331,7 @@ class MapDriverController extends GetxController {
|
||||
'driverGoToPassengerTime': DateTime.now().toString(),
|
||||
'status': 'Applied'
|
||||
});
|
||||
if (AppLink.endPoint != AppLink.seferCairoServer) {
|
||||
CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
|
||||
'id': (rideId),
|
||||
'driverGoToPassengerTime': DateTime.now().toString(),
|
||||
'status': 'Applied'
|
||||
});
|
||||
}
|
||||
|
||||
// Get.find<HomeCaptainController>().changeToAppliedRide('Applied');
|
||||
|
||||
Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
|
||||
112
lib/controller/home/captin/navigation_service.dart
Normal file
112
lib/controller/home/captin/navigation_service.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
|
||||
import 'package:sefer_driver/constant/api_key.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/functions/crud.dart';
|
||||
import 'package:sefer_driver/controller/functions/tts.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
|
||||
/// Handles map-related logic: fetching routes, drawing polylines, and managing markers.
|
||||
class NavigationService extends GetxService {
|
||||
final CRUD _crud = CRUD();
|
||||
final TextToSpeechController _tts = Get.put(TextToSpeechController());
|
||||
|
||||
final RxSet<Marker> markers = <Marker>{}.obs;
|
||||
final RxSet<Polyline> polylines = <Polyline>{}.obs;
|
||||
final RxString currentInstruction = "".obs;
|
||||
|
||||
BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
|
||||
BitmapDescriptor passengerIcon = BitmapDescriptor.defaultMarker;
|
||||
BitmapDescriptor startIcon = BitmapDescriptor.defaultMarker;
|
||||
BitmapDescriptor endIcon = BitmapDescriptor.defaultMarker;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_loadCustomIcons();
|
||||
}
|
||||
|
||||
void _loadCustomIcons() async {
|
||||
carIcon = await _createBitmapDescriptor('assets/images/car.png');
|
||||
passengerIcon = await _createBitmapDescriptor('assets/images/picker.png');
|
||||
startIcon = await _createBitmapDescriptor('assets/images/A.png');
|
||||
endIcon = await _createBitmapDescriptor('assets/images/b.png');
|
||||
}
|
||||
|
||||
Future<BitmapDescriptor> _createBitmapDescriptor(String assetName) {
|
||||
return BitmapDescriptor.fromAssetImage(
|
||||
ImageConfiguration(
|
||||
size: const Size(30, 35), devicePixelRatio: Get.pixelRatio),
|
||||
assetName,
|
||||
);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getRoute({
|
||||
required LatLng origin,
|
||||
required LatLng destination,
|
||||
}) async {
|
||||
final url =
|
||||
'${AppLink.googleMapsLink}directions/json?language=${box.read(BoxName.lang)}&destination=${destination.latitude},${destination.longitude}&origin=${origin.latitude},${origin.longitude}&key=${AK.mapAPIKEY}';
|
||||
|
||||
final response = await _crud.getGoogleApi(link: url, payload: {});
|
||||
|
||||
if (response != null && response['routes'].isNotEmpty) {
|
||||
return response['routes'][0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void drawRoute(Map<String, dynamic> routeData, {Color color = Colors.blue}) {
|
||||
final pointsString = routeData["overview_polyline"]["points"];
|
||||
final points = decodePolyline(pointsString)
|
||||
.map((p) => LatLng(p[0].toDouble(), p[1].toDouble()))
|
||||
.toList();
|
||||
|
||||
final polyline = Polyline(
|
||||
polylineId: PolylineId(routeData["summary"] ?? DateTime.now().toString()),
|
||||
points: points,
|
||||
width: 8,
|
||||
color: color,
|
||||
);
|
||||
|
||||
polylines.add(polyline);
|
||||
}
|
||||
|
||||
void updateCarMarker(LatLng position, double heading) {
|
||||
markers.removeWhere((m) => m.markerId.value == 'MyLocation');
|
||||
markers.add(
|
||||
Marker(
|
||||
markerId: MarkerId('MyLocation'.tr),
|
||||
position: position,
|
||||
icon: carIcon,
|
||||
rotation: heading,
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void setInitialMarkers(
|
||||
LatLng passengerLocation, LatLng passengerDestination) {
|
||||
markers.clear();
|
||||
markers.add(Marker(
|
||||
markerId: const MarkerId('passengerLocation'),
|
||||
position: passengerLocation,
|
||||
icon: passengerIcon,
|
||||
));
|
||||
markers.add(Marker(
|
||||
markerId: const MarkerId('passengerDestination'),
|
||||
position: passengerDestination,
|
||||
icon: endIcon,
|
||||
));
|
||||
}
|
||||
|
||||
void clearRoutes() {
|
||||
polylines.clear();
|
||||
currentInstruction.value = "";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -10,6 +11,7 @@ import 'package:sefer_driver/constant/colors.dart';
|
||||
// استخدام نفس مسارات الاستيراد التي قدمتها
|
||||
import '../../../constant/api_key.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../functions/crud.dart';
|
||||
import '../../functions/tts.dart';
|
||||
|
||||
@@ -525,45 +527,127 @@ class NavigationController extends GetxController {
|
||||
String _parseInstruction(String html) =>
|
||||
html.replaceAll(RegExp(r'<[^>]*>'), ' ');
|
||||
|
||||
double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
|
||||
const R = 6371.0; // km
|
||||
final dLat = (lat2 - lat1) * math.pi / 180.0;
|
||||
final dLon = (lon2 - lon1) * math.pi / 180.0;
|
||||
final a = math.sin(dLat / 2) * math.sin(dLat / 2) +
|
||||
math.cos(lat1 * math.pi / 180.0) *
|
||||
math.cos(lat2 * math.pi / 180.0) *
|
||||
math.sin(dLon / 2) *
|
||||
math.sin(dLon / 2);
|
||||
final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
|
||||
return R * c;
|
||||
}
|
||||
|
||||
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
|
||||
double _kmToLatDelta(double km) => km / 111.0;
|
||||
|
||||
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض)
|
||||
double _kmToLngDelta(double km, double atLat) =>
|
||||
km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9);
|
||||
|
||||
/// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة)
|
||||
double _relevanceScore(String name, String query) {
|
||||
final n = name.toLowerCase();
|
||||
final parts =
|
||||
query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2);
|
||||
double s = 0.0;
|
||||
for (final p in parts) {
|
||||
if (n.startsWith(p)) {
|
||||
s += 2.0;
|
||||
} else if (n.contains(p)) {
|
||||
s += 1.0;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Future<void> getPlaces() async {
|
||||
if (placeDestinationController.text.trim().isEmpty) {
|
||||
final q = placeDestinationController.text.trim();
|
||||
if (q.isEmpty) {
|
||||
placesDestination = [];
|
||||
update();
|
||||
return;
|
||||
}
|
||||
if (myLocation == null) {
|
||||
Get.snackbar('انتظر', 'جاري تحديد موقعك الحالي...');
|
||||
return;
|
||||
}
|
||||
final query = placeDestinationController.text.trim();
|
||||
|
||||
final lat = myLocation!.latitude;
|
||||
final lng = myLocation!.longitude;
|
||||
const double range = 2.2;
|
||||
final lat_min = lat - range,
|
||||
lat_max = lat + range,
|
||||
lng_min = lng - range,
|
||||
lng_max = lng + range;
|
||||
|
||||
// نصف قطر البحث بالكيلومتر (عدّل حسب رغبتك)
|
||||
const radiusKm = 200.0;
|
||||
|
||||
// حساب الباوند الصحيح (درجات، وليس 2.2 درجة ثابتة)
|
||||
final latDelta = _kmToLatDelta(radiusKm);
|
||||
final lngDelta = _kmToLngDelta(radiusKm, lat);
|
||||
|
||||
final latMin = lat - latDelta;
|
||||
final latMax = lat + latDelta;
|
||||
final lngMin = lng - lngDelta;
|
||||
final lngMax = lng + lngDelta;
|
||||
|
||||
try {
|
||||
final response = await CRUD().post(
|
||||
link: AppLink.getPlacesSyria,
|
||||
payload: {
|
||||
'query': query,
|
||||
'lat_min': lat_min.toString(),
|
||||
'lat_max': lat_max.toString(),
|
||||
'lng_min': lng_min.toString(),
|
||||
'lng_max': lng_max.toString(),
|
||||
'query': q,
|
||||
'lat_min': latMin.toString(),
|
||||
'lat_max': latMax.toString(),
|
||||
'lng_min': lngMin.toString(),
|
||||
'lng_max': lngMax.toString(),
|
||||
},
|
||||
);
|
||||
if (response != 'failure') {
|
||||
placesDestination = response['message'] ?? [];
|
||||
|
||||
// يدعم شكلي استجابة: إما {"...","message":[...]} أو قائمة مباشرة [...]
|
||||
List list;
|
||||
if (response is Map && response['message'] is List) {
|
||||
list = List.from(response['message'] as List);
|
||||
} else if (response is List) {
|
||||
list = List.from(response);
|
||||
} else {
|
||||
placesDestination = [];
|
||||
print('Unexpected response shape');
|
||||
return;
|
||||
}
|
||||
|
||||
// جهّز الحقول المحتملة للأسماء
|
||||
String _bestName(Map p) {
|
||||
return (p['name'] ?? p['name_ar'] ?? p['name_en'] ?? '').toString();
|
||||
}
|
||||
|
||||
// احسب المسافة ودرجة التطابق والنقاط
|
||||
for (final p in list) {
|
||||
final plat = double.tryParse(p['latitude']?.toString() ?? '') ?? 0.0;
|
||||
final plng = double.tryParse(p['longitude']?.toString() ?? '') ?? 0.0;
|
||||
|
||||
final d = _haversineKm(lat, lng, plat, plng);
|
||||
final rel = _relevanceScore(_bestName(p), q);
|
||||
|
||||
// معادلة ترتيب ذكية: مسافة أقل + تطابق أعلى = نقاط أعلى
|
||||
// تضيف +1 لضمان عدم وصول الوزن للصفر عند عدم وجود تطابق
|
||||
final score = (1.0 / (1.0 + d)) * (1.0 + rel);
|
||||
|
||||
p['distanceKm'] = d;
|
||||
p['relevance'] = rel;
|
||||
p['score'] = score;
|
||||
}
|
||||
|
||||
// رتّب حسب score تنازليًا، ثم المسافة تصاعديًا كحسم
|
||||
list.sort((a, b) {
|
||||
final sa = (a['score'] ?? 0.0) as double;
|
||||
final sb = (b['score'] ?? 0.0) as double;
|
||||
final cmp = sb.compareTo(sa);
|
||||
if (cmp != 0) return cmp;
|
||||
final da = (a['distanceKm'] ?? 1e9) as double;
|
||||
final db = (b['distanceKm'] ?? 1e9) as double;
|
||||
return da.compareTo(db);
|
||||
});
|
||||
|
||||
// خذ أول 10–15 للعرض (اختياري)، أو اعرض الكل
|
||||
placesDestination = list.take(15).toList();
|
||||
Log.print('placesDestination: $placesDestination');
|
||||
update();
|
||||
} catch (e) {
|
||||
print('Exception in getPlaces: $e');
|
||||
} finally {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/controller/payment/smsPaymnet/payment_services.dart';
|
||||
import 'package:sefer_driver/main.dart';
|
||||
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -27,48 +28,48 @@ class PaymobPayout extends GetxController {
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
if (didAuthenticate) {
|
||||
var dec = await CRUD()
|
||||
.postWallet(link: AppLink.paymobPayoutDriverWallet, payload: {
|
||||
"issuer": issuer,
|
||||
"method": "wallet",
|
||||
"amount": amount, //9.0,
|
||||
"full_name":
|
||||
'${box.read(BoxName.nameDriver)} ${box.read(BoxName.lastNameDriver)}',
|
||||
"msisdn": msisdn, //"01010101010",
|
||||
"bank_transaction_type": "cash_transfer"
|
||||
});
|
||||
if (dec['disbursement_status'] == 'successful') {
|
||||
var paymentToken = await Get.find<CaptainWalletController>()
|
||||
.generateToken(
|
||||
((-1) * (double.parse(dec['amount'].toString())) - payOutFee)
|
||||
.toStringAsFixed(0));
|
||||
await CRUD().postWallet(link: AppLink.addDrivePayment, payload: {
|
||||
'rideId': DateTime.now().toIso8601String(),
|
||||
'amount':
|
||||
((-1) * (double.parse(dec['amount'].toString())) - payOutFee)
|
||||
.toStringAsFixed(0),
|
||||
'payment_method': 'payout',
|
||||
'passengerID': 'myself',
|
||||
'token': paymentToken,
|
||||
'driverID': box.read(BoxName.driverID).toString(),
|
||||
});
|
||||
await Get.find<CaptainWalletController>()
|
||||
.addSeferWallet('payout fee myself', payOutFee.toString());
|
||||
await updatePaymentToPaid(box.read(BoxName.driverID).toString());
|
||||
await sendEmail(
|
||||
box.read(BoxName.driverID).toString(),
|
||||
amount,
|
||||
box.read(BoxName.phoneDriver).toString(),
|
||||
box.read(BoxName.nameDriver).toString(),
|
||||
'Wallet',
|
||||
box.read(BoxName.emailDriver).toString());
|
||||
// var dec = await CRUD()
|
||||
// .postWallet(link: AppLink.paymobPayoutDriverWallet, payload: {
|
||||
// "issuer": issuer,
|
||||
// "method": "wallet",
|
||||
// "amount": amount, //9.0,
|
||||
// "full_name":
|
||||
// '${box.read(BoxName.nameDriver)} ${box.read(BoxName.lastNameDriver)}',
|
||||
// "msisdn": msisdn, //"01010101010",
|
||||
// "bank_transaction_type": "cash_transfer"
|
||||
// });
|
||||
// if (dec['disbursement_status'] == 'successful') {
|
||||
// var paymentToken = await Get.find<CaptainWalletController>()
|
||||
// .generateToken(
|
||||
// ((-1) * (double.parse(dec['amount'].toString())) - payOutFee)
|
||||
// .toStringAsFixed(0));
|
||||
// await CRUD().postWallet(link: AppLink.addDrivePayment, payload: {
|
||||
// 'rideId': DateTime.now().toIso8601String(),
|
||||
// 'amount':
|
||||
// ((-1) * (double.parse(dec['amount'].toString())) - payOutFee)
|
||||
// .toStringAsFixed(0),
|
||||
// 'payment_method': 'payout',
|
||||
// 'passengerID': 'myself',
|
||||
// 'token': paymentToken,
|
||||
// 'driverID': box.read(BoxName.driverID).toString(),
|
||||
// });
|
||||
// await Get.find<CaptainWalletController>()
|
||||
// .addSeferWallet('payout fee myself', payOutFee.toString());
|
||||
// await updatePaymentToPaid(box.read(BoxName.driverID).toString());
|
||||
// await sendEmail(
|
||||
// box.read(BoxName.driverID).toString(),
|
||||
// amount,
|
||||
// box.read(BoxName.phoneDriver).toString(),
|
||||
// box.read(BoxName.nameDriver).toString(),
|
||||
// 'Wallet',
|
||||
// box.read(BoxName.emailDriver).toString());
|
||||
|
||||
mySnackbarSuccess('${'Transaction successful'.tr} ${dec['amount']}');
|
||||
// mySnackbarSuccess('${'Transaction successful'.tr} ${dec['amount']}');
|
||||
|
||||
Get.find<CaptainWalletController>().refreshCaptainWallet();
|
||||
} else if (dec['disbursement_status'] == 'failed') {
|
||||
mySnackeBarError('Transaction failed'.tr);
|
||||
}
|
||||
// Get.find<CaptainWalletController>().refreshCaptainWallet();
|
||||
// } else if (dec['disbursement_status'] == 'failed') {
|
||||
// mySnackeBarError('Transaction failed'.tr);
|
||||
// }
|
||||
} else {
|
||||
MyDialog().getDialog('Authentication failed'.tr, ''.tr, () {
|
||||
Get.back();
|
||||
|
||||
@@ -63,7 +63,7 @@ class SplashScreenController extends GetxController
|
||||
box.read(BoxName.onBoarding) == null
|
||||
? Get.off(() => OnBoardingPage())
|
||||
: box.read(BoxName.phoneDriver) != null &&
|
||||
box.read(BoxName.phoneVerified) == '1'
|
||||
box.read(BoxName.phoneVerified).toString() == '1'
|
||||
? await Get.put(LoginDriverController())
|
||||
.loginWithGoogleCredential(
|
||||
box.read(BoxName.driverID).toString(),
|
||||
|
||||
Reference in New Issue
Block a user