Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps
This commit is contained in:
436
lib/controller/home/map/ui_interactions_controller.dart
Normal file
436
lib/controller/home/map/ui_interactions_controller.dart
Normal file
@@ -0,0 +1,436 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../constant/info.dart';
|
||||
import '../../../main.dart'; // contains global 'box'
|
||||
import '../../../print.dart';
|
||||
import '../../../services/emergency_signal_service.dart';
|
||||
import '../../../views/widgets/elevated_btn.dart';
|
||||
import '../../../views/widgets/mydialoug.dart';
|
||||
import '../../../views/widgets/my_textField.dart';
|
||||
import '../../../views/home/map_page_passenger.dart';
|
||||
import '../../../views/widgets/error_snakbar.dart';
|
||||
import '../../../models/model/painter_copoun.dart';
|
||||
import '../../functions/launch.dart';
|
||||
import '../../firebase/local_notification.dart';
|
||||
import '../../firebase/notification_service.dart';
|
||||
import '../../functions/crud.dart';
|
||||
import '../../functions/tts.dart';
|
||||
import 'ride_lifecycle_controller.dart';
|
||||
import 'location_search_controller.dart';
|
||||
import 'map_engine_controller.dart';
|
||||
|
||||
class UiInteractionsController extends GetxController {
|
||||
TextEditingController sosPhonePassengerProfile = TextEditingController();
|
||||
TextEditingController whatsAppLocationText = TextEditingController();
|
||||
final sosFormKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
EmergencySignalService.instance.startListening(() {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
if (rideLifecycle.statusRide == 'Begin' ||
|
||||
rideLifecycle.statusRide == 'start') {
|
||||
Log.print("🚨 Emergency shake verified! Prompting SOS...");
|
||||
sosPassenger();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _ensureSosNumber(Function onSuccess) async {
|
||||
String? storedPhone = box.read(BoxName.sosPhonePassenger);
|
||||
if (storedPhone != null && storedPhone.isNotEmpty) {
|
||||
onSuccess();
|
||||
return;
|
||||
}
|
||||
|
||||
sosPhonePassengerProfile.clear();
|
||||
Get.defaultDialog(
|
||||
title: 'Add SOS Phone'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Form(
|
||||
key: sosFormKey,
|
||||
child: Column(
|
||||
children: [
|
||||
MyTextForm(
|
||||
controller: sosPhonePassengerProfile,
|
||||
label: 'insert sos phone'.tr,
|
||||
hint: 'e.g. 0912345678 (Default +963)'.tr,
|
||||
type: TextInputType.phone,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Note: If no country code is entered, it will be saved as Syrian (+963).".tr,
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Save'.tr,
|
||||
onPressed: () async {
|
||||
if (sosFormKey.currentState!.validate()) {
|
||||
Get.back();
|
||||
var numberPhone =
|
||||
formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
|
||||
|
||||
await CRUD().post(
|
||||
link: AppLink.updateprofile,
|
||||
payload: {
|
||||
'id': box.read(BoxName.passengerID),
|
||||
'sosPhone': numberPhone,
|
||||
},
|
||||
);
|
||||
|
||||
box.write(BoxName.sosPhonePassenger, numberPhone);
|
||||
onSuccess();
|
||||
}
|
||||
},
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'Cancel'.tr,
|
||||
onPressed: () => Get.back(),
|
||||
kolor: AppColor.redColor,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void sosPassenger() {
|
||||
_ensureSosNumber(() {
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
title: "Emergency SOS".tr,
|
||||
titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
|
||||
content: Column(
|
||||
children: [
|
||||
Icon(Icons.warning_amber_rounded, size: 50, color: AppColor.redColor),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Do you want to send an emergency message to your SOS contact?".tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: "Send SOS".tr,
|
||||
kolor: AppColor.redColor,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
_shareTripDetailsSOS();
|
||||
},
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: "I'm Safe".tr,
|
||||
kolor: AppColor.greenColor,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _shareTripDetailsSOS() {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
|
||||
String message = "**Emergency SOS from Passenger:**\n";
|
||||
String origin = locSearch.startNameAddress;
|
||||
String destination = locSearch.endNameAddress;
|
||||
|
||||
message += "* ${'Origin'.tr}: $origin\n";
|
||||
message += "* ${'Destination'.tr}: $destination\n";
|
||||
message += "* ${'Driver Name'.tr}: ${rideLifecycle.driverName}\n";
|
||||
message +=
|
||||
"* ${'Car'.tr}: ${rideLifecycle.make} - ${rideLifecycle.model} - ${rideLifecycle.licensePlate}\n";
|
||||
message += "* ${'Phone'.tr}: ${rideLifecycle.driverPhone}\n\n";
|
||||
|
||||
message +=
|
||||
"${'Location'.tr}: https://www.google.com/maps/search/?api=1&query=${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}\n";
|
||||
message += "Please help! Contact me as soon as possible.".tr;
|
||||
|
||||
launchCommunication(
|
||||
'whatsapp', box.read(BoxName.sosPhonePassenger), message);
|
||||
}
|
||||
|
||||
String formatSyrianPhone(String phone) {
|
||||
phone = phone.replaceAll(' ', '').replaceAll('+', '');
|
||||
if (phone.startsWith('00963')) {
|
||||
phone = phone.replaceFirst('00963', '963');
|
||||
}
|
||||
if (phone.startsWith('0963')) {
|
||||
phone = phone.replaceFirst('0963', '963');
|
||||
}
|
||||
if (phone.startsWith('963')) {
|
||||
return phone;
|
||||
}
|
||||
if (phone.startsWith('09')) {
|
||||
return '963' + phone.substring(1);
|
||||
}
|
||||
if (phone.startsWith('9') && phone.length == 9) {
|
||||
return '963' + phone;
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
|
||||
String formatSyrianPhoneNumber(String phoneNumber) {
|
||||
String trimmedPhone = phoneNumber.trim();
|
||||
if (trimmedPhone.startsWith('09')) {
|
||||
return '963${trimmedPhone.substring(1)}';
|
||||
}
|
||||
if (trimmedPhone.startsWith('963')) {
|
||||
return trimmedPhone;
|
||||
}
|
||||
return '963$trimmedPhone';
|
||||
}
|
||||
|
||||
void sendSMS(String to) async {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
String formattedDriverPhone =
|
||||
rideLifecycle.driverPhone.replaceAll(' ', '').replaceAll('+', '');
|
||||
|
||||
String message =
|
||||
'Hi! This is ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n I am using ${box.read(AppInformation.appName)} to ride with ${rideLifecycle.passengerName} as the driver. ${rideLifecycle.passengerName} \nis driving a ${rideLifecycle.model}\n with license plate ${rideLifecycle.licensePlate}.\n I am currently located at ${Get.find<LocationSearchController>().passengerLocation}.\n If you need to reach me, please contact the driver directly at\n\n $formattedDriverPhone.';
|
||||
|
||||
launchCommunication('sms', to, message);
|
||||
}
|
||||
|
||||
void sendWhatsapp(String to) async {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
String formattedPhone = formatSyrianPhone(to);
|
||||
|
||||
String message =
|
||||
'${'${'Hi! This is'.tr} ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n${' I am using'.tr}'} ${AppInformation.appName}${' to ride with'.tr} ${rideLifecycle.passengerName}${' as the driver.'.tr} ${rideLifecycle.passengerName} \n${'is driving a '.tr}${rideLifecycle.model}\n${' with license plate '.tr}${rideLifecycle.licensePlate}.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n ${rideLifecycle.driverPhone}.';
|
||||
|
||||
launchCommunication('whatsapp', formattedPhone, message);
|
||||
}
|
||||
|
||||
Future<dynamic> driverArrivePassengerDialoge() {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
return Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
title: 'Hi ,I Arrive your location'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
middleText: 'Please go to Car Driver'.tr,
|
||||
middleTextStyle: AppStyle.title,
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok I will go now.'.tr,
|
||||
onPressed: () {
|
||||
NotificationService.sendNotification(
|
||||
target: rideLifecycle.driverToken.toString(),
|
||||
title: 'Hi ,I will go now'.tr,
|
||||
body: 'I will go now'.tr,
|
||||
isTopic: false,
|
||||
tone: 'ding',
|
||||
driverList: [],
|
||||
category: 'Hi ,I will go now',
|
||||
);
|
||||
Get.back();
|
||||
rideLifecycle.remainingTime = 0;
|
||||
rideLifecycle.update();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void getDialog(String title, String? midTitle, VoidCallback onPressed) {
|
||||
final textToSpeechController = Get.find<TextToSpeechController>();
|
||||
Get.defaultDialog(
|
||||
title: title,
|
||||
titleStyle: AppStyle.title,
|
||||
middleTextStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await textToSpeechController.speakText(title ?? midTitle!);
|
||||
},
|
||||
icon: const Icon(Icons.headphones),
|
||||
),
|
||||
Text(
|
||||
midTitle!,
|
||||
style: AppStyle.title,
|
||||
)
|
||||
],
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Ok'.tr,
|
||||
onPressed: onPressed,
|
||||
kolor: AppColor.greenColor,
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'Cancel',
|
||||
kolor: AppColor.redColor,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future shareTripWithFamily() async {
|
||||
_ensureSosNumber(() {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
String storedPhone = box.read(BoxName.sosPhonePassenger)!;
|
||||
|
||||
if (rideLifecycle.rideId == 'yet' || rideLifecycle.driverId.isEmpty) {
|
||||
Get.snackbar("Alert".tr, "Wait for the trip to start first".tr);
|
||||
return;
|
||||
}
|
||||
|
||||
var numberPhone = formatSyrianPhoneNumber(storedPhone);
|
||||
String trackingLink = rideLifecycle.generateTrackingLink(
|
||||
rideLifecycle.rideId, rideLifecycle.driverId);
|
||||
|
||||
String message = """
|
||||
مرحباً، تابع رحلتي مباشرة على تطبيق انطلق 🚗
|
||||
|
||||
يمكنك تتبع مسار الرحلة من هنا:
|
||||
$trackingLink
|
||||
|
||||
السائق: ${rideLifecycle.passengerName}
|
||||
السيارة: ${rideLifecycle.model} - ${rideLifecycle.licensePlate}
|
||||
شكراً لاستخدامك انطلق!
|
||||
"""
|
||||
.tr;
|
||||
|
||||
String messageEn = """Hello, follow my trip live on Intaleq 🚗
|
||||
|
||||
Track my ride here:
|
||||
$trackingLink
|
||||
|
||||
Driver: ${rideLifecycle.passengerName}
|
||||
Car: ${rideLifecycle.model} - ${rideLifecycle.licensePlate}
|
||||
Thank you for using Intaleq!
|
||||
""";
|
||||
|
||||
String userLanguage = box.read(BoxName.lang) ?? 'ar';
|
||||
message = (userLanguage == 'ar') ? message : messageEn;
|
||||
|
||||
Log.print("Sending WhatsApp to: $numberPhone");
|
||||
launchCommunication('whatsapp', numberPhone, message);
|
||||
|
||||
box.write(BoxName.parentTripSelected, true);
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
Future getTokenForParent() async {
|
||||
_ensureSosNumber(() async {
|
||||
String storedPhone = box.read(BoxName.sosPhonePassenger)!;
|
||||
var numberPhone = formatSyrianPhoneNumber(storedPhone);
|
||||
Log.print("Searching for Parent Token with Phone: $numberPhone");
|
||||
|
||||
var res = await CRUD()
|
||||
.post(link: AppLink.getTokenParent, payload: {'phone': numberPhone});
|
||||
|
||||
if (res is Map<String, dynamic>) {
|
||||
handleResponse(res);
|
||||
} else {
|
||||
try {
|
||||
var decoded = jsonDecode(res);
|
||||
handleResponse(decoded);
|
||||
} catch (e) {
|
||||
Log.print("Error parsing parent response: $res");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleResponse(Map<String, dynamic> res) {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
if (res['status'] == 'failure') {
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
|
||||
Get.defaultDialog(
|
||||
title: "No user found".tr,
|
||||
titleStyle: AppStyle.title,
|
||||
content: Column(
|
||||
children: [
|
||||
Text(
|
||||
"No passenger found for the given phone number".tr,
|
||||
style: AppStyle.title,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Send Intaleq app to him".tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(color: AppColor.greenColor, fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
],
|
||||
),
|
||||
confirm: MyElevatedButton(
|
||||
title: 'Send Invite'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
var rawPhone = box.read(BoxName.sosPhonePassenger);
|
||||
if (rawPhone == null) return;
|
||||
var phone = formatSyrianPhoneNumber(rawPhone);
|
||||
|
||||
var message = '''Dear Friend,
|
||||
|
||||
🚀 I have just started an exciting trip on Intaleq!
|
||||
Download the app to track my ride:
|
||||
|
||||
👉 Android: https://play.google.com/store/apps/details?id=com.Intaleq.intaleq&hl=en-US
|
||||
👉 iOS: https://apps.apple.com/st/app/intaleq-rider/id6748075179
|
||||
|
||||
See you there!
|
||||
Intaleq Team''';
|
||||
|
||||
launchCommunication('whatsapp', phone, message);
|
||||
},
|
||||
),
|
||||
cancel: MyElevatedButton(
|
||||
title: 'Cancel'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
);
|
||||
} else if (res['status'] == 'success') {
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
|
||||
Get.snackbar("Success".tr, "The invitation was sent successfully".tr,
|
||||
backgroundColor: AppColor.greenColor, colorText: Colors.white);
|
||||
|
||||
List tokensData = res['data'];
|
||||
for (var device in tokensData) {
|
||||
String tokenParent = device['token'];
|
||||
|
||||
NotificationService.sendNotification(
|
||||
category: "Trip Monitoring",
|
||||
target: tokenParent,
|
||||
title: "Trip Monitoring".tr,
|
||||
body: "Click to track the trip".tr,
|
||||
isTopic: false,
|
||||
tone: 'tone1',
|
||||
driverList: [rideLifecycle.rideId, rideLifecycle.driverId],
|
||||
);
|
||||
box.write(BoxName.tokenParent, tokenParent);
|
||||
}
|
||||
box.write(BoxName.parentTripSelected, true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
EmergencySignalService.instance.stopListening();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user