Fix payment server: remove global conRide to prevent crashing non-ride DB queries
This commit is contained in:
@@ -187,7 +187,13 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
});
|
||||
socket!.disconnect();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// أي تغيّر آخر (متاح ⇄ مشغول) يجب أن يصل السيرفر فوراً: هو الحدث الذي
|
||||
// ينقل الكابتن بين geo:drivers:available و geo:drivers:busy. وبلا هذا
|
||||
// ننتظر أول حدث GPS — وفلتر المسافة قد يؤجّله دقائق إن كان واقفاً.
|
||||
_emitCurrentLocationNow();
|
||||
});
|
||||
|
||||
bool deps = await _awaitDependencies();
|
||||
@@ -349,11 +355,11 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
if (sid != null || eid != null) {
|
||||
isSocketConnected = true;
|
||||
|
||||
if (myLocation.latitude != 0) {
|
||||
print("🚀 Sending initial location to socket after connect...");
|
||||
emitLocationToSocket(myLocation, heading, speed);
|
||||
}
|
||||
|
||||
// هذا الـ emit هو ما يُسجّل الكابتن في geo:drivers:available بعد كل
|
||||
// اتصال جديد، فلا نتخطاه لمجرد أن أول تثبيت GPS لم يصل بعد.
|
||||
print("🚀 Sending initial location to socket after connect...");
|
||||
_emitCurrentLocationNow();
|
||||
|
||||
_startHeartbeat();
|
||||
|
||||
// 🆕 طلب أي رحلات انتظار فاتتنا أثناء انقطاع الاتصال
|
||||
@@ -546,6 +552,30 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
_socketHeartbeat?.cancel();
|
||||
}
|
||||
|
||||
/// يبثّ الموقع الحالي فوراً، ويجلب تثبيتاً واحداً إن لم يكن لدينا موقع بعد.
|
||||
///
|
||||
/// ضروري بعد كل اتصال جديد وبعد كل تغيّر حالة: ملفّ التتبّع في وضع الانتظار
|
||||
/// يستخدم فلتر مسافة (50–100 متر)، فالكابتن الواقف قد لا يُصدر أي حدث
|
||||
/// onLocationChanged إطلاقاً — ولو اعتمدنا عليه وحده لبقي غير مرئي على
|
||||
/// خريطة الراكب حتى يتحرّك فعلياً.
|
||||
Future<void> _emitCurrentLocationNow() async {
|
||||
if (socket == null || !socket!.connected) return;
|
||||
|
||||
if (myLocation.latitude == 0 && myLocation.longitude == 0) {
|
||||
await getLocation();
|
||||
}
|
||||
if (myLocation.latitude == 0 && myLocation.longitude == 0) {
|
||||
Log.print("⚠️ No GPS fix yet — initial location emit skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBusMode) {
|
||||
emitBusLocationToSocket(myLocation, heading, speed);
|
||||
} else {
|
||||
emitLocationToSocket(myLocation, heading, speed);
|
||||
}
|
||||
}
|
||||
|
||||
// In LocationController.dart
|
||||
|
||||
void emitLocationToSocket(LatLng pos, double head, double spd) {
|
||||
|
||||
@@ -373,68 +373,115 @@ class HomeCaptainController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void onButtonSelected() {
|
||||
/// زرّ «متاح / غير متاح» في الواجهة — يقلب الحالة الحالية.
|
||||
void onButtonSelected() => setActive(!isActive);
|
||||
|
||||
/// تفعيل/إيقاف الكابتن بشكل صريح.
|
||||
/// مُعامِلة صريحة بدل القلب (toggle) لأن الاستدعاء التلقائي في onInit كان
|
||||
/// يعتمد على أن isActive تساوي false — فأي إعادة بناء للكونترولر أو
|
||||
/// استدعاء ثانٍ كانت تُطفئ الكابتن بدل تشغيله. الاستدعاء بنفس القيمة
|
||||
/// الحالية آمن: يعيد التأكد من أن التتبّع شغّال فقط.
|
||||
void setActive(bool active) {
|
||||
if (!Get.isRegistered<CaptainWalletController>()) {
|
||||
Get.put(CaptainWalletController());
|
||||
}
|
||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
||||
|
||||
// Toggle Active State
|
||||
isActive = !isActive;
|
||||
|
||||
if (isActive) {
|
||||
try {
|
||||
_checkFatigueBeforeOnline(); // Throws exception if tired
|
||||
|
||||
if (double.parse(totalPoints) > minPointsThreshold) {
|
||||
locationController.startLocationUpdates();
|
||||
HapticFeedback.heavyImpact();
|
||||
activeStartTime = DateTime.now();
|
||||
|
||||
activeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
activeDuration = DateTime.now().difference(activeStartTime!);
|
||||
stringActiveDuration = formatDuration(activeDuration);
|
||||
|
||||
// Increment Fatigue Counter (write to box every 30s)
|
||||
_fatigueSeconds++;
|
||||
if (_fatigueSeconds % 30 == 0) {
|
||||
int totalSeconds =
|
||||
(box.read('fatigue_total_seconds') ?? 0) + _fatigueSeconds;
|
||||
box.write('fatigue_total_seconds', totalSeconds);
|
||||
_fatigueSeconds = 0;
|
||||
if (totalSeconds >= 12 * 3600) {
|
||||
// 12 hours
|
||||
_forceOfflineDueToFatigue();
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
});
|
||||
} else {
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
savePeriod(activeDuration);
|
||||
activeDuration = Duration.zero;
|
||||
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
// Driver is fatigued, revert state
|
||||
isActive = false;
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
savePeriod(activeDuration);
|
||||
activeDuration = Duration.zero;
|
||||
|
||||
// Save offline time for Fatigue Monitoring reset
|
||||
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
|
||||
update();
|
||||
if (!active) {
|
||||
_goOffline();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_checkFatigueBeforeOnline(); // يرمي استثناءً إذا تجاوز 12 ساعة
|
||||
} catch (_) {
|
||||
isActive = false;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
// الرصيد غير كافٍ: نُطفئ فعلياً بدل إبقاء isActive = true بلا تتبّع،
|
||||
// فقد كانت الواجهة تعرض «متاح» بينما لا يُبَث أي موقع.
|
||||
final double? points = double.tryParse(totalPoints);
|
||||
if (points == null || points <= minPointsThreshold) {
|
||||
_goOffline();
|
||||
return;
|
||||
}
|
||||
|
||||
final bool wasActive = isActive;
|
||||
isActive = true;
|
||||
|
||||
// آمنة للاستدعاء المتكرر: تتجاهل الاشتراك إن كان قائماً بالفعل.
|
||||
locationController.startLocationUpdates();
|
||||
|
||||
if (!wasActive) {
|
||||
HapticFeedback.heavyImpact();
|
||||
activeStartTime = DateTime.now();
|
||||
activeTimer?.cancel();
|
||||
activeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
activeDuration = DateTime.now().difference(activeStartTime!);
|
||||
stringActiveDuration = formatDuration(activeDuration);
|
||||
|
||||
// Increment Fatigue Counter (write to box every 30s)
|
||||
_fatigueSeconds++;
|
||||
if (_fatigueSeconds % 30 == 0) {
|
||||
int totalSeconds =
|
||||
(box.read('fatigue_total_seconds') ?? 0) + _fatigueSeconds;
|
||||
box.write('fatigue_total_seconds', totalSeconds);
|
||||
_fatigueSeconds = 0;
|
||||
if (totalSeconds >= 12 * 3600) {
|
||||
// 12 hours
|
||||
_forceOfflineDueToFatigue();
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void _goOffline() {
|
||||
isActive = false;
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
if (activeDuration.inSeconds > 0) savePeriod(activeDuration);
|
||||
activeDuration = Duration.zero;
|
||||
|
||||
// Save offline time for Fatigue Monitoring reset
|
||||
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
|
||||
update();
|
||||
}
|
||||
|
||||
/// يضبط حالة الكابتن المخزَّنة عند إقلاع التطبيق.
|
||||
/// تذكير بمصطلحات التطبيق: 'off' = متاح، 'on' = مشغول برحلة،
|
||||
/// 'blocked' = محظور، وهي القيمة التي يقرأها emitLocationToSocket ويبني
|
||||
/// عليها السيرفر عضوية geo:drivers:available.
|
||||
///
|
||||
/// كانت 'off' تُكتب دون شرط في آخر onInit، فتُلغي حظراً ساري المفعول
|
||||
/// كتبه checkAndShowBlockDialog قبلها بسطر، وتُعيد كابتناً في رحلة نشطة
|
||||
/// إلى حوض المتاحين بعد إعادة فتح التطبيق.
|
||||
void _applyInitialDriverStatus() {
|
||||
final String? blockStr = box.read(BoxName.blockUntilDate);
|
||||
if (blockStr != null && blockStr.isNotEmpty) {
|
||||
final DateTime? blockExpiry = DateTime.tryParse(blockStr);
|
||||
if (blockExpiry != null && DateTime.now().isBefore(blockExpiry)) {
|
||||
box.write(BoxName.statusDriverLocation, 'blocked');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// رحلة نجت من إغلاق التطبيق: نُبقيه مشغولاً حتى لا يُوزَّع عليه طلب ثانٍ.
|
||||
final String? rideStatus = box.read(BoxName.rideStatus);
|
||||
if (rideStatus == 'Apply' || rideStatus == 'Arrived' ||
|
||||
rideStatus == 'Begin') {
|
||||
box.write(BoxName.statusDriverLocation, 'on');
|
||||
return;
|
||||
}
|
||||
|
||||
box.write(BoxName.statusDriverLocation, 'off'); // متاح
|
||||
}
|
||||
|
||||
// متغيرات العداد للحظر
|
||||
@@ -815,8 +862,16 @@ class HomeCaptainController extends GetxController {
|
||||
|
||||
Get.put(FirebaseMessagesController());
|
||||
addToken();
|
||||
|
||||
// ⚠️ الترتيب مقصود: الحالة تُكتب قبل تشغيل التتبّع.
|
||||
// أول update_location يُرسَل بعد اتصال السوكيت مباشرة، وهو الذي يُدخل
|
||||
// الكابتن إلى geo:drivers:available على السيرفر. وكتابة الحالة بعد
|
||||
// تشغيل التتبّع (كما كانت في آخر السطور) تعني أن ذلك الـ emit قد يحمل
|
||||
// حالة قديمة ('on' من رحلة سابقة لم تُغلق مثلاً).
|
||||
_applyInitialDriverStatus();
|
||||
|
||||
await getlocation();
|
||||
onButtonSelected();
|
||||
setActive(true);
|
||||
getDriverRate();
|
||||
addCustomCarIcon();
|
||||
getKazanPercent();
|
||||
@@ -829,8 +884,8 @@ class HomeCaptainController extends GetxController {
|
||||
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
||||
// getRefusedOrderByCaptain();
|
||||
// 🔥 الفحص عند تشغيل التطبيق
|
||||
// (يكتب 'blocked' عند وجود حظر ساري — ولم تعد أي كتابة بعده تدهسه)
|
||||
checkAndShowBlockDialog();
|
||||
box.write(BoxName.statusDriverLocation, 'off');
|
||||
// 2. عدل الليسنر ليصبح مشروطاً
|
||||
// Camera follow timer — only moves when the driver has
|
||||
// actually moved > 15 meters, saving GPU/battery on idle.
|
||||
|
||||
Reference in New Issue
Block a user