new backend and more secure 29-04-2026
This commit is contained in:
1
.env
1
.env
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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
3
lib/env/env.dart
vendored
@@ -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
26332
lib/env/env.g.dart
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user