272 lines
10 KiB
Dart
272 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:intaleq_maps/intaleq_maps.dart';
|
|
|
|
import '../../../constant/colors.dart';
|
|
import '../../../constant/table_names.dart';
|
|
import '../../../controller/functions/toast.dart';
|
|
import '../../../controller/home/map/location_search_controller.dart';
|
|
import '../../../controller/home/map/map_engine_controller.dart';
|
|
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
|
import '../../../main.dart';
|
|
import '../../../print.dart';
|
|
import '../../widgets/mydialoug.dart';
|
|
import 'rp_theme.dart';
|
|
|
|
/// All controller side-effects for the route planner live here, so the
|
|
/// widgets stay declarative. This is a faithful port of the behavior that
|
|
/// previously lived inside `form_search_*.dart` and `main_bottom_Menu_map`.
|
|
///
|
|
/// No controller is modified — this only *calls* their existing API.
|
|
class RpActions {
|
|
RpActions._();
|
|
|
|
static LocationSearchController get _s =>
|
|
Get.find<LocationSearchController>();
|
|
static MapEngineController get _map => Get.find<MapEngineController>();
|
|
static RideLifecycleController get _ride =>
|
|
Get.find<RideLifecycleController>();
|
|
|
|
// ── Destination ─────────────────────────────────────────────────────────
|
|
static void pickDestinationOnMap() {
|
|
_map.changeMainBottomMenuMap();
|
|
_map.changePickerShown();
|
|
}
|
|
|
|
static Future<void> selectDestination(int index, dynamic res) async {
|
|
final lat = res['latitude'];
|
|
final lng = res['longitude'];
|
|
if (lat == null || lng == null) {
|
|
Toast.show(Get.context!, 'Invalid location data', AppColor.redColor);
|
|
return;
|
|
}
|
|
final String title =
|
|
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString();
|
|
|
|
await sql.insertMapLocation({
|
|
'latitude': lat,
|
|
'longitude': lng,
|
|
'name': title,
|
|
'rate': 'N/A',
|
|
'createdAt': DateTime.now().toIso8601String(),
|
|
}, TableName.recentLocations);
|
|
|
|
final dest =
|
|
LatLng(double.parse(lat.toString()), double.parse(lng.toString()));
|
|
|
|
if (_ride.isAnotherOreder) {
|
|
_s.myDestination = dest;
|
|
_s.clearPlacesDestination();
|
|
await _ride.getDirectionMap(
|
|
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
|
'${_s.myDestination.latitude},${_s.myDestination.longitude}');
|
|
_map.isPickerShown = false;
|
|
_s.passengerStartLocationFromMap = false;
|
|
_map.changeMainBottomMenuMap();
|
|
_ride.showBottomSheet1();
|
|
} else {
|
|
_s.passengerLocation = _s.newMyLocation;
|
|
_s.myDestination = dest;
|
|
_s.convertHintTextDestinationNewPlaces(index);
|
|
_s.clearPlacesDestination();
|
|
_map.changeMainBottomMenuMap();
|
|
_s.passengerStartLocationFromMap = true;
|
|
_map.isPickerShown = true;
|
|
_map.mapController?.animateCamera(
|
|
CameraUpdate.newLatLngZoom(_s.passengerLocation, 16),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Origin / pickup ───────────────────────────────────────────────────────
|
|
/// Map-pick for the *other person's* pickup (order-for-someone-else mode).
|
|
static void pickOtherPickupOnMap() {
|
|
_s.passengerStartLocationFromMap = true;
|
|
_map.changeMainBottomMenuMap();
|
|
_map.changePickerShown();
|
|
}
|
|
|
|
/// Map-pick for my own start point (updates the pickup without re-routing).
|
|
static void pickMyStartOnMap() {
|
|
_s.startLocationFromMap = true;
|
|
_map.changeMainBottomMenuMap();
|
|
_map.changePickerShown();
|
|
}
|
|
|
|
/// Selecting a searched start point (other person's pickup).
|
|
static void selectStart(dynamic res) {
|
|
final lat = res['latitude'];
|
|
final lng = res['longitude'];
|
|
if (lat == null || lng == null) return;
|
|
final String title =
|
|
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString();
|
|
_s.passengerLocation =
|
|
LatLng(double.parse(lat.toString()), double.parse(lng.toString()));
|
|
_s.placeStartController.text = title;
|
|
_s.clearPlacesStart();
|
|
_s.update();
|
|
}
|
|
|
|
// ── Gate Selection (Bottom Sheet) ──────────────────────────────────────────
|
|
static void handleLocationSelection(
|
|
BuildContext context, int index, dynamic place, bool isOrigin) {
|
|
final hasGates = place['gates'] != null &&
|
|
place['gates'] is List &&
|
|
(place['gates'] as List).isNotEmpty;
|
|
|
|
if (!hasGates) {
|
|
if (isOrigin) {
|
|
selectStart(place);
|
|
} else {
|
|
selectDestination(index, place);
|
|
}
|
|
return;
|
|
}
|
|
|
|
final List gates = place['gates'];
|
|
final bool isArabic = Get.locale?.languageCode == 'ar';
|
|
|
|
showModalBottomSheet(
|
|
context: context,
|
|
backgroundColor: Colors.transparent,
|
|
isScrollControlled: true,
|
|
builder: (context) {
|
|
return Container(
|
|
padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 24),
|
|
decoration: BoxDecoration(
|
|
color: RP.sheetElevated,
|
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(RP.rSheet)),
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
RP.grabber(),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Select Gate'.tr,
|
|
style: RP.title,
|
|
),
|
|
const SizedBox(height: 16),
|
|
...gates.map((gate) {
|
|
final String gateName = isArabic
|
|
? (gate['name_ar'] ?? gate['name_en'] ?? 'Gate'.tr)
|
|
: (gate['name_en'] ?? gate['name_ar'] ?? 'Gate'.tr);
|
|
|
|
return ListTile(
|
|
contentPadding: EdgeInsets.zero,
|
|
leading: Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.primaryColor.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(Icons.door_sliding_outlined,
|
|
color: AppColor.primaryColor, size: 20),
|
|
),
|
|
title: Text(gateName, style: RP.fieldValue),
|
|
subtitle: gate['is_main_gate'] == true
|
|
? Text('Main Gate'.tr, style: RP.body)
|
|
: null,
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
final modifiedPlace = Map<String, dynamic>.from(place);
|
|
modifiedPlace['latitude'] = gate['latitude'];
|
|
modifiedPlace['longitude'] = gate['longitude'];
|
|
modifiedPlace['name_ar'] = '${place['name_ar'] ?? place['name'] ?? ''} - $gateName';
|
|
modifiedPlace['name'] = '${place['name'] ?? place['name_ar'] ?? ''} - $gateName';
|
|
|
|
if (isOrigin) {
|
|
selectStart(modifiedPlace);
|
|
} else {
|
|
selectDestination(index, modifiedPlace);
|
|
}
|
|
},
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
// ── Stops (menu waypoints) ────────────────────────────────────────────────
|
|
static void addStop() => _s.addMenuWaypoint();
|
|
static void removeStop(int index) => _s.removeMenuWaypoint(index);
|
|
static void pickStopOnMap(int index) => _s.startPickingWaypointOnMap(index);
|
|
|
|
// ── Home / Work quick actions ─────────────────────────────────────────────
|
|
static void pickHomeOrWork({required bool isWork}) {
|
|
if (isWork) {
|
|
_s.workLocationFromMap = true;
|
|
} else {
|
|
_s.homeLocationFromMap = true;
|
|
}
|
|
_map.changeMainBottomMenuMap();
|
|
_map.changePickerShown();
|
|
}
|
|
|
|
static void changeHomeOrWork({required bool isWork}) {
|
|
MyDialog().getDialog(
|
|
isWork ? 'Change Work location ?'.tr : 'Change Home location ?'.tr,
|
|
'',
|
|
() => pickHomeOrWork(isWork: isWork),
|
|
);
|
|
}
|
|
|
|
/// Navigate to a saved Home/Work location and open the ride sheet.
|
|
static Future<void> goToSaved(String boxName, String hint) async {
|
|
try {
|
|
final locationString = box.read(boxName).toString();
|
|
final parts = locationString.split(',');
|
|
final latLng =
|
|
LatLng(double.parse(parts[0].trim()), double.parse(parts[1].trim()));
|
|
|
|
_s.hintTextDestinationPoint = hint.tr;
|
|
_map.changeMainBottomMenuMap();
|
|
|
|
await _ride.getDirectionMap(
|
|
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
|
'${latLng.latitude},${latLng.longitude}',
|
|
);
|
|
|
|
_s.currentLocationToFormPlaces = false;
|
|
_s.clearPlacesDestination();
|
|
_s.passengerStartLocationFromMap = false;
|
|
_map.isPickerShown = false;
|
|
_ride.showBottomSheet1();
|
|
} catch (e) {
|
|
Log.print('RpActions.goToSaved error: $e');
|
|
Toast.show(Get.context!, 'Failed to get location'.tr, AppColor.redColor);
|
|
}
|
|
}
|
|
|
|
// ── Favorites / recents ───────────────────────────────────────────────────
|
|
static Future<void> addFavorite(
|
|
BuildContext context, dynamic lat, dynamic lng, String title) async {
|
|
if (lat == null || lng == null) {
|
|
Toast.show(context, 'Invalid location data', AppColor.redColor);
|
|
return;
|
|
}
|
|
await sql.insertMapLocation({
|
|
'latitude': lat,
|
|
'longitude': lng,
|
|
'name': title,
|
|
'rate': 'N/A',
|
|
}, TableName.placesFavorite);
|
|
if (!context.mounted) return;
|
|
Toast.show(
|
|
context, '$title ${'Saved Successfully'.tr}', AppColor.primaryColor);
|
|
}
|
|
|
|
/// Route to a stored place (recent or favorite) after confirmation.
|
|
static Future<void> goToStoredPlace(dynamic place) async {
|
|
await _s.getLocation();
|
|
await _ride.getDirectionMap(
|
|
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
|
'${place['latitude']},${place['longitude']}',
|
|
);
|
|
_ride.showBottomSheet1();
|
|
}
|
|
}
|