25-09-22/1

This commit is contained in:
Hamza-Ayed
2025-09-22 17:28:19 +03:00
parent 13d77e118c
commit 7595be8067
19 changed files with 536 additions and 246 deletions

View File

@@ -6,7 +6,6 @@ import 'dart:math' as math;
import 'dart:ui';
import 'dart:convert';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:Intaleq/constant/univeries_polygon.dart';
@@ -14,7 +13,6 @@ import 'package:Intaleq/controller/firebase/local_notification.dart';
import 'package:Intaleq/controller/functions/encrypt_decrypt.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_confetti/flutter_confetti.dart';
import 'package:uni_links/uni_links.dart';
import 'package:vector_math/vector_math.dart' show radians, degrees;
import 'package:Intaleq/controller/functions/tts.dart';
@@ -54,14 +52,11 @@ import '../functions/launch.dart';
import '../functions/package_info.dart';
import '../functions/secure_storage.dart';
import '../payment/payment_controller.dart';
import 'deep_link_controller.dart';
import 'device_tier.dart';
import 'vip_waitting_page.dart';
class MapPassengerController extends GetxController {
// --- START: DEEP LINKING ADDITIONS ---
StreamSubscription? _linkSubscription;
// --- END: DEEP LINKING ADDITIONS ---
bool isLoading = true;
TextEditingController placeDestinationController = TextEditingController();
TextEditingController increasFeeFromPassenger = TextEditingController();
@@ -276,6 +271,11 @@ class MapPassengerController extends GetxController {
late DateTime newTime = DateTime.now();
int hours = 0;
int minutes = 0;
// --- إضافة جديدة: للوصول إلى وحدة التحكم بالروابط ---
final DeepLinkController _deepLinkController = Get.find();
// ------------------------------------------------
void onChangedPassengerCount(int newValue) {
selectedPassengerCount = newValue;
update();
@@ -286,92 +286,6 @@ class MapPassengerController extends GetxController {
update();
}
/// Initializes the deep link listener.
/// It checks for the initial link when the app starts and then listens for subsequent links.
Future<void> _initUniLinks() async {
try {
// Get the initial link that opened the app
final initialLink = await getInitialUri();
if (initialLink != null) {
handleDeepLink(initialLink);
}
} on PlatformException {
print('Failed to get initial deep link.');
} on FormatException {
print('Invalid initial deep link format.');
}
// Listen for incoming links while the app is running
_linkSubscription = uriLinkStream.listen((Uri? link) {
handleDeepLink(link);
}, onError: (err) {
print('Error listening to deep links: $err');
});
}
/// Parses the incoming deep link and triggers the route initiation.
void handleDeepLink(Uri? link) {
if (link == null) return;
// Check if the link matches your app's scheme and path
// e.g., intaleq://map?lat=31.9539&lng=35.9106
if (link.scheme == 'intaleq' && link.host == 'map') {
final latString = link.queryParameters['lat'];
final lngString = link.queryParameters['lng'];
if (latString != null && lngString != null) {
final double? lat = double.tryParse(latString);
final double? lng = double.tryParse(lngString);
if (lat != null && lng != null) {
final destination = LatLng(lat, lng);
print('Deep link received. Destination: $destination');
initiateRouteFromDeepLink(destination);
} else {
print('Failed to parse lat/lng from deep link.');
}
}
}
}
/// Sets the destination from the deep link and updates the UI to show the map.
void initiateRouteFromDeepLink(LatLng destination) async {
// Wait for map controller to be ready
if (mapController == null) {
await Future.delayed(const Duration(seconds: 1));
if (mapController == null) {
print("Map controller is not available to handle deep link.");
return;
}
}
myDestination = destination;
// Animate camera to user's current location to show the starting point
await mapController?.animateCamera(CameraUpdate.newLatLng(
LatLng(passengerLocation.latitude, passengerLocation.longitude)));
// Ensure the main menu is visible to start the booking process
if (isMainBottomMenuMap) {
changeMainBottomMenuMap();
}
passengerStartLocationFromMap = true;
isPickerShown = true;
hintTextDestinationPoint = "Destination from external link".tr;
update();
// The user can now see the destination and proceed to get the route and price.
Get.snackbar(
"Location Received".tr,
"The destination has been set from the link.".tr,
backgroundColor: AppColor.greenColor,
colorText: Colors.white,
);
}
// --- END: DEEP LINKING METHODS ---
void getCurrentLocationFormString() async {
currentLocationToFormPlaces = true;
currentLocationString = 'Waiting for your location'.tr;
@@ -3282,8 +3196,6 @@ class MapPassengerController extends GetxController {
print(
"--- MapPassengerController: Closing and cleaning up all resources. ---");
_linkSubscription?.cancel();
// 1. إلغاء المؤقتات الفردية
// Using ?.cancel() is safe even if the timer is null
markerReloadingTimer.cancel();
@@ -3616,6 +3528,8 @@ class MapPassengerController extends GetxController {
update();
}
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
const R = 6371.0; // km
final dLat = (lat2 - lat1) * math.pi / 180.0;
@@ -3629,6 +3543,29 @@ class MapPassengerController extends GetxController {
return R * c;
}
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
double _kmToLatDelta(double km) => km / 111.0;
/// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض)
double _kmToLngDelta(double km, double atLat) =>
km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9);
/// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة)
double _relevanceScore(String name, String query) {
final n = name.toLowerCase();
final parts =
query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2);
double s = 0.0;
for (final p in parts) {
if (n.startsWith(p)) {
s += 2.0;
} else if (n.contains(p)) {
s += 1.0;
}
}
return s;
}
Future<void> getPlaces() async {
final q = placeDestinationController.text.trim();
if (q.isEmpty) {
@@ -3640,11 +3577,17 @@ class MapPassengerController extends GetxController {
final lat = passengerLocation.latitude;
final lng = passengerLocation.longitude;
const range = 2.2;
final latMin = lat - range;
final latMax = lat + range;
final lngMin = lng - range;
final lngMax = lng + range;
// نصف قطر البحث بالكيلومتر (عدّل حسب رغبتك)
const radiusKm = 200.0;
// حساب الباوند الصحيح (درجات، وليس 2.2 درجة ثابتة)
final latDelta = _kmToLatDelta(radiusKm);
final lngDelta = _kmToLngDelta(radiusKm, lat);
final latMin = lat - latDelta;
final latMax = lat + latDelta;
final lngMin = lng - lngDelta;
final lngMax = lng + lngDelta;
try {
final response = await CRUD().post(
@@ -3658,43 +3601,59 @@ class MapPassengerController extends GetxController {
},
);
if (response != 'failure') {
final list = (response['message'] as List?) ?? [];
// احسب المسافة وألصقها بكل عنصر
for (final p in list) {
final plat = double.tryParse(p['latitude']?.toString() ?? '') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '') ?? 0.0;
p['distanceKm'] = _haversineKm(lat, lng, plat, plng);
}
// رتّب حسب الأقرب
list.sort((a, b) {
final da = (a['distanceKm'] ?? 1e9) as double;
final db = (b['distanceKm'] ?? 1e9) as double;
final cmp = da.compareTo(db);
if (cmp != 0) return cmp;
// تعادل؟ فضّل من يطابق الاسم
final nameA =
(a['name'] ?? a['name_ar'] ?? a['name_en'] ?? '').toString();
final nameB =
(b['name'] ?? b['name_ar'] ?? b['name_en'] ?? '').toString();
final qLower = q.toLowerCase();
final hitA = nameA.toLowerCase().contains(qLower) ? 0 : 1;
final hitB = nameB.toLowerCase().contains(qLower) ? 0 : 1;
return hitA.compareTo(hitB);
});
placesDestination = list;
update();
// يدعم شكلي استجابة: إما {"...","message":[...]} أو قائمة مباشرة [...]
List list;
if (response is Map && response['message'] is List) {
list = List.from(response['message'] as List);
} else if (response is List) {
list = List.from(response);
} else {
print('Server error');
print('Unexpected response shape');
return;
}
// جهّز الحقول المحتملة للأسماء
String _bestName(Map p) {
return (p['name'] ?? p['name_ar'] ?? p['name_en'] ?? '').toString();
}
// احسب المسافة ودرجة التطابق والنقاط
for (final p in list) {
final plat = double.tryParse(p['latitude']?.toString() ?? '') ?? 0.0;
final plng = double.tryParse(p['longitude']?.toString() ?? '') ?? 0.0;
final d = _haversineKm(lat, lng, plat, plng);
final rel = _relevanceScore(_bestName(p), q);
// معادلة ترتيب ذكية: مسافة أقل + تطابق أعلى = نقاط أعلى
// تضيف +1 لضمان عدم وصول الوزن للصفر عند عدم وجود تطابق
final score = (1.0 / (1.0 + d)) * (1.0 + rel);
p['distanceKm'] = d;
p['relevance'] = rel;
p['score'] = score;
}
// رتّب حسب score تنازليًا، ثم المسافة تصاعديًا كحسم
list.sort((a, b) {
final sa = (a['score'] ?? 0.0) as double;
final sb = (b['score'] ?? 0.0) as double;
final cmp = sb.compareTo(sa);
if (cmp != 0) return cmp;
final da = (a['distanceKm'] ?? 1e9) as double;
final db = (b['distanceKm'] ?? 1e9) as double;
return da.compareTo(db);
});
// خذ أول 1015 للعرض (اختياري)، أو اعرض الكل
placesDestination = list.take(15).toList();
Log.print('placesDestination: $placesDestination');
update();
} catch (e) {
print('Exception: $e');
print('Exception in getPlaces: $e');
}
} // Future getPlaces() async {
}
// var languageCode;
// // تحديد اللغة حسب الإدخال
@@ -5804,16 +5763,45 @@ class MapPassengerController extends GetxController {
}
}
// --- دالة جديدة للاستماع ومعالجة الرابط ---
void _listenForDeepLink() {
// استمع إلى أي تغيير في الإحداثيات القادمة من الرابط
ever(_deepLinkController.deepLinkLatLng, (LatLng? latLng) {
if (latLng != null) {
print('MapPassengerController detected deep link LatLng: $latLng');
// عندما يتم استلام إحداثيات جديدة، عينها كوجهة
myDestination = latLng;
// قم بتشغيل المنطق الخاص بك لعرض المسار
// (تأكد من أن `passengerLocation` لديها قيمة أولاً)
if (passengerLocation != null) {
getDirectionMap(
'${passengerLocation.latitude},${passengerLocation.longitude}',
'${myDestination.latitude},${myDestination.longitude}');
} else {
// يمكنك إظهار رسالة للمستخدم لتمكين الموقع أولاً
print(
'Cannot process deep link route yet, passenger location is null.');
}
// إعادة تعيين القيمة إلى null لمنع التشغيل مرة أخرى عند إعادة بناء الواجهة
_deepLinkController.deepLinkLatLng.value = null;
}
});
}
@override
void onInit() async {
super.onInit();
// // --- إضافة جديدة: تهيئة وحدة التحكم في الروابط العميقة ---
// Get.put(DeepLinkController(), permanent: true);
// // ----------------------------------------------------
// مرحلة 0: الضروري جداً لعرض الخريطة سريعاً
mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
await initilizeGetStorage(); // إعداد سريع
await _initMinimalIcons(); // start/end فقط
await addToken(); // لو لازم للمصادقة
await _initUniLinks();
_listenForDeepLink();
await getLocation(); // لتحديد الكاميرا
box.write(BoxName.carType, 'yet');
box.write(BoxName.tipPercentage, '0');