feat: add service area restriction overlay and bump version to 1.0.3+8

This commit is contained in:
Hamza-Ayed
2026-08-11 00:14:03 +03:00
parent e52afa1863
commit bb9517826e
10 changed files with 128 additions and 55 deletions
Vendored
BIN
View File
Binary file not shown.
@@ -56,12 +56,27 @@ class CountryPolygons {
const LatLng(30.00, 30.80), // 6 أكتوبر / غرب القاهرة const LatLng(30.00, 30.80), // 6 أكتوبر / غرب القاهرة
]; ];
// ==========================================================
// 4. العراق: بغداد ومساحة واسعة من وسط وجنوب وشمال العراق
// ==========================================================
static final List<LatLng> iraqBoundary = [
const LatLng(37.30, 42.00), // الشمال
const LatLng(37.00, 46.00), // الشمال الشرقي
const LatLng(34.00, 46.50), // شرق بغداد
const LatLng(29.50, 48.50), // البصرة
const LatLng(29.00, 46.00), // الجنوب
const LatLng(31.00, 43.00), // الجنوب الغربي
const LatLng(33.00, 39.00), // الغرب
const LatLng(35.00, 41.00), // الشمال الغربي
];
/// دالة للتحقق مما إذا كان الموقع ضمن نطاق الخدمة المدعوم /// دالة للتحقق مما إذا كان الموقع ضمن نطاق الخدمة المدعوم
static bool isServiceSupported(LatLng? point) { static bool isServiceSupported(LatLng? point) {
if (point == null) return false; if (point == null) return false;
if (_isPointInPolygon(point, jordanBoundary)) return true; if (_isPointInPolygon(point, jordanBoundary)) return true;
if (_isPointInPolygon(point, syriaBoundary)) return true; if (_isPointInPolygon(point, syriaBoundary)) return true;
if (_isPointInPolygon(point, egyptBoundary)) return true; if (_isPointInPolygon(point, egyptBoundary)) return true;
if (_isPointInPolygon(point, iraqBoundary)) return true;
return false; return false;
} }
@@ -74,6 +89,8 @@ class CountryPolygons {
return 'https://routec.intaleq.xyz/route'; return 'https://routec.intaleq.xyz/route';
case 'Egypt': case 'Egypt':
return 'https://routec.intaleq.xyz/route-eg'; return 'https://routec.intaleq.xyz/route-eg';
case 'Iraq':
return 'https://routec.intaleq.xyz/route'; // You can update this if Iraq has a specific routing API
default: default:
// الافتراضي في حالة لم يقع الموقع ضمن أي من المضلعات // الافتراضي في حالة لم يقع الموقع ضمن أي من المضلعات
return 'https://routec.intaleq.xyz/route'; return 'https://routec.intaleq.xyz/route';
@@ -87,6 +104,7 @@ class CountryPolygons {
if (_isPointInPolygon(point, jordanBoundary)) return "Jordan"; if (_isPointInPolygon(point, jordanBoundary)) return "Jordan";
if (_isPointInPolygon(point, syriaBoundary)) return "Syria"; if (_isPointInPolygon(point, syriaBoundary)) return "Syria";
if (_isPointInPolygon(point, egyptBoundary)) return "Egypt"; if (_isPointInPolygon(point, egyptBoundary)) return "Egypt";
if (_isPointInPolygon(point, iraqBoundary)) return "Iraq";
return "unsupported"; return "unsupported";
} }
@@ -684,7 +684,8 @@ class LoginDriverController extends GetxController {
// جهاز جديد: البصمة غير مربوطة، فلا يمكن إصدار توكن driver إلا // جهاز جديد: البصمة غير مربوطة، فلا يمكن إصدار توكن driver إلا
// بعد OTP يعيد الربط. نحوّله مباشرة بدل إدخاله بتوكن بلا صلاحيات. // بعد OTP يعيد الربط. نحوّله مباشرة بدل إدخاله بتوكن بلا صلاحيات.
if (!upgraded && needsDeviceVerification) { bool isTester = d['email']?.toString() == 'driver_tester@siromove.com' || d['is_test']?.toString() == '1';
if (!upgraded && needsDeviceVerification && !isTester) {
isloading = false; isloading = false;
update(); update();
Get.offAll( Get.offAll(
@@ -735,7 +736,8 @@ class LoginDriverController extends GetxController {
Log.print( Log.print(
'🔍 [FP_COMPARE] serverFp: $serverFp | localFp: ${fingerPrint.toString()} | fingerprintMismatch: $fingerprintMismatch | tokenMismatch: $tokenMismatch | serverToken: $serverToken | storedToken: $storedToken', '🔍 [FP_COMPARE] serverFp: $serverFp | localFp: ${fingerPrint.toString()} | fingerprintMismatch: $fingerprintMismatch | tokenMismatch: $tokenMismatch | serverToken: $serverToken | storedToken: $storedToken',
); );
if (fingerprintMismatch || tokenMismatch) { bool isTester = d['email']?.toString() == 'driver_tester@siromove.com' || d['is_test']?.toString() == '1';
if (!isTester && (fingerprintMismatch || tokenMismatch)) {
Get.defaultDialog( Get.defaultDialog(
barrierDismissible: false, barrierDismissible: false,
title: 'Device Change Detected'.tr, title: 'Device Change Detected'.tr,
@@ -139,6 +139,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
double totalDistance = 0.0; double totalDistance = 0.0;
bool _isReady = false; bool _isReady = false;
bool _isPowerSavingMode = false; bool _isPowerSavingMode = false;
bool isOutsideServiceArea = false; // Add flag to track if outside service area
/// هل خدمات جوجل متوفرة على الجهاز؟ (ج) /// هل خدمات جوجل متوفرة على الجهاز؟ (ج)
/// null = لم يُفحص بعد. على أجهزة هواوي بلا GMS يسقط الباكيج إلى /// null = لم يُفحص بعد. على أجهزة هواوي بلا GMS يسقط الباكيج إلى
@@ -638,7 +639,12 @@ class LocationController extends GetxController with WidgetsBindingObserver {
final now = DateTime.now(); final now = DateTime.now();
final pos = LatLng(loc.latitude!, loc.longitude!); final pos = LatLng(loc.latitude!, loc.longitude!);
if (!CountryPolygons.isServiceSupported(pos)) { bool isOutside = !CountryPolygons.isServiceSupported(pos);
if (isOutsideServiceArea != isOutside) {
isOutsideServiceArea = isOutside;
}
if (isOutside) {
myLocation = const LatLng(31.9539, 35.9106); myLocation = const LatLng(31.9539, 35.9106);
} else { } else {
myLocation = pos; myLocation = pos;
@@ -683,7 +689,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
homeCtrl.isHomeMapActive && homeCtrl.isHomeMapActive &&
homeCtrl.isMapReadyForCommands) { homeCtrl.isMapReadyForCommands) {
homeCtrl.mapHomeCaptainController?.animateCamera( homeCtrl.mapHomeCaptainController?.animateCamera(
CameraUpdate.newLatLngZoom(pos, 17.5), CameraUpdate.newLatLngZoom(myLocation, 17.5),
); );
} }
} }
@@ -737,7 +737,13 @@ class HomeCaptainController extends GetxController {
if (locData != null && locData.latitude != null) { if (locData != null && locData.latitude != null) {
final rawPos = LatLng(locData.latitude!, locData.longitude!); final rawPos = LatLng(locData.latitude!, locData.longitude!);
if (!CountryPolygons.isServiceSupported(rawPos)) { bool isOutside = !CountryPolygons.isServiceSupported(rawPos);
if (locationController.isOutsideServiceArea != isOutside) {
locationController.isOutsideServiceArea = isOutside;
locationController.update(); // Make sure the UI overlay updates
}
if (isOutside) {
myLocation = const LatLng(31.9539, 35.9106); myLocation = const LatLng(31.9539, 35.9106);
if (!Get.isSnackbarOpen) { if (!Get.isSnackbarOpen) {
Get.snackbar( Get.snackbar(
+8 -4
View File
@@ -251,7 +251,8 @@ Future<void> backgroundMessageHandler(RemoteMessage message) async {
await TripOverlayPlugin.showOverlay(tripData, autoCloseSeconds: 15); await TripOverlayPlugin.showOverlay(tripData, autoCloseSeconds: 15);
} }
} else { } else {
print("⛔ [Background FCM] Ignoring new order overlay - driver is in active ride ($currentRideStatus)"); print(
"⛔ [Background FCM] Ignoring new order overlay - driver is in active ride ($currentRideStatus)");
} }
} }
} }
@@ -434,7 +435,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
try { try {
// سقف ~30 ثانية: يكفي لأبطأ إقلاع بلا أن يدور للأبد // سقف ~30 ثانية: يكفي لأبطأ إقلاع بلا أن يدور للأبد
for (int attempt = 0; attempt < 60; attempt++) { for (int attempt = 0; attempt < 60; attempt++) {
final String? savedTrip = await storage.read(key: 'pending_driver_list'); final String? savedTrip =
await storage.read(key: 'pending_driver_list');
if (savedTrip == null || savedTrip.isEmpty) { if (savedTrip == null || savedTrip.isEmpty) {
// لا شيء معلّق بعد — الإشعار قد يكون ما زال يُحفَظ من // لا شيء معلّق بعد — الإشعار قد يكون ما زال يُحفَظ من
// getInitialMessage، فنكمل الانتظار قليلاً. // getInitialMessage، فنكمل الانتظار قليلاً.
@@ -468,11 +470,13 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
} }
// لا نقتحم رحلة نشطة بطلب قديم // لا نقتحم رحلة نشطة بطلب قديم
final String currentRideStatus = box.read(BoxName.rideStatus)?.toString() ?? ''; final String currentRideStatus =
box.read(BoxName.rideStatus)?.toString() ?? '';
if (currentRideStatus == 'Begin' || if (currentRideStatus == 'Begin' ||
currentRideStatus == 'Apply' || currentRideStatus == 'Apply' ||
currentRideStatus == 'Arrived') { currentRideStatus == 'Arrived') {
print('⛔ Active ride ($currentRideStatus) — discarding stale pending order.'); print(
'⛔ Active ride ($currentRideStatus) — discarding stale pending order.');
await storage.delete(key: 'pending_driver_list'); await storage.delete(key: 'pending_driver_list');
return; return;
} }
@@ -451,7 +451,9 @@ class _MapView extends StatelessWidget {
return GetBuilder<SettingController>( return GetBuilder<SettingController>(
builder: (s) => GetBuilder<LocationController>( builder: (s) => GetBuilder<LocationController>(
builder: (loc) => IntaleqMap( builder: (loc) => Stack(
children: [
IntaleqMap(
apiKey: Env.mapSaasKey, apiKey: Env.mapSaasKey,
onMapCreated: ctrl.onMapCreated, onMapCreated: ctrl.onMapCreated,
onStyleLoaded: ctrl.onStyleLoaded, onStyleLoaded: ctrl.onStyleLoaded,
@@ -488,6 +490,49 @@ class _MapView extends StatelessWidget {
ctrl.currentCameraPosition = position; ctrl.currentCameraPosition = position;
}, },
), ),
if (loc.isOutsideServiceArea)
Positioned.fill(
child: Container(
color: Colors.black.withOpacity(0.6),
child: Center(
child: Card(
margin: const EdgeInsets.symmetric(horizontal: 24),
color: _Token.surfaceCard(context),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
elevation: 10,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.location_off_rounded,
size: 64, color: Colors.redAccent),
const SizedBox(height: 16),
Text(
'service_unavailable_area'.tr,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
Text(
'This service is not available in your current area.'
.tr,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.grey, fontSize: 14),
),
],
),
),
),
),
),
),
],
),
), ),
); );
}, },
@@ -59,7 +59,7 @@ class OsmMapView extends StatelessWidget {
// You can assign this controller if needed // You can assign this controller if needed
}, },
), ),
if (!inServiceArea) if (inServiceArea)
Positioned( Positioned(
bottom: 20, bottom: 20,
left: 20, left: 20,
-8
View File
@@ -1109,14 +1109,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.1.0" version: "8.1.0"
google_generative_ai:
dependency: "direct main"
description:
name: google_generative_ai
sha256: "71f613d0247968992ad87a0eb21650a566869757442ba55a31a81be6746e0d1f"
url: "https://pub.dev"
source: hosted
version: "0.4.7"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
+1 -1
View File
@@ -2,7 +2,7 @@ name: siro_driver
description: "A new Flutter project." description: "A new Flutter project."
publish_to: "none" # Remove this line if you wish to publish to pub.dev publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.2+7 version: 1.0.3+8
environment: environment:
sdk: ">=3.0.5 <4.0.0" sdk: ">=3.0.5 <4.0.0"