Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps
This commit is contained in:
@@ -2,11 +2,19 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:sefer_driver/constant/colors.dart';
|
||||
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import 'package:sefer_driver/views/widgets/mydialoug.dart';
|
||||
import '../../../../controller/voice_call_controller.dart';
|
||||
import '../../../../controller/functions/launch.dart';
|
||||
import '../../../../controller/functions/location_controller.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../widgets/error_snakbar.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import '../../../../controller/firebase/notification_service.dart';
|
||||
import '../../../../controller/functions/crud.dart';
|
||||
import '../../../../constant/links.dart';
|
||||
import '../../../widgets/my_textField.dart';
|
||||
|
||||
class PassengerInfoWindow extends StatelessWidget {
|
||||
const PassengerInfoWindow({super.key});
|
||||
@@ -132,12 +140,19 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
Icon(Icons.location_on,
|
||||
size: 14, color: Colors.grey[600]),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'${controller.distance} km',
|
||||
style: TextStyle(
|
||||
color: Colors.grey[700],
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600),
|
||||
// 🔥 [Fix Overflow] Flexible لمنع الـ overflow + تحويل المسافة
|
||||
// السيرفر يُرجع المسافة بالأمتار (5864.022)
|
||||
Flexible(
|
||||
child: Text(
|
||||
_formatDistanceDisplay(
|
||||
controller.distance),
|
||||
style: TextStyle(
|
||||
color: Colors.grey[700],
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Icon(Icons.access_time_filled,
|
||||
@@ -172,8 +187,8 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
await controller.driverCallPassenger();
|
||||
|
||||
if (canCall) {
|
||||
makePhoneCall(
|
||||
controller.passengerPhone.toString());
|
||||
_showCallSelectionDialog(
|
||||
context, controller);
|
||||
} else {
|
||||
// هنا ممكن تظهر رسالة: تم منع الاتصال بسبب كثرة الإلغاءات
|
||||
mySnackeBarError(
|
||||
@@ -194,6 +209,26 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
color: Colors.green, size: 22),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
_showMessageOptions(context, controller),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
shape: BoxShape.circle,
|
||||
border:
|
||||
Border.all(color: Colors.grey.shade300),
|
||||
),
|
||||
child: Icon(
|
||||
MaterialCommunityIcons
|
||||
.message_text_outline,
|
||||
color: AppColor.primaryColor,
|
||||
size: 22),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -372,13 +407,40 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
onPressed: () {
|
||||
MyDialog().getDialog(
|
||||
"Start Trip?".tr,
|
||||
"Ensure the passenger is in the car.".tr,
|
||||
() async {
|
||||
await controller.startRideFromDriver();
|
||||
Get.back();
|
||||
},
|
||||
// 🔥 [Fix Start-Ride] استخدام Get.defaultDialog بدلاً من MyDialog
|
||||
// لأن MyDialog يستخدم Navigator.of(context, rootNavigator: true).pop()
|
||||
// الذي يتعارض مع Get.dialog() المستخدم في startRideFromDriver()
|
||||
// وقد يُسبب Get.back() اللاحق إغلاق صفحة الماب بدلاً من الـ loading dialog
|
||||
Get.defaultDialog(
|
||||
title: "Start Trip?".tr,
|
||||
titleStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
middleText: "Ensure the passenger is in the car.".tr,
|
||||
barrierDismissible: true,
|
||||
radius: 14,
|
||||
confirm: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF27AE60),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
onPressed: () async {
|
||||
// نُغلق الديالوج بـ Get.back() لضمان أن GetX يعرف أنه أُغلق
|
||||
Get.back();
|
||||
// ثم نُنفذ startRideFromDriver الذي يستخدم Get.dialog و Get.back بأمان
|
||||
await controller.startRideFromDriver();
|
||||
},
|
||||
child: Text('Start'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
),
|
||||
cancel: TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text('Cancel'.tr,
|
||||
style: const TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.play_circle_fill_rounded),
|
||||
@@ -389,4 +451,167 @@ class PassengerInfoWindow extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showCallSelectionDialog(
|
||||
BuildContext context, MapDriverController controller) {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Call Options'.tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Choose how you want to call the passenger'.tr,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.green.withOpacity(0.1),
|
||||
child: const Icon(Icons.phone_android_rounded,
|
||||
color: Colors.green),
|
||||
),
|
||||
title: Text('Standard Call'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text('Uses cellular network'.tr,
|
||||
style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
makePhoneCall(controller.passengerPhone.toString());
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
child: Icon(Icons.wifi_calling_3_rounded,
|
||||
color: AppColor.primaryColor),
|
||||
),
|
||||
title: Text('Free Call'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text('Voice call over internet'.tr,
|
||||
style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
final voiceCtrl = Get.find<VoiceCallController>();
|
||||
final driverId = box.read(BoxName.driverID).toString();
|
||||
voiceCtrl.startCall(
|
||||
rideIdVal: controller.rideId,
|
||||
driverId: driverId,
|
||||
passengerId: controller.passengerId,
|
||||
remoteNameVal: controller.passengerName ?? "Passenger",
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showMessageOptions(
|
||||
BuildContext context, MapDriverController controller) {
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(25)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('Quick Messages'.tr,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 15),
|
||||
_buildQuickMessageItem("Where are you, sir?".tr, controller),
|
||||
_buildQuickMessageItem("I've arrived.".tr, controller),
|
||||
const Divider(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.messageToPassenger,
|
||||
decoration:
|
||||
InputDecoration(hintText: 'Type a message...'.tr),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.send),
|
||||
onPressed: () {
|
||||
_sendMessage(controller, controller.messageToPassenger.text,
|
||||
'cancel');
|
||||
controller.messageToPassenger.clear();
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuickMessageItem(String text, MapDriverController controller) {
|
||||
return ListTile(
|
||||
title: Text(text),
|
||||
onTap: () {
|
||||
_sendMessage(controller, text, 'ding');
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _sendMessage(
|
||||
MapDriverController controller, String body, String tone) async {
|
||||
try {
|
||||
await CRUD().post(
|
||||
link: AppLink.sendChatMessage,
|
||||
payload: {
|
||||
'ride_id': controller.rideId.toString(),
|
||||
'sender_id': box.read(BoxName.driverID).toString(),
|
||||
'receiver_id': controller.passengerId.toString(),
|
||||
'sender_type': 'driver',
|
||||
'message_content': body,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
// Ignore or log error
|
||||
}
|
||||
|
||||
NotificationService.sendNotification(
|
||||
target: controller.tokenPassenger.toString(),
|
||||
title: 'Driver Message'.tr,
|
||||
body: body,
|
||||
isTopic: false,
|
||||
tone: tone,
|
||||
driverList: [],
|
||||
category: 'message From Driver',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// تحويل المسافة من الأمتار إلى عرض مقروء
|
||||
/// السيرفر يُرجع المسافة بالأمتار (مثال: 5864.022)
|
||||
/// النتيجة: "5.9 km" أو "250 م"
|
||||
String _formatDistanceDisplay(String rawDistance) {
|
||||
final meters = double.tryParse(rawDistance) ?? 0.0;
|
||||
if (meters >= 1000) {
|
||||
return '${(meters / 1000).toStringAsFixed(1)} km';
|
||||
} else if (meters > 0) {
|
||||
return '${meters.toStringAsFixed(0)} م';
|
||||
}
|
||||
return rawDistance; // fallback للقيمة الأصلية
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user