new backend and more secure 29-04-2026

This commit is contained in:
Hamza-Ayed
2026-04-30 01:44:23 +03:00
parent 6bfc15abb2
commit b102af8f28
7 changed files with 13447 additions and 13571 deletions

1
.env
View File

@@ -58,6 +58,7 @@ sss_encryptionSalt=zg-vklie-2l1ZlpxiLJ6wQOvbb4TnC9XrxgUEyVQIu6TID4qP4FUUqoS5XrXl
addd=BlBlNl addd=BlBlNl
getLocationAreaLinks =https://api.tripz-egypt.com/tripz/ride/location/get_location_area_links.php getLocationAreaLinks =https://api.tripz-egypt.com/tripz/ride/location/get_location_area_links.php
allowed=mobile-app: allowed=mobile-app:
allowedWallet=TripzWallet:
passnpassenger=hbgbitbXrXrBr passnpassenger=hbgbitbXrXrBr
newId=new newId=new
a=q a=q

View File

@@ -9,6 +9,7 @@ class AK {
X.r(X.r(X.r(Env.stripePublishableKe, cn), cC), cs); X.r(X.r(X.r(Env.stripePublishableKe, cn), cC), cs);
static final String sss_pass = X.r(X.r(X.r(Env.sss_pass, cn), cC), cs); static final String sss_pass = X.r(X.r(X.r(Env.sss_pass, cn), cC), cs);
static final String allowed = Env.allowed; static final String allowed = Env.allowed;
static final String allowedWallet = Env.allowedWallet;
static final String passnpassenger = X static final String passnpassenger = X
.r(X.r(X.r(Env.passnpassenger, cn), cC), cs) .r(X.r(X.r(Env.passnpassenger, cn), cC), cs)
.toString() .toString()

View File

@@ -114,8 +114,11 @@ class LoginController extends GetxController {
if (response.statusCode == 200) { if (response.statusCode == 200) {
final decoded = jsonDecode(response.body); final decoded = jsonDecode(response.body);
final String? jwt = final String? jwt = decoded['data'] != null
decoded['data'] != null ? decoded['data']['jwt'] : (decoded['message'] != null ? decoded['message']['jwt'] : decoded['jwt']); ? decoded['data']['jwt']
: (decoded['message'] != null
? decoded['message']['jwt']
: decoded['jwt']);
if (jwt != null) { if (jwt != null) {
// نشفر الـ JWT بالتشفير الثلاثي قبل التخزين في GetStorage // نشفر الـ JWT بالتشفير الثلاثي قبل التخزين في GetStorage
@@ -144,8 +147,11 @@ class LoginController extends GetxController {
Log.print('response: ${response.body}'); Log.print('response: ${response.body}');
if (response.statusCode == 200) { if (response.statusCode == 200) {
final decoded = jsonDecode(response.body); final decoded = jsonDecode(response.body);
final String? jwt = final String? jwt = decoded['data'] != null
decoded['data'] != null ? decoded['data']['jwt'] : (decoded['message'] != null ? decoded['message']['jwt'] : decoded['jwt']); ? decoded['data']['jwt']
: (decoded['message'] != null
? decoded['message']['jwt']
: decoded['jwt']);
if (jwt != null) { if (jwt != null) {
box.write(BoxName.jwt, c(jwt)); box.write(BoxName.jwt, c(jwt));
@@ -216,13 +222,12 @@ class LoginController extends GetxController {
Future<String?> getJwtWallet() async { Future<String?> getJwtWallet() async {
dev = Platform.isAndroid ? 'android' : 'ios'; dev = Platform.isAndroid ? 'android' : 'ios';
// await DeviceHelper.initAndStore();
final String fp = box.read(BoxName.deviceFpEncrypted) ?? ''; final String fp = box.read(BoxName.deviceFpEncrypted) ?? '';
var payload = { var payload = {
'id': box.read(BoxName.passengerID), 'id': box.read(BoxName.passengerID),
'password': AK.passnpassenger, 'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev', 'aud': '${AK.allowedWallet}$dev',
'fingerPrint': fp, 'fingerPrint': fp,
}; };
@@ -230,23 +235,26 @@ class LoginController extends GetxController {
Uri.parse(AppLink.loginJwtWalletRider), Uri.parse(AppLink.loginJwtWalletRider),
body: payload, body: payload,
); );
Log.print('AppLink.loginJwtWalletRider: ${AppLink.loginJwtWalletRider}');
// Log.print('payload: ${payload}'); Log.print('AppLink.loginJwtWalletRider: ${AppLink.loginJwtWalletRider}');
Log.print('response wallet: ${response.body}'); Log.print('response wallet: ${response.body}');
if (response.statusCode == 200) { if (response.statusCode == 200) {
final decoded = jsonDecode(response.body); final decoded = jsonDecode(response.body);
final String? jwt =
decoded['data'] != null ? decoded['data']['jwt'] : decoded['jwt']; // ← الإصلاح: نقرأ من message أو data أو root
final String? hmac = final inner = decoded['data'] ?? decoded['message'] ?? decoded;
decoded['data'] != null ? decoded['data']['hmac'] : decoded['hmac'];
final String? jwt = inner['jwt'];
final String? hmac = inner['hmac'];
Log.print('jwt extracted: $jwt');
Log.print('hmac extracted: $hmac');
if (hmac != null) { if (hmac != null) {
// نخزن الـ hmac للاستخدام في X-HMAC-Auth header
box.write(BoxName.hmac, hmac); box.write(BoxName.hmac, hmac);
} }
// wallet JWT يُرجَع مباشرة دون تشفير ثلاثي
return jwt; return jwt;
} }

View File

@@ -54,7 +54,8 @@ class CRUD {
final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver); final phone = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
// طباعة الخطأ في الكونسول للمطور للمتابعة الفورية // طباعة الخطأ في الكونسول للمطور للمتابعة الفورية
Log.print("🚨 [ADD_ERROR] Where: $where | Error: $error | Details: $details"); Log.print(
"🚨 [ADD_ERROR] Where: $where | Error: $error | Details: $details");
// Fire-and-forget call to prevent infinite loops if the logger itself fails. // Fire-and-forget call to prevent infinite loops if the logger itself fails.
CRUD().post( CRUD().post(
@@ -114,6 +115,9 @@ class CRUD {
final body = response.body; final body = response.body;
Log.print('request: ${response.request}'); Log.print('request: ${response.request}');
Log.print('body: $body'); Log.print('body: $body');
// Log.print('link: $link');
Log.print('headers: $headers');
Log.print('payload: $payload');
// 2xx // 2xx
if (sc >= 200 && sc < 300) { if (sc >= 200 && sc < 300) {
@@ -219,7 +223,8 @@ class CRUD {
body: payload, body: payload,
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}', 'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]}',
'X-Device-FP': _getFpHeader(), 'X-Device-FP': _getFpHeader(),
}, },
); );
@@ -227,14 +232,18 @@ class CRUD {
if (retryResponse.statusCode == 200) { if (retryResponse.statusCode == 200) {
return retryResponse.body; return retryResponse.body;
} }
return jsonEncode({'status': 'failure', 'message': 'token_expired_retry_failed'}); return jsonEncode(
{'status': 'failure', 'message': 'token_expired_retry_failed'});
} else { } else {
return jsonEncode({'status': 'failure', 'message': '401_unauthorized'}); return jsonEncode({'status': 'failure', 'message': '401_unauthorized'});
} }
} else { } else {
addError('Non-200 response code: ${response.statusCode}', addError('Non-200 response code: ${response.statusCode}',
'crud().get - Other', url.toString()); 'crud().get - Other', url.toString());
return jsonEncode({'status': 'failure', 'message': 'server_error_${response.statusCode}'}); return jsonEncode({
'status': 'failure',
'message': 'server_error_${response.statusCode}'
});
} }
} }
@@ -257,6 +266,10 @@ class CRUD {
'X-HMAC-Auth': hmac.toString(), 'X-HMAC-Auth': hmac.toString(),
'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز 'X-Device-FP': _getFpHeader(), // ← إثبات الجهاز
}; };
// add print debug
Log.print('headers: $headers');
Log.print('payload: $payload');
Log.print('link: $link');
return await _makeRequest( return await _makeRequest(
link: link, link: link,
@@ -711,7 +724,8 @@ class CRUD {
if (response.statusCode == 200 || response.statusCode == 201) { if (response.statusCode == 200 || response.statusCode == 201) {
return jsonDecode(response.body); return jsonDecode(response.body);
} }
Log.print('MapSaas Post Error: ${response.statusCode} - ${response.body}'); Log.print(
'MapSaas Post Error: ${response.statusCode} - ${response.body}');
return null; return null;
} catch (e) { } catch (e) {
Log.print('MapSaas Post Exception: $e'); Log.print('MapSaas Post Exception: $e');

View File

@@ -210,8 +210,7 @@ class MapPassengerController extends GetxController {
bool rideConfirm = false; bool rideConfirm = false;
bool isMarkersShown = false; bool isMarkersShown = false;
bool isMainBottomMenuMap = true; bool isMainBottomMenuMap = true;
Timer? markerReloadingTimer2 = Timer(Duration.zero, () {});
Timer? markerReloadingTimer1 = Timer(Duration.zero, () {});
int durationToPassenger = 0; int durationToPassenger = 0;
bool isWayPointSheet = false; bool isWayPointSheet = false;
bool isWayPointStopsSheet = false; bool isWayPointStopsSheet = false;
@@ -228,7 +227,7 @@ class MapPassengerController extends GetxController {
var dataCarsLocationByPassenger; var dataCarsLocationByPassenger;
var datadriverCarsLocationToPassengerAfterApplied; var datadriverCarsLocationToPassengerAfterApplied;
CarLocation? nearestCar; CarLocation? nearestCar;
late Timer? markerReloadingTimer = Timer(Duration.zero, () {});
bool shouldFetch = true; // Flag to determine if fetch should be executed bool shouldFetch = true; // Flag to determine if fetch should be executed
int selectedPassengerCount = 1; int selectedPassengerCount = 1;
double progress = 0; double progress = 0;
@@ -335,12 +334,17 @@ class MapPassengerController extends GetxController {
// لتخزين نقاط مسار السائق الحالية للمقارنة // لتخزين نقاط مسار السائق الحالية للمقارنة
List<LatLng> _currentDriverRoutePoints = []; List<LatLng> _currentDriverRoutePoints = [];
// متغير لتتبع مصدر القبول — Socket أم غيره
String _rideAcceptedViaSource = "Unknown";
// عدّاد تحديثات الموقع المستلمة من السوكيت (لقياس الصحة)
int _socketLocationUpdatesCount = 0;
final Map<RideState, int> _pollingIntervals = { final Map<RideState, int> _pollingIntervals = {
RideState.noRide: 6, RideState.noRide: 6,
RideState.searching: 5, RideState.searching: 8,
RideState.driverApplied: 10, RideState.driverApplied: 10,
RideState.driverArrived: 8, RideState.driverArrived: 15,
RideState.inProgress: 6, RideState.inProgress: 15,
RideState.cancelled: 3600, RideState.cancelled: 3600,
RideState.finished: 3600, RideState.finished: 3600,
RideState.preCheckReview: 3600, RideState.preCheckReview: 3600,
@@ -485,6 +489,49 @@ class MapPassengerController extends GetxController {
currentRideState.value == RideState.inProgress; currentRideState.value == RideState.inProgress;
} }
/// فحص سريع: هل السوكيت يعمل ويرسل بيانات؟
bool _isSocketHealthy() {
if (!isSocketConnected) return false;
if (_lastSocketLocationTime == null) return false;
final diff = DateTime.now().difference(_lastSocketLocationTime!).inSeconds;
return diff < 20; // إذا آخر تحديث قبل أقل من 20 ثانية
}
/// 🧠 خوارزمية ذكية: حساب أقصر مسافة بين موقع السائق والـ Polyline (بدون API)
double _calculateDistanceToPolyline(LatLng point, List<LatLng> polyline) {
if (polyline.isEmpty) return 999.0;
double minDistance = double.infinity;
for (int i = 0; i < polyline.length - 1; i++) {
double d = _distToSegment(point, polyline[i], polyline[i + 1]);
if (d < minDistance) minDistance = d;
}
return minDistance;
}
double _distToSegment(LatLng p, LatLng v, LatLng w) {
double l2 = _dist2(v, w);
if (l2 == 0) return _distanceBetween(p, v);
double t = ((p.latitude - v.latitude) * (w.latitude - v.latitude) +
(p.longitude - v.longitude) * (w.longitude - v.longitude)) /
l2;
t = max(0, min(1, t));
return _distanceBetween(
p,
LatLng(v.latitude + t * (w.latitude - v.latitude),
v.longitude + t * (w.longitude - v.longitude)));
}
double _dist2(LatLng v, LatLng w) {
return pow(v.latitude - w.latitude, 2).toDouble() +
pow(v.longitude - w.longitude, 2).toDouble();
}
double _distanceBetween(LatLng p1, LatLng p2) {
return Geolocator.distanceBetween(
p1.latitude, p1.longitude, p2.latitude, p2.longitude);
}
// ============================================================================== // ==============================================================================
// 2. العقل المدبر: توجيه الحالات // 2. العقل المدبر: توجيه الحالات
// ============================================================================== // ==============================================================================
@@ -841,7 +888,7 @@ class MapPassengerController extends GetxController {
title: "We apologize 😔".tr, title: "We apologize 😔".tr,
middleText: "No drivers found at the moment.\nPlease try again later.".tr, middleText: "No drivers found at the moment.\nPlease try again later.".tr,
confirm: ElevatedButton( confirm: ElevatedButton(
onPressed: () => Get.back(), onPressed: () => Navigator.pop(Get.context!),
child: Text("Ok".tr), child: Text("Ok".tr),
), ),
); );
@@ -976,6 +1023,11 @@ class MapPassengerController extends GetxController {
driverArrivePassengerDialoge(); driverArrivePassengerDialoge();
startTimerDriverWaitPassenger5Minute(); startTimerDriverWaitPassenger5Minute();
// 4. إزالة مسار السائق واستعادة مسار الراكب للوجهة
if (polylineCoordinates.isNotEmpty) {
_playRouteAnimation(polylineCoordinates, lastComputedBounds);
}
update(); update();
} }
@@ -1083,6 +1135,13 @@ class MapPassengerController extends GetxController {
if (!isSocketConnected || data == null) return; if (!isSocketConnected || data == null) return;
// 🔥 1. تسجيل وقت استلام البيانات (تغذية الـ Watchdog) // 🔥 1. تسجيل وقت استلام البيانات (تغذية الـ Watchdog)
_lastSocketLocationTime = DateTime.now(); _lastSocketLocationTime = DateTime.now();
_socketLocationUpdatesCount++;
// 🧠 إذا وصلتنا 3 تحديثات ناجحة من السوكيت، أوقف البولينج (إن كان يعمل)
if (_socketLocationUpdatesCount >= 3 && _locationPollingTimer != null) {
Log.print("✅ Socket delivering locations reliably. Stopping polling.");
_stopDriverLocationPolling();
}
try { try {
double lat = double.tryParse(data['latitude']?.toString() ?? '0') ?? 0; double lat = double.tryParse(data['latitude']?.toString() ?? '0') ?? 0;
double lng = double.tryParse(data['longitude']?.toString() ?? '0') ?? 0; double lng = double.tryParse(data['longitude']?.toString() ?? '0') ?? 0;
@@ -1101,6 +1160,19 @@ class MapPassengerController extends GetxController {
currentLocationOfDrivers = newPos; currentLocationOfDrivers = newPos;
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// 🔥 2. Smart Rerouting Logic ( deviation > 50m )
// ------------------------------------------------------------------
if (_currentDriverRoutePoints.isNotEmpty) {
double deviation =
_calculateDistanceToPolyline(newPos, _currentDriverRoutePoints);
if (deviation > 50.0) {
Log.print(
"⚠️ Driver deviated by ${deviation.toStringAsFixed(1)}m. Smart Rerouting triggered.");
// إعادة رسم المسار محلياً (لا يتم استدعاؤه إلا عند الانحراف الحقيقي)
calculateDriverToPassengerRoute(newPos, passengerLocation);
}
}
// 🔥 تحديث الكاميرا: ضمان بقاء السيارة في منتصف الخريطة // 🔥 تحديث الكاميرا: ضمان بقاء السيارة في منتصف الخريطة
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// ملاحظة: تأكد من أن mapController قد تم تهيئته (initialized) // ملاحظة: تأكد من أن mapController قد تم تهيئته (initialized)
@@ -1277,6 +1349,9 @@ class MapPassengerController extends GetxController {
return; return;
} }
_rideAcceptedViaSource = source;
_socketLocationUpdatesCount = 0;
_isAcceptanceProcessed = true; // قفل الباب _isAcceptanceProcessed = true; // قفل الباب
Log.print("🚀 Winner: $source triggered acceptance! Processing..."); Log.print("🚀 Winner: $source triggered acceptance! Processing...");
@@ -1321,7 +1396,7 @@ class MapPassengerController extends GetxController {
// 5. 🔥 العمليات الجغرافية (المسار والوقت) 🔥 // 5. 🔥 العمليات الجغرافية (المسار والوقت) 🔥
// أ) جلب موقع السائق الأولي // أ) جلب موقع السائق الأولي
// await getDriverCarsLocationToPassengerAfterApplied();// stop this to use socket update await getDriverCarsLocationToPassengerAfterApplied();
_startSocketWatchdog(); _startSocketWatchdog();
// ب) رسم المسار وحساب الوقت // ب) رسم المسار وحساب الوقت
if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) { if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
@@ -1336,63 +1411,63 @@ class MapPassengerController extends GetxController {
final int timeToPassengerSeconds = final int timeToPassengerSeconds =
timeToPassengerFromDriverAfterApplied; // مثلاً من السيرفر timeToPassengerFromDriverAfterApplied; // مثلاً من السيرفر
final double distanceDriverToPassengerMeters = final double distanceDriverToPassengerMeters =
double.parse(distanceByPassenger); double.tryParse(distanceByPassenger.toString()) ?? 0.0;
// [PiP] تم تعطيل الإشعار المستمر القديم (Foreground Service) واستبداله بـ PiP
// await RideTrackingNative.updateRideTracking(
// driverName: driverName,
// driverPhone: driverPhone,
// carDetails: '$make • $carColor • $licensePlate',
// driverLat: driverCarsLocationToPassengerAfterApplied.last.latitude,
// driverLng: driverCarsLocationToPassengerAfterApplied.last.longitude,
// passengerLat: passengerLocation.latitude,
// passengerLng: passengerLocation.longitude,
// destLat: myDestination.latitude,
// destLng: myDestination.longitude,
// rideState: 'waiting',
// estimatedTimeMinutes: (timeToPassengerSeconds / 60).round(),
// totalDistanceMeters: distanceDriverToPassengerMeters,
// );
// [PiP] تفعيل PiP عند بدء الرحلة (سيدخل وضع النافذة العائمة عند خروج المستخدم) // [PiP] تفعيل PiP عند بدء الرحلة (سيدخل وضع النافذة العائمة عند خروج المستخدم)
PipService.enablePip(); PipService.enablePip();
// 6. بدء تتبع الموقع الدوري (Polling Backup + Smart Rerouting) // 6. 🔥 القرار الذكي: هل نبدأ البولينج أم نعتمد على السوكيت؟ 🔥
// سيبدأ العمل بعد 6 ثواني if (source == "Socket" && isSocketConnected) {
// ✅ السوكيت هو من أخبرنا بالقبول — نثق به ولا نشغل البولينج
Log.print(
"🧠 Smart Mode: Socket accepted ride. Skipping polling, relying on WebSocket.");
// الـ watchdog وحده يكفي كشبكة أمان
} else {
// ⚠️ القبول من FCM أو Polling — نشغل البولينج كالمعتاد
Log.print("🔄 Fallback Mode: $source accepted ride. Starting polling.");
_startDriverLocationPollingWithTimer(); _startDriverLocationPollingWithTimer();
} }
}
Timer? _watchdogTimer; Timer? _watchdogTimer;
void _startSocketWatchdog() { void _startSocketWatchdog() {
_watchdogTimer?.cancel(); _watchdogTimer?.cancel();
Log.print("👀 Starting Socket Watchdog (Hybrid Mode)..."); Log.print("👀 Starting Socket Watchdog (Hybrid Mode)...");
// نفحص كل 5 ثواني
_watchdogTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { _watchdogTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
// شروط إيقاف الحارس (إذا انتهت الرحلة)
if (currentRideState.value != RideState.driverApplied && if (currentRideState.value != RideState.driverApplied &&
currentRideState.value != RideState.driverArrived &&
currentRideState.value != RideState.inProgress) { currentRideState.value != RideState.inProgress) {
timer.cancel(); timer.cancel();
return; return;
} }
// 1. حساب الزمن المنقضي منذ آخر تحديث سوكيت
final lastTime = _lastSocketLocationTime ?? final lastTime = _lastSocketLocationTime ??
DateTime.now().subtract(const Duration(minutes: 1)); DateTime.now().subtract(const Duration(minutes: 1));
final difference = DateTime.now().difference(lastTime).inSeconds; final difference = DateTime.now().difference(lastTime).inSeconds;
// 2. القرار
if (difference < 15 && isSocketConnected) { if (difference < 15 && isSocketConnected) {
// ✅ الوضع ممتاز: وصلنا تحديث في آخر 10 ثواني والسوكيت متصل // ✅ السوكيت صحي — تأكد أن البولينج متوقف إذا كنا في وضع السوكيت
// لا تفعل شيئاً (وفر السيرفر والبطارية) if (_locationPollingTimer != null &&
// Log.print("✅ Socket is healthy. Skipping API poll."); _rideAcceptedViaSource == "Socket") {
} else { Log.print("✅ Socket recovered. Stopping polling fallback.");
// ⚠️ الوضع حرج: السوكيت معلق أو مفصول لأكثر من 10 ثواني _stopDriverLocationPolling();
Log.print("⚠️ Socket silent for ${difference}s. Forcing API Poll..."); }
} else if (difference >= 15 && difference < 30) {
// استدعاء دالة البولينغ (لمرة واحدة) // ⚠️ تأخر متوسط — جلب واحد فقط
Log.print("⚠️ Socket silent for ${difference}s. Single API Poll...");
await getDriverCarsLocationToPassengerAfterApplied(); await getDriverCarsLocationToPassengerAfterApplied();
} else if (difference >= 30) {
// 🔴 السوكيت ميت — تفعيل البولينج الكامل كـ fallback
if (_locationPollingTimer == null) {
Log.print(
"🔴 Socket dead for ${difference}s. Activating polling fallback!");
_startDriverLocationPollingWithTimer();
} else {
// البولينج يعمل بالفعل، فقط أكمل
await getDriverCarsLocationToPassengerAfterApplied();
}
} }
}); });
} }
@@ -1441,9 +1516,7 @@ class MapPassengerController extends GetxController {
_uiCountdownTimer?.cancel(); _uiCountdownTimer?.cancel();
// 3. إيقاف مؤقتات الخريطة // 3. إيقاف مؤقتات الخريطة
markerReloadingTimer?.cancel();
markerReloadingTimer1?.cancel();
markerReloadingTimer2?.cancel();
_animationTimers.forEach((key, timer) => timer.cancel()); _animationTimers.forEach((key, timer) => timer.cancel());
_animationTimers.clear(); _animationTimers.clear();
@@ -1518,8 +1591,10 @@ class MapPassengerController extends GetxController {
break; break;
case RideState.searching: case RideState.searching:
// 1. التحقق من حالة الطلب (هل قبله أحد؟) عبر البولينج فقط إذا كان السوكيت غير متصل // Guard: Don't poll if ride is not registered yet
if (!isSocketConnected) { if (rideId == 'yet' || rideId.isEmpty) break;
// 1. التحقق من حالة الطلب (هل قبله أحد؟) عبر البولينج كشبكة أمان
try { try {
String statusFromServer = await getRideStatus(rideId); String statusFromServer = await getRideStatus(rideId);
if (statusFromServer == 'Apply' || statusFromServer == 'Applied') { if (statusFromServer == 'Apply' || statusFromServer == 'Applied') {
@@ -1529,7 +1604,6 @@ class MapPassengerController extends GetxController {
} catch (e) { } catch (e) {
Log.print('Error polling getRideStatus: $e'); Log.print('Error polling getRideStatus: $e');
} }
}
final now = DateTime.now(); final now = DateTime.now();
final int elapsedSeconds = now.difference(_searchStartTime!).inSeconds; final int elapsedSeconds = now.difference(_searchStartTime!).inSeconds;
@@ -1604,7 +1678,10 @@ class MapPassengerController extends GetxController {
Log.print('Error polling for Arrived/Begin status: $e'); Log.print('Error polling for Arrived/Begin status: $e');
} }
} }
// 🧠 جلب الموقع فقط إذا السوكيت غير صحي
if (!_isSocketHealthy()) {
getDriverCarsLocationToPassengerAfterApplied(); getDriverCarsLocationToPassengerAfterApplied();
}
break; break;
case RideState.driverArrived: case RideState.driverArrived:
@@ -1653,7 +1730,10 @@ class MapPassengerController extends GetxController {
_isRideBeginLogicExecuted = true; _isRideBeginLogicExecuted = true;
_executeBeginRideLogic(); _executeBeginRideLogic();
} }
// 🧠 جلب الموقع فقط إذا السوكيت غير صحي
if (!_isSocketHealthy()) {
getDriverCarsLocationToPassengerAfterApplied(); getDriverCarsLocationToPassengerAfterApplied();
}
break; break;
case RideState.finished: case RideState.finished:
tripFinishedFromDriver(); tripFinishedFromDriver();
@@ -1975,40 +2055,6 @@ class MapPassengerController extends GetxController {
_stopWaitPassengerTimer(); _stopWaitPassengerTimer();
// 1) بيانات السائق والرحلة // 1) بيانات السائق والرحلة
final driverName = this.driverName ?? 'السائق';
final driverPhone = this.driverPhone ?? '';
final carBrand = this.make ?? '';
final carColor = this.carColor ?? '';
final carPlate = this.licensePlate ?? '';
final carDetails = '$carBrand$carColor$carPlate';
final driverLat = driverCarsLocationToPassengerAfterApplied.last.latitude;
final driverLng = driverCarsLocationToPassengerAfterApplied.last.longitude;
final passengerLat = passengerLocation.latitude;
final passengerLng = passengerLocation.longitude;
final destLat = myDestination.latitude ?? 0.0;
final destLng = myDestination.longitude ?? 0.0;
final int timeToDestinationSeconds =
durationToRide; // موجود عندك من التايمر
final double totalDistanceMeters = double.parse(distanceByPassenger);
// [PiP] تم تعطيل الإشعار المستمر القديم (Foreground Service) واستبداله بـ PiP
// await RideTrackingNative.updateRideTracking(
// driverName: driverName,
// driverPhone: driverPhone,
// carDetails: carDetails,
// driverLat: driverLat,
// driverLng: driverLng,
// passengerLat: passengerLat,
// passengerLng: passengerLng,
// destLat: destLat,
// destLng: destLng,
// rideState: 'inProgress',
// estimatedTimeMinutes: (timeToDestinationSeconds / 60).round(),
// totalDistanceMeters: totalDistanceMeters,
// );
// 3) بدء التايمر الداخلي الخاص بك (للـ ETA داخل التطبيق نفسه)
rideIsBeginPassengerTimer(); rideIsBeginPassengerTimer();
update(); update();
} }
@@ -2095,28 +2141,6 @@ class MapPassengerController extends GetxController {
update(); update();
} }
void convertHintTextStartNewPlaces(int index) {
if (placesStart.isEmpty) {
hintTextStartPoint = 'Search for your Start point'.tr;
update();
} else {
var res = placesStart[index];
hintTextStartPoint = res['displayName']?['text'] ??
res['formattedAddress'] ??
'Unknown Place';
double? lat = res['location']?['latitude'];
double? lng = res['location']?['longitude'];
if (lat != null && lng != null) {
newStartPointLocation = LatLng(lat, lng);
}
update();
}
}
void convertHintTextPlaces(int index, var res) { void convertHintTextPlaces(int index, var res) {
if (placeListResponseAll[index].isEmpty) { if (placeListResponseAll[index].isEmpty) {
placeListResponseAll[index] = res; placeListResponseAll[index] = res;
@@ -2137,62 +2161,6 @@ class MapPassengerController extends GetxController {
} }
} }
void convertHintTextPlaces1(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint1 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint1 = wayPoint1[index]['name'];
currentLocationString1 = wayPoint1[index]['name'];
double lat = wayPoint1[index]['geometry']['location']['lat'];
double lng = wayPoint1[index]['geometry']['location']['lng'];
newPointLocation1 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces2(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint2 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint2 = wayPoint2[index]['name'];
currentLocationString2 = wayPoint1[index]['name'];
double lat = wayPoint2[index]['geometry']['location']['lat'];
double lng = wayPoint2[index]['geometry']['location']['lng'];
newPointLocation2 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces3(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint3 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint3 = wayPoint3[index]['name'];
currentLocationString3 = wayPoint1[index]['name'];
double lat = wayPoint3[index]['geometry']['location']['lat'];
double lng = wayPoint3[index]['geometry']['location']['lng'];
newPointLocation3 = LatLng(lat, lng);
update();
}
}
void convertHintTextPlaces4(int index) {
if (wayPoint1.isEmpty) {
hintTextwayPoint4 = 'Search for your Start point'.tr;
update();
} else {
hintTextwayPoint4 = wayPoint4[index]['name'];
currentLocationString4 = wayPoint1[index]['name'];
double lat = wayPoint4[index]['geometry']['location']['lat'];
double lng = wayPoint4[index]['geometry']['location']['lng'];
newPointLocation4 = LatLng(lat, lng);
update();
}
}
void convertHintTextDestinationNewPlaces(int index) { void convertHintTextDestinationNewPlaces(int index) {
if (placesDestination.isEmpty) { if (placesDestination.isEmpty) {
hintTextDestinationPoint = 'Search for your destination'.tr; hintTextDestinationPoint = 'Search for your destination'.tr;
@@ -3004,30 +2972,6 @@ class MapPassengerController extends GetxController {
update(); update();
} }
void clearPlaces1() {
wayPoint1 = [];
hintTextwayPoint1 = 'Search for waypoint'.tr;
update();
}
void clearPlaces2() {
wayPoint2 = [];
hintTextwayPoint2 = 'Search for waypoint'.tr;
update();
}
void clearPlaces3() {
wayPoint3 = [];
hintTextwayPoint3 = 'Search for waypoint'.tr;
update();
}
void clearPlaces4() {
wayPoint4 = [];
hintTextwayPoint4 = 'Search for waypoint'.tr;
update();
}
int selectedReason = -1; int selectedReason = -1;
String? cancelNote; String? cancelNote;
void selectReason0(int index, String note) { void selectReason0(int index, String note) {
@@ -3675,118 +3619,6 @@ class MapPassengerController extends GetxController {
late LatLng currentDriverLocation; late LatLng currentDriverLocation;
late double headingList; late double headingList;
// Future getCarsLocationByPassengerAndReloadMarker() async {
// if (statusRide == 'wait') {
// carsLocationByPassenger = [];
// LatLngBounds bounds = calculateBounds(
// passengerLocation.latitude, passengerLocation.longitude, 7000);
// var res;
// if (box.read(BoxName.carType) == 'Lady') {
// res = await CRUD()
// .get(link: AppLink.getFemalDriverLocationByPassenger, payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// });
// } else if (box.read(BoxName.carType) == 'Speed') {
// res = await CRUD().get(
// link: AppLink.getCarsLocationByPassengerSpeed,
// payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// },
// );
// } else if (box.read(BoxName.carType) == 'Delivery') {
// res = await CRUD().get(
// link: AppLink.getCarsLocationByPassengerDelivery,
// payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// },
// );
// } else {
// res = await CRUD()
// .get(link: AppLink.getCarsLocationByPassenger, payload: {
// 'southwestLat': bounds.southwest.latitude.toString(),
// 'southwestLon': bounds.southwest.longitude.toString(),
// 'northeastLat': bounds.northeast.latitude.toString(),
// 'northeastLon': bounds.northeast.longitude.toString(),
// });
// }
// if (res == 'failure') {
// noCarString = true;
// dataCarsLocationByPassenger = res;
// update();
// } else {
// // Get.snackbar('no car', 'message');
// noCarString = false;
// dataCarsLocationByPassenger = jsonDecode(res);
// // if (dataCarsLocationByPassenger.length > carsOrder) {
// driverId = dataCarsLocationByPassenger['message'][carsOrder]
// ['driver_id']
// .toString();
// gender = dataCarsLocationByPassenger['message'][carsOrder]['gender']
// .toString();
// // }
// carsLocationByPassenger.clear(); // Clear existing markers
// // late LatLng lastDriverLocation; // Initialize a variable for last location
// for (var i = 0;
// i < dataCarsLocationByPassenger['message'].length;
// i++) {
// var json = dataCarsLocationByPassenger['message'][i];
// // CarLocationModel model = CarLocationModel.fromJson(json);
// if (carLocationsModels.length < i + 1) {
// // carLocationsModels.add(model);
// markers.add(
// Marker(
// markerId: MarkerId(json['latitude']),
// position: LatLng(
// double.parse(json['latitude']),
// double.parse(json['longitude']),
// ),
// rotation: double.parse(json['heading']),
// icon: json['model'].toString().contains('دراجة')
// ? motoIcon
// : json['gender'] == 'Male'.tr
// ? carIcon
// : ladyIcon,
// ),
// );
// driversToken.add(json['token']);
// // driversToken = json['token'];
// } else {
// // carLocationsModels[i] = model;
// markers[i] = Marker(
// markerId: MarkerId(json['latitude']),
// position: LatLng(
// double.parse(json['latitude']),
// double.parse(json['longitude']),
// ),
// rotation: double.parse(json['heading']),
// icon: json['model'].contains('دراجة')
// ? motoIcon
// : json['gender'] == 'Male'.tr
// ? carIcon
// : ladyIcon,
// );
// // driversToken = json['token'];
// driversToken.add(json['token']);
// }
// }
// }
// update();
// }
// }
Map<String, Timer> _animationTimers = {}; Map<String, Timer> _animationTimers = {};
final int updateIntervalMs = 100; // Update every 100ms final int updateIntervalMs = 100; // Update every 100ms
final double minMovementThreshold = final double minMovementThreshold =
@@ -3808,14 +3640,6 @@ class MapPassengerController extends GetxController {
// After 4 attempts, stop the search // After 4 attempts, stop the search
t.cancel(); t.cancel();
// No cars found after 4 attempts
// MyDialog().getDialog(
// "No Car or Driver Found in your area.".tr,
// "No Car or Driver Found in your area.".tr,
// () {
// Get.back();
// },
// );
if (!foundCars) { if (!foundCars) {
noCarString = true; noCarString = true;
dataCarsLocationByPassenger = 'failure'; dataCarsLocationByPassenger = 'failure';
@@ -3876,43 +3700,6 @@ class MapPassengerController extends GetxController {
}); });
} }
// String getLocationArea(double latitude, double longitude) {
// final locations = box.read(BoxName.locationName) ?? [];
// for (final location in locations) {
// final locationData = location as Map<String, dynamic>;
// // Debugging: Print location data
// // Log.print('Location Data: $locationData');
// // Convert string values to double
// final minLatitude =
// double.tryParse(locationData['min_latitude'].toString()) ?? 0.0;
// final maxLatitude =
// double.tryParse(locationData['max_latitude'].toString()) ?? 0.0;
// final minLongitude =
// double.tryParse(locationData['min_longitude'].toString()) ?? 0.0;
// final maxLongitude =
// double.tryParse(locationData['max_longitude'].toString()) ?? 0.0;
// // Debugging: Print converted values
// Log.print(
// 'Converted Values: minLatitude=$minLatitude, maxLatitude=$maxLatitude, minLongitude=$minLongitude, maxLongitude=$maxLongitude');
// if (latitude >= minLatitude &&
// latitude <= maxLatitude &&
// longitude >= minLongitude &&
// longitude <= maxLongitude) {
// box.write(BoxName.serverChosen, (locationData['server_link']));
// // Log.print(
// // 'locationData----server_link: ${(locationData['server_link'])}');
// return locationData['name'];
// }
// }
// // Default case
// box.write(BoxName.serverChosen, AppLink.IntaleqSyriaServer);
// return 'Cairo';
// }
String getLocationArea(double latitude, double longitude) { String getLocationArea(double latitude, double longitude) {
LatLng passengerPoint = LatLng(latitude, longitude); LatLng passengerPoint = LatLng(latitude, longitude);
@@ -4762,9 +4549,7 @@ Intaleq Team''';
"--- MapPassengerController: Closing and cleaning up all resources. ---"); "--- MapPassengerController: Closing and cleaning up all resources. ---");
// 1. إلغاء المؤقتات الفردية (باستخدام ?. الآمن) // 1. إلغاء المؤقتات الفردية (باستخدام ?. الآمن)
markerReloadingTimer?.cancel();
markerReloadingTimer1?.cancel();
markerReloadingTimer2?.cancel();
timerToPassengerFromDriverAfterApplied?.cancel(); timerToPassengerFromDriverAfterApplied?.cancel();
_timer?.cancel(); _timer?.cancel();
_masterTimer?.cancel(); // (أضف المؤقت الرئيسي) _masterTimer?.cancel(); // (أضف المؤقت الرئيسي)
@@ -6039,55 +5824,73 @@ Intaleq Team''';
if (context == null) return; if (context == null) return;
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// إغلاق أي سناك بار مفتوح حالياً لتجنب التكرار // Close any existing open dialogs first
ScaffoldMessenger.of(context).hideCurrentSnackBar(); if (Get.isDialogOpen == true) {
Get.back();
}
ScaffoldMessenger.of(context).showSnackBar( Get.dialog(
SnackBar( Dialog(
content: Row( backgroundColor: Colors.transparent,
elevation: 0,
child: Container(
padding: const EdgeInsets.all(24),
width: 180,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.95),
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 20,
spreadRadius: 5,
)
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// App Logo
Image.asset(
'assets/images/logo.gif',
height: 64,
errorBuilder: (context, error, stackTrace) => const Icon(
Icons.map,
size: 64,
color: AppColor.primaryColor,
),
),
const SizedBox(height: 16),
const SizedBox( const SizedBox(
width: 24, width: 24,
height: 24, height: 24,
child: MyCircularProgressIndicator(), child: MyCircularProgressIndicator(),
), ),
const SizedBox(width: 16), const SizedBox(height: 16),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text( Text(
'Drawing route on map...'.tr, 'Drawing route on map...'.tr,
style: const TextStyle( style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
color: AppColor.primaryColor,
), ),
), textAlign: TextAlign.center,
const SizedBox(height: 4),
Text(
'Please wait while we prepare your trip.'.tr,
style: const TextStyle(
color: Colors.white70,
fontSize: 12,
),
), ),
], ],
), ),
), ),
],
),
backgroundColor: AppColor.primaryColor.withOpacity(0.95),
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
margin: const EdgeInsets.fromLTRB(16, 0, 16, 110),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
elevation: 8,
), ),
barrierDismissible: false,
); );
// Auto-dismiss after exactly 2 seconds
Future.delayed(const Duration(seconds: 2), () {
if (Get.isDialogOpen == true) {
Get.back();
}
});
}); });
} }
@@ -7007,9 +6810,23 @@ Intaleq Team''';
// --- ⬇️ الإضافة الجديدة: التحقق من سياق حدود المطار ⬇️ --- // --- ⬇️ الإضافة الجديدة: التحقق من سياق حدود المطار ⬇️ ---
// !! ⚠️ تأكد من أن هذه هي المتغيرات الصحيحة لإحداثيات نقطة النهاية !! // !! ⚠️ تأكد من أن هذه هي المتغيرات الصحيحة لإحداثيات نقطة النهاية !!
double destLat = 0.0;
double destLng = 0.0;
try {
destLat = myDestination.latitude;
destLng = myDestination.longitude;
} catch (_) {
if (coordinatesWithoutEmpty.isNotEmpty) {
destLat =
double.tryParse(coordinatesWithoutEmpty.last.split(',')[0]) ?? 0.0;
destLng =
double.tryParse(coordinatesWithoutEmpty.last.split(',')[1]) ?? 0.0;
}
}
final bool damascusAirportBoundCtx = _isInsideDamascusAirportBounds( final bool damascusAirportBoundCtx = _isInsideDamascusAirportBounds(
myDestination.latitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح destLat,
myDestination.longitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح destLng,
); );
final bool isInDamascusAirportBoundCtx = _isInsideDamascusAirportBounds( final bool isInDamascusAirportBoundCtx = _isInsideDamascusAirportBounds(
newMyLocation.latitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح newMyLocation.latitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح
@@ -7494,10 +7311,6 @@ Intaleq Team''';
} }
late List recentPlaces = []; late List recentPlaces = [];
getFavioratePlaces0() async {
recentPlaces = await sql.getCustomQuery(
'SELECT DISTINCT latitude, longitude, name, rate FROM ${TableName.recentLocations}');
}
getFavioratePlaces() async { getFavioratePlaces() async {
recentPlaces = await sql.getCustomQuery( recentPlaces = await sql.getCustomQuery(

3
lib/env/env.dart vendored
View File

@@ -32,6 +32,9 @@ abstract class Env {
@EnviedField(varName: 'allowed', obfuscate: true) @EnviedField(varName: 'allowed', obfuscate: true)
static final String allowed = _Env.allowed; static final String allowed = _Env.allowed;
@EnviedField(varName: 'allowedWallet', obfuscate: true)
static final String allowedWallet = _Env.allowedWallet;
@EnviedField(varName: 'apiKeyHere', obfuscate: true) @EnviedField(varName: 'apiKeyHere', obfuscate: true)
static final String apiKeyHere = _Env.apiKeyHere; static final String apiKeyHere = _Env.apiKeyHere;

26332
lib/env/env.g.dart vendored

File diff suppressed because it is too large Load Diff