2026-04-03-pre map libra
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_contacts/contact.dart';
|
||||
@@ -124,24 +125,175 @@ Download the Intaleq app now and enjoy your ride!
|
||||
/// **MODIFIED**: This function now opens the phone's native contact picker.
|
||||
Future<void> pickContactFromNativeApp() async {
|
||||
try {
|
||||
log('=== START: FETCHING ALL CONTACTS FOR BOTTOM SHEET ===',
|
||||
name: 'ContactPicker');
|
||||
|
||||
if (await FlutterContacts.requestPermission(readonly: true)) {
|
||||
// 1. Open the native contacts app to select a single contact.
|
||||
final Contact? contact = await FlutterContacts.openExternalPick();
|
||||
// عرض شاشة تحميل بسيطة ريثما يتم جلب الأسماء
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()),
|
||||
barrierDismissible: false);
|
||||
|
||||
// 2. If a contact is selected and has a phone number...
|
||||
if (contact != null && contact.phones.isNotEmpty) {
|
||||
String selectedPhone = contact.phones.first.number;
|
||||
log('Permission granted. Calling FlutterContacts.getContacts()...',
|
||||
name: 'ContactPicker');
|
||||
|
||||
// 3. Format the number and update the text field.
|
||||
invitePhoneController.text = _formatSyrianPhoneNumber(selectedPhone);
|
||||
update();
|
||||
// جلب جميع جهات الاتصال إجبارياً من الصفر مع خصائصها
|
||||
List<Contact> allContacts =
|
||||
await FlutterContacts.getContacts(withProperties: true);
|
||||
|
||||
log('Total Contacts Fetched from Device: ${allContacts.length}',
|
||||
name: 'ContactPicker');
|
||||
|
||||
// فصل الأسماء لمعرفة الخلل
|
||||
List<Contact> validContacts = [];
|
||||
List<Contact> invalidContacts = [];
|
||||
|
||||
for (var c in allContacts) {
|
||||
if (c.phones.isNotEmpty) {
|
||||
validContacts.add(c);
|
||||
} else {
|
||||
invalidContacts.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
log('Contacts WITH phone numbers: ${validContacts.length}',
|
||||
name: 'ContactPicker');
|
||||
log('Contacts WITHOUT phone numbers: ${invalidContacts.length}',
|
||||
name: 'ContactPicker');
|
||||
|
||||
// طباعة أول 20 اسم صالح
|
||||
log('--- Sample of VALID Contacts ---', name: 'ContactPicker');
|
||||
for (int i = 0; i < validContacts.length && i < 20; i++) {
|
||||
log('[$i] Name: ${validContacts[i].displayName}, Phone: ${validContacts[i].phones.first.number}',
|
||||
name: 'ContactPicker');
|
||||
}
|
||||
|
||||
// طباعة أول 20 اسم غير صالح (بدون أرقام) لفحص المشكلة
|
||||
log('--- Sample of INVALID Contacts (No Phone) ---',
|
||||
name: 'ContactPicker');
|
||||
for (int i = 0; i < invalidContacts.length && i < 20; i++) {
|
||||
log('[$i] Name: ${invalidContacts[i].displayName}',
|
||||
name: 'ContactPicker');
|
||||
}
|
||||
|
||||
Get.back(); // إغلاق شاشة التحميل
|
||||
|
||||
if (validContacts.isEmpty) {
|
||||
mySnackeBarError('No contacts with phone numbers found'.tr);
|
||||
return;
|
||||
}
|
||||
|
||||
// متغيرات للبحث داخل القائمة المنسدلة
|
||||
RxList<Contact> filteredContacts = validContacts.obs;
|
||||
TextEditingController searchController = TextEditingController();
|
||||
|
||||
// دالة لتنظيف النصوص من أي رموز معطوبة
|
||||
String sanitizeText(String input) {
|
||||
if (input.isEmpty) return '';
|
||||
return input
|
||||
.replaceAll(
|
||||
RegExp(r'[^\x00-\x7F\u0600-\u06FF\u08A0-\u08FF\p{L}\p{N}\s]'),
|
||||
'')
|
||||
.trim();
|
||||
}
|
||||
|
||||
// فتح دليل هاتف مخصص داخل التطبيق
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
height: Get.height * 0.85,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(Get.context!).scaffoldBackgroundColor,
|
||||
borderRadius:
|
||||
const BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
width: 50,
|
||||
height: 5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[400],
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
Text("Select a Contact".tr,
|
||||
style: const TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: TextField(
|
||||
controller: searchController,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search name or number...".tr,
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 0),
|
||||
),
|
||||
onChanged: (value) {
|
||||
filteredContacts.value = validContacts.where((c) {
|
||||
final nameMatch = c.displayName
|
||||
.toLowerCase()
|
||||
.contains(value.toLowerCase());
|
||||
final phoneMatch =
|
||||
c.phones.first.number.contains(value);
|
||||
return nameMatch || phoneMatch;
|
||||
}).toList();
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Obx(() => ListView.builder(
|
||||
itemCount: filteredContacts.length,
|
||||
itemBuilder: (context, index) {
|
||||
Contact c = filteredContacts[index];
|
||||
var firstPhone = c.phones.first;
|
||||
|
||||
String selectedPhone =
|
||||
firstPhone.normalizedNumber.isNotEmpty
|
||||
? firstPhone.normalizedNumber
|
||||
: firstPhone.number;
|
||||
|
||||
String safeName = sanitizeText(c.displayName);
|
||||
if (safeName.isEmpty) safeName = 'Unknown'.tr;
|
||||
|
||||
String safePhone = sanitizeText(selectedPhone);
|
||||
String initial = safeName.isNotEmpty
|
||||
? safeName[0].toUpperCase()
|
||||
: '?';
|
||||
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Colors.blueAccent.withOpacity(0.1),
|
||||
child: Text(initial,
|
||||
style: const TextStyle(
|
||||
color: Colors.blueAccent)),
|
||||
),
|
||||
title: Text(safeName),
|
||||
subtitle: Text(safePhone,
|
||||
textDirection: TextDirection.ltr),
|
||||
onTap: () {
|
||||
selectPhone(selectedPhone);
|
||||
},
|
||||
);
|
||||
},
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
isScrollControlled: true,
|
||||
);
|
||||
} else {
|
||||
log('Permission DENIED', name: 'ContactPicker');
|
||||
mySnackeBarError('Contact permission is required to pick contacts'.tr);
|
||||
}
|
||||
} catch (e) {
|
||||
mySnackeBarError('An error occurred while picking contacts: $e'.tr);
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
log('CRITICAL ERROR: $e', name: 'ContactPicker');
|
||||
mySnackeBarError('An error occurred while loading contacts: $e'.tr);
|
||||
}
|
||||
log('=== END: FETCHING CONTACTS ===', name: 'ContactPicker');
|
||||
}
|
||||
|
||||
/// **FIX**: Added the missing 'selectPhone' method.
|
||||
@@ -196,11 +348,11 @@ Download the Intaleq app now and enjoy your ride!
|
||||
Get.back();
|
||||
} else if (isAvailable) {
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
if ((driverInvitationData[index]['isGiftToken']).toString() ==
|
||||
'0') {
|
||||
@@ -210,23 +362,23 @@ Download the Intaleq app now and enjoy your ride!
|
||||
payload: {'id': (driverInvitationData[index]['id'])});
|
||||
await Get.find<CaptainWalletController>().addDriverPayment(
|
||||
'paymentMethod',
|
||||
('50000'),
|
||||
('500'),
|
||||
'',
|
||||
);
|
||||
await Get.find<CaptainWalletController>()
|
||||
.addDriverWalletToInvitor(
|
||||
'paymentMethod',
|
||||
(driverInvitationData[index]['driverInviterId']),
|
||||
('50000'),
|
||||
('500'),
|
||||
);
|
||||
NotificationCaptainController().addNotificationCaptain(
|
||||
driverInvitationData[index]['driverInviterId'].toString(),
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have 50000".tr} ${'SYP'.tr}',
|
||||
'${"You have 500".tr} ${'SYP'.tr}',
|
||||
false);
|
||||
NotificationController().showNotification(
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have 50000".tr} ${'SYP'.tr}',
|
||||
'${"You have 500".tr} ${'SYP'.tr}',
|
||||
'tone1',
|
||||
'');
|
||||
} else {
|
||||
@@ -265,21 +417,21 @@ Download the Intaleq app now and enjoy your ride!
|
||||
Get.back();
|
||||
} else if (isAvailable) {
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
if (driverInvitationDataToPassengers[index]['isGiftToken']
|
||||
.toString() ==
|
||||
'0') {
|
||||
Get.back();
|
||||
await Get.find<CaptainWalletController>()
|
||||
.addDriverWallet('paymentMethod', '20000', '20000');
|
||||
.addDriverWallet('paymentMethod', '200', '200');
|
||||
await Get.find<CaptainWalletController>()
|
||||
.addDriverWalletToInvitor('paymentMethod',
|
||||
driverInvitationData[index]['driverInviterId'], '20000');
|
||||
driverInvitationData[index]['driverInviterId'], '200');
|
||||
await CRUD().post(
|
||||
link: AppLink.updatePassengerGift,
|
||||
payload: {'id': driverInvitationDataToPassengers[index]['id']},
|
||||
@@ -288,7 +440,7 @@ Download the Intaleq app now and enjoy your ride!
|
||||
driverInvitationDataToPassengers[index]['passengerInviterId']
|
||||
.toString(),
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have 20000".tr} ${'SYP'.tr}',
|
||||
'${"You have 200".tr} ${'SYP'.tr}',
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -13,19 +13,20 @@ import '../functions/add_error.dart';
|
||||
import '../functions/encrypt_decrypt.dart';
|
||||
|
||||
class GoogleSignInHelper {
|
||||
static final GoogleSignIn _googleSignIn = GoogleSignIn(
|
||||
scopes: [
|
||||
'email',
|
||||
'profile',
|
||||
],
|
||||
);
|
||||
static final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
|
||||
|
||||
// متغير ثابت لحفظ حالة المستخدم محلياً كبديل لخاصية currentUser المحذوفة
|
||||
static GoogleSignInAccount? _cachedUser;
|
||||
|
||||
// Method to handle Google Sign-In
|
||||
static Future<GoogleSignInAccount?> signIn() async {
|
||||
try {
|
||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||
final GoogleSignInAccount? googleUser =
|
||||
await _googleSignIn.authenticate();
|
||||
|
||||
if (googleUser != null) {
|
||||
_cachedUser = googleUser; // حفظ الجلسة في الكاش المحلي
|
||||
await _handleSignUp(googleUser);
|
||||
|
||||
if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||
Get.to(() => SmsSignupEgypt());
|
||||
} else if (box.read(BoxName.countryCode) == 'Jordan') {
|
||||
@@ -40,32 +41,23 @@ class GoogleSignInHelper {
|
||||
|
||||
Future<GoogleSignInAccount?> signInFromLogin() async {
|
||||
try {
|
||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||
final GoogleSignInAccount? googleUser =
|
||||
await _googleSignIn.authenticate();
|
||||
|
||||
if (googleUser != null) {
|
||||
// Handle sign-up and store user information
|
||||
_cachedUser = googleUser; // حفظ الجلسة في الكاش المحلي
|
||||
await _handleSignUp(googleUser);
|
||||
|
||||
// Retrieve driverID and emailDriver with added validation
|
||||
final driverID =
|
||||
(box.read(BoxName.driverID)!.toString()) ?? 'Unknown ID';
|
||||
(box.read(BoxName.driverID)?.toString()) ?? 'Unknown ID';
|
||||
final emailDriver =
|
||||
(box.read(BoxName.emailDriver)!.toString()) ?? 'Unknown Email';
|
||||
(box.read(BoxName.emailDriver)?.toString()) ?? 'Unknown Email';
|
||||
|
||||
// Debug print statements
|
||||
print('Driver ID: $driverID');
|
||||
print('Driver Email: $emailDriver');
|
||||
|
||||
// Check if driverID is a valid numeric string
|
||||
// if (driverID != 'Unknown ID' && int.tryParse(driverID) != null) {
|
||||
await Get.find<LoginDriverController>()
|
||||
.loginWithGoogleCredential(driverID, emailDriver);
|
||||
// }
|
||||
// else {
|
||||
// print('Invalid driverID format: $driverID');
|
||||
// Get.snackbar('Login Error', 'Invalid driver ID format.',
|
||||
// backgroundColor: AppColor.redColor);
|
||||
// }
|
||||
}
|
||||
|
||||
return googleUser;
|
||||
@@ -78,204 +70,29 @@ class GoogleSignInHelper {
|
||||
}
|
||||
|
||||
static Future<void> _handleSignUp(GoogleSignInAccount user) async {
|
||||
// Store driver information
|
||||
box.write(BoxName.driverID,
|
||||
(user.id) ?? 'Unknown ID'); // Ensure there's a fallback value
|
||||
box.write(BoxName.driverID, (user.id) ?? 'Unknown ID');
|
||||
box.write(BoxName.emailDriver, (user.email) ?? 'Unknown Email');
|
||||
}
|
||||
|
||||
// Method to handle Google Sign-Out
|
||||
static Future<void> signOut() async {
|
||||
try {
|
||||
await _googleSignIn.signOut();
|
||||
} catch (error) {
|
||||
// التعامل مع الخطأ بصمت إذا كانت جلسة جوجل فارغة مسبقاً
|
||||
} finally {
|
||||
_cachedUser = null; // مسح الكاش المحلي بشكل إلزامي
|
||||
await _handleSignOut();
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _handleSignOut() async {
|
||||
// Clear stored driver information
|
||||
box.erase();
|
||||
storage.deleteAll();
|
||||
Get.offAll(OnBoardingPage());
|
||||
// Perform any additional sign-out tasks or API calls here
|
||||
// For example, you can notify your server about the user sign-out
|
||||
}
|
||||
|
||||
// Method to get the current signed-in user
|
||||
// استخدام الكاش المحلي بدلاً من استدعاء المكتبة
|
||||
static GoogleSignInAccount? getCurrentUser() {
|
||||
return _googleSignIn.currentUser;
|
||||
return _cachedUser;
|
||||
}
|
||||
}
|
||||
// import 'dart:async';
|
||||
// import 'dart:async';
|
||||
// import 'dart:convert';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import 'package:sefer_driver/constant/box_name.dart';
|
||||
// import 'package:sefer_driver/controller/auth/captin/login_captin_controller.dart';
|
||||
// import 'package:sefer_driver/main.dart';
|
||||
// import 'package:sefer_driver/views/auth/captin/ai_page.dart';
|
||||
// import 'package:sefer_driver/views/auth/captin/cards/sms_signup.dart';
|
||||
// import 'package:sefer_driver/views/home/on_boarding_page.dart';
|
||||
// import 'package:sefer_driver/views/widgets/error_snakbar.dart';
|
||||
// import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
// import '../functions/add_error.dart';
|
||||
|
||||
// /// Helper class to manage Google Sign-In via an external browser and polling.
|
||||
// class GoogleSignInHelper {
|
||||
// // URLs for your server endpoints
|
||||
// static const String _startLoginUrl =
|
||||
// 'https://api.tripz-egypt.com/tripz/auth/google_auth/login.php';
|
||||
// static const String _checkStatusUrl =
|
||||
// 'https://api.tripz-egypt.com/tripz/auth/google_auth/check_status.php';
|
||||
|
||||
// static Future<void> _initiateSignIn(
|
||||
// Function(Map<String, dynamic> userData) onSignInSuccess) async {
|
||||
// try {
|
||||
// // Show a loading dialog to the user
|
||||
// Get.dialog(
|
||||
// const Center(
|
||||
// child: Material(
|
||||
// color: Colors.transparent,
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// CircularProgressIndicator(),
|
||||
// SizedBox(height: 16),
|
||||
// Text(
|
||||
// "Waiting for browser sign-in...",
|
||||
// style: TextStyle(color: Colors.white, fontSize: 16),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// barrierDismissible: false,
|
||||
// );
|
||||
|
||||
// // 1. Get the auth URL and login token from the server
|
||||
// final startResponse = await http.get(Uri.parse(_startLoginUrl));
|
||||
// if (startResponse.statusCode != 200) {
|
||||
// throw Exception('Failed to start login process.');
|
||||
// }
|
||||
|
||||
// final startData = jsonDecode(startResponse.body);
|
||||
// final String authUrl = startData['authUrl'];
|
||||
// final String loginToken = startData['loginToken'];
|
||||
|
||||
// // 2. Launch the URL in an external browser
|
||||
// if (!await launchUrl(Uri.parse(authUrl),
|
||||
// mode: LaunchMode.externalApplication)) {
|
||||
// throw Exception('Could not launch browser.');
|
||||
// }
|
||||
|
||||
// // 3. Start polling the server for status
|
||||
// await _startPolling(loginToken, onSignInSuccess);
|
||||
// } catch (e) {
|
||||
// addError(e.toString(), '_initiateSignIn');
|
||||
// mySnackeBarError('Sign-in failed: ${e.toString()}');
|
||||
// // Close the loading dialog on error
|
||||
// if (Get.isDialogOpen ?? false) {
|
||||
// Get.back();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// static Future<void> _startPolling(String loginToken,
|
||||
// Function(Map<String, dynamic> userData) onSignInSuccess) async {
|
||||
// Timer? poller;
|
||||
// const int maxAttempts = 30; // Poll for 60 seconds (30 attempts * 2s)
|
||||
// int attempts = 0;
|
||||
|
||||
// poller = Timer.periodic(const Duration(seconds: 2), (timer) async {
|
||||
// if (attempts >= maxAttempts) {
|
||||
// timer.cancel();
|
||||
// if (Get.isDialogOpen ?? false) Get.back();
|
||||
// mySnackeBarError('Sign-in timed out. Please try again.');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// attempts++;
|
||||
|
||||
// try {
|
||||
// final statusResponse = await http.post(
|
||||
// Uri.parse(_checkStatusUrl),
|
||||
// headers: {'Content-Type': 'application/json'},
|
||||
// body: jsonEncode({'loginToken': loginToken}),
|
||||
// );
|
||||
|
||||
// if (statusResponse.statusCode == 200) {
|
||||
// final statusData = jsonDecode(statusResponse.body);
|
||||
// if (statusData['status'] == 'success') {
|
||||
// timer.cancel();
|
||||
// if (Get.isDialogOpen ?? false) Get.back();
|
||||
// // Success!
|
||||
// onSignInSuccess(statusData['userData']);
|
||||
// }
|
||||
// // If status is 'pending', do nothing and wait for the next poll.
|
||||
// }
|
||||
// } catch (e) {
|
||||
// // Handle polling errors silently or log them
|
||||
// debugPrint("Polling error: $e");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// /// Triggers the sign-in process for a new user.
|
||||
// static Future<void> signIn() async {
|
||||
// await _initiateSignIn((userData) async {
|
||||
// debugPrint('Sign-in success data: $userData');
|
||||
// await _handleSignUp(userData);
|
||||
// if (box.read(BoxName.countryCode) == 'Egypt') {
|
||||
// Get.offAll(() => SmsSignupEgypt());
|
||||
// } else if (box.read(BoxName.countryCode) == 'Jordan') {
|
||||
// Get.offAll(() => AiPage());
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// /// Triggers the sign-in process for an existing user.
|
||||
// static Future<void> signInFromLogin() async {
|
||||
// await _initiateSignIn((userData) async {
|
||||
// debugPrint('Sign-in from login success data: $userData');
|
||||
// await _handleSignUp(userData);
|
||||
|
||||
// final driverID = userData['id']?.toString() ?? 'Unknown ID';
|
||||
// final emailDriver = userData['email']?.toString() ?? 'Unknown Email';
|
||||
|
||||
// debugPrint('Driver ID from server: $driverID');
|
||||
// debugPrint('Driver Email from server: $emailDriver');
|
||||
|
||||
// await Get.find<LoginDriverController>()
|
||||
// .loginWithGoogleCredential(driverID, emailDriver);
|
||||
// });
|
||||
// }
|
||||
|
||||
// /// Stores user information received from the server.
|
||||
// static Future<void> _handleSignUp(Map<String, dynamic> userData) async {
|
||||
// box.write(BoxName.driverID, userData['id'] ?? 'Unknown ID');
|
||||
// box.write(BoxName.emailDriver, userData['email'] ?? 'Unknown Email');
|
||||
// }
|
||||
|
||||
// /// Clears local data.
|
||||
// static Future<void> signOut() async {
|
||||
// box.remove(BoxName.driverID);
|
||||
// box.remove(BoxName.emailDriver);
|
||||
// box.remove(BoxName.lang);
|
||||
// box.remove(BoxName.nameDriver);
|
||||
// box.remove(BoxName.passengerID);
|
||||
// box.remove(BoxName.phoneDriver);
|
||||
// box.remove(BoxName.tokenFCM);
|
||||
// box.remove(BoxName.tokens);
|
||||
// box.remove(BoxName.carPlate);
|
||||
// box.remove(BoxName.lastNameDriver);
|
||||
// box.remove(BoxName.agreeTerms);
|
||||
// box.remove(BoxName.tokenDriver);
|
||||
// box.remove(BoxName.countryCode);
|
||||
// box.remove(BoxName.accountIdStripeConnect);
|
||||
// box.remove(BoxName.phoneVerified);
|
||||
// Get.offAll(() => OnBoardingPage());
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -65,9 +65,9 @@ class NotificationController extends GetxController {
|
||||
print('✅ Notifications initialized with Action Buttons Support');
|
||||
|
||||
await _flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
|
||||
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
|
||||
settings: initializationSettings,
|
||||
);
|
||||
|
||||
// إنشاء قناة الأندرويد ذات الأهمية القصوى
|
||||
@@ -183,10 +183,10 @@ class NotificationController extends GetxController {
|
||||
|
||||
// عرض الإشعار
|
||||
await _flutterLocalNotificationsPlugin.show(
|
||||
1001, // ID ثابت لاستبدال الإشعار القديم
|
||||
title,
|
||||
"$price - مسافة $formattedBigText", // نص مختصر يظهر في البار العلوي
|
||||
details,
|
||||
id: 1001, // ID ثابت لاستبدال الإشعار القديم
|
||||
title: title,
|
||||
body: "$price - مسافة $formattedBigText", // نص مختصر يظهر في البار العلوي
|
||||
notificationDetails: details,
|
||||
payload: jsonEncode({
|
||||
'type': 'Order',
|
||||
'data': myListString,
|
||||
@@ -215,7 +215,7 @@ class NotificationController extends GetxController {
|
||||
|
||||
// أ) زر القبول
|
||||
if (response.actionId == 'ACCEPT_ORDER') {
|
||||
await _flutterLocalNotificationsPlugin.cancel(1001); // حذف الإشعار
|
||||
await _flutterLocalNotificationsPlugin.cancel(id: 1001); // حذف الإشعار
|
||||
_processAcceptOrder(listData);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ class NotificationController extends GetxController {
|
||||
|
||||
// ج) زر الرفض
|
||||
else if (response.actionId == 'REJECT_ORDER') {
|
||||
await _flutterLocalNotificationsPlugin.cancel(1001); // حذف الإشعار
|
||||
await _flutterLocalNotificationsPlugin.cancel(id: 1001); // حذف الإشعار
|
||||
_processRejectOrder(listData);
|
||||
}
|
||||
|
||||
@@ -428,7 +428,11 @@ class NotificationController extends GetxController {
|
||||
NotificationDetails details =
|
||||
NotificationDetails(android: android, iOS: ios);
|
||||
|
||||
await _flutterLocalNotificationsPlugin.show(0, title, message, details,
|
||||
await _flutterLocalNotificationsPlugin.show(
|
||||
id: 0,
|
||||
title: title,
|
||||
body: message,
|
||||
notificationDetails: details,
|
||||
payload: jsonEncode({'title': title, 'data': payLoad}));
|
||||
}
|
||||
|
||||
@@ -490,7 +494,7 @@ class NotificationController extends GetxController {
|
||||
// ==============================================================================
|
||||
Future<void> cancelOrderNotification() async {
|
||||
// 1001 هو نفس الآيدي الذي استخدمناه عند عرض الإشعار
|
||||
await _flutterLocalNotificationsPlugin.cancel(1001);
|
||||
await _flutterLocalNotificationsPlugin.cancel(id: 1001);
|
||||
print("🗑️ Order Notification Cancelled (Taken by another driver)");
|
||||
}
|
||||
|
||||
@@ -522,14 +526,12 @@ class NotificationController extends GetxController {
|
||||
}
|
||||
|
||||
await _flutterLocalNotificationsPlugin.zonedSchedule(
|
||||
notificationId,
|
||||
title,
|
||||
message,
|
||||
scheduledDate,
|
||||
details,
|
||||
androidScheduleMode: AndroidScheduleMode.exact,
|
||||
uiLocalNotificationDateInterpretation:
|
||||
UILocalNotificationDateInterpretation.absoluteTime,
|
||||
id: notificationId,
|
||||
title: title,
|
||||
body: message,
|
||||
scheduledDate: scheduledDate,
|
||||
notificationDetails: details,
|
||||
androidScheduleMode: AndroidScheduleMode.exact, // أو exactAllowWhileIdle
|
||||
matchDateTimeComponents: null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,10 +45,10 @@ Future<bool> onStart(ServiceInstance service) async {
|
||||
print("✅ Background Service: Socket Connected!");
|
||||
if (service is AndroidServiceInstance) {
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
notificationId,
|
||||
'أنت متصل الآن',
|
||||
'بانتظار الطلبات...',
|
||||
const NotificationDetails(
|
||||
id: notificationId,
|
||||
title: 'أنت متصل الآن',
|
||||
body: 'بانتظار الطلبات...',
|
||||
notificationDetails: const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
notificationChannelId,
|
||||
'خدمة السائق',
|
||||
@@ -107,10 +107,10 @@ Future<bool> onStart(ServiceInstance service) async {
|
||||
if (service is AndroidServiceInstance) {
|
||||
if (await service.isForegroundService()) {
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
notificationId,
|
||||
'خدمة السائق نشطة',
|
||||
'بانتظار الطلبات...',
|
||||
const NotificationDetails(
|
||||
id: notificationId,
|
||||
title: 'خدمة السائق نشطة',
|
||||
body: 'بانتظار الطلبات...',
|
||||
notificationDetails: const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
notificationChannelId,
|
||||
'خدمة السائق',
|
||||
|
||||
@@ -2,10 +2,8 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:jwt_decoder/jwt_decoder.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:sefer_driver/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:sefer_driver/controller/functions/network/net_guard.dart';
|
||||
import 'package:secure_string_operations/secure_string_operations.dart';
|
||||
import 'package:sefer_driver/constant/box_name.dart';
|
||||
import 'package:sefer_driver/constant/links.dart';
|
||||
import 'package:sefer_driver/controller/auth/captin/login_captin_controller.dart';
|
||||
@@ -16,8 +14,6 @@ import 'package:sefer_driver/env/env.dart';
|
||||
import 'package:sefer_driver/print.dart';
|
||||
|
||||
import '../../constant/api_key.dart';
|
||||
import '../../constant/char_map.dart';
|
||||
import '../../constant/info.dart';
|
||||
import '../../views/widgets/error_snakbar.dart';
|
||||
import 'gemeni.dart';
|
||||
import 'upload_image.dart';
|
||||
|
||||
@@ -51,7 +51,7 @@ class DeviceController {
|
||||
final deviceInfo = DeviceInfo(
|
||||
manufacturer: androidInfo.manufacturer,
|
||||
model: androidInfo.model,
|
||||
deviceId: androidInfo.serialNumber,
|
||||
deviceId: androidInfo.id,
|
||||
osVersion: androidInfo.version.release,
|
||||
platform: 'Android',
|
||||
deviceName: androidInfo.device,
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:trip_overlay_plugin/trip_overlay_plugin.dart';
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/links.dart';
|
||||
import '../../main.dart';
|
||||
import '../../print.dart';
|
||||
import '../home/captin/home_captain_controller.dart';
|
||||
import '../home/captin/map_driver_controller.dart';
|
||||
import '../home/payment/captain_wallet_controller.dart';
|
||||
@@ -74,7 +75,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
@override
|
||||
Future<void> onInit() async {
|
||||
super.onInit();
|
||||
print('🚀 LocationController Starting...');
|
||||
Log.print('🚀 LocationController Starting...');
|
||||
|
||||
// 1. Register Lifecycle Observer
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
@@ -83,7 +84,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
// مراقب الحالة (Status Watcher)
|
||||
box.listenKey(BoxName.statusDriverLocation, (value) {
|
||||
if (value == 'blocked') {
|
||||
print("⛔ Driver is Blocked: Force Stopping Location Updates.");
|
||||
Log.print("⛔ Driver is Blocked: Force Stopping Location Updates.");
|
||||
stopLocationUpdates();
|
||||
if (socket != null && socket!.connected) {
|
||||
socket!.emit('update_location', {
|
||||
@@ -113,7 +114,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
await startLocationUpdates();
|
||||
}
|
||||
|
||||
print('✅ LocationController Initialized.');
|
||||
Log.print('✅ LocationController Initialized.');
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -133,7 +134,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
print("📱 Lifecycle: App is in FOREGROUND");
|
||||
Log.print("📱 Lifecycle: App is in FOREGROUND");
|
||||
box.write(BoxName.isAppInForeground, true);
|
||||
|
||||
// إيقاف خدمة الخلفية لأننا في الواجهة الآن
|
||||
@@ -141,7 +142,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// التأكد من أن السوكيت متصل، وإذا لا، نعيد الاتصال فوراً
|
||||
if (socket == null || !socket!.connected) {
|
||||
print("🔄 Socket disconnected in background. Reconnecting now...");
|
||||
Log.print("🔄 Socket disconnected in background. Reconnecting now...");
|
||||
initSocket();
|
||||
} else {
|
||||
// إذا كان متصلاً، ننعش المستمعين فقط للتأكد
|
||||
@@ -149,7 +150,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
} else if (state == AppLifecycleState.paused ||
|
||||
state == AppLifecycleState.detached) {
|
||||
print("📱 Lifecycle: App is in BACKGROUND");
|
||||
Log.print("📱 Lifecycle: App is in BACKGROUND");
|
||||
box.write(BoxName.isAppInForeground, false);
|
||||
|
||||
// تشغيل خدمة الخلفية لضمان بقاء التطبيق حياً
|
||||
@@ -186,14 +187,15 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
// 1. إذا كان السوكيت موجوداً، فقط تأكد من اتصاله
|
||||
if (socket != null) {
|
||||
if (!socket!.connected) {
|
||||
print("🟡 Socket exists but disconnected. Reconnecting...");
|
||||
Log.print("🟡 Socket exists but disconnected. Reconnecting...");
|
||||
socket!.connect();
|
||||
}
|
||||
_setupSocketListeners(); // تحديث المستمعين
|
||||
return;
|
||||
}
|
||||
|
||||
print("🟡 [LocationController] Creating NEW Socket for Driver: $driverId");
|
||||
Log.print(
|
||||
"🟡 [LocationController] Creating NEW Socket for Driver: $driverId");
|
||||
|
||||
// 2. إنشاء الاتصال
|
||||
socket = IO.io(
|
||||
@@ -222,20 +224,27 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
socket!.off('ride_cancelled');
|
||||
|
||||
socket!.onConnect((_) {
|
||||
print('✅ Socket Connected! ID: ${socket?.id}');
|
||||
Log.print('✅ Socket Connected! ID: ${socket?.id}');
|
||||
isSocketConnected = true;
|
||||
_startHeartbeat();
|
||||
});
|
||||
|
||||
socket!.onDisconnect((_) {
|
||||
print('❌ Socket Disconnected');
|
||||
Log.print('❌ Socket Disconnected');
|
||||
isSocketConnected = false;
|
||||
_stopHeartbeat();
|
||||
});
|
||||
socket!.onConnectError((err) {
|
||||
Log.print('❌ Socket Connect Error: $err');
|
||||
});
|
||||
|
||||
socket!.onError((err) {
|
||||
Log.print('❌ Socket General Error: $err');
|
||||
});
|
||||
|
||||
// 🔥 الاستماع للطلبات الجديدة
|
||||
socket!.on('new_ride_request', (data) {
|
||||
print("🔔 Socket: New Ride Request Arrived!");
|
||||
Log.print("🔔 Socket: New Ride Request Arrived!");
|
||||
|
||||
// نستخدم Future.microtask لضمان عدم حظر الـ UI Thread
|
||||
Future.microtask(() {
|
||||
@@ -259,7 +268,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
handleIncomingOrder(convertedMap, "Socket");
|
||||
}
|
||||
} catch (e) {
|
||||
print("❌ Error processing socket data: $e");
|
||||
Log.print("❌ Error processing socket data: $e");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -267,7 +276,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 🔥 الاستماع للإلغاء
|
||||
socket!.on('cancel_ride', (data) {
|
||||
print("🚫 Socket: Ride Cancelled Event Received");
|
||||
Log.print("🚫 Socket: Ride Cancelled Event Received");
|
||||
String reason = data['reason'] ?? 'No reason provided';
|
||||
if (Get.isRegistered<MapDriverController>()) {
|
||||
Get.find<MapDriverController>()
|
||||
@@ -280,13 +289,13 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
Future<void> handleIncomingOrder(
|
||||
Map<String, dynamic> rideData, String source) async {
|
||||
print("📦 Socket Order Received from ($source)");
|
||||
Log.print("📦 Socket Order Received from ($source)");
|
||||
|
||||
// 🔴 1. التحقق من حالة التطبيق قبل أي شيء 🔴
|
||||
bool isAppInForeground = box.read(BoxName.isAppInForeground) ?? false;
|
||||
|
||||
if (!isAppInForeground) {
|
||||
print(
|
||||
Log.print(
|
||||
"🛑 التطبيق في الخلفية. السوكيت سيتجاهل التوجيه ويترك المهمة للـ Overlay.");
|
||||
return; // 👈 هذا السطر يمنع السوكيت من إكمال العمل وفتح الصفحة
|
||||
}
|
||||
@@ -294,7 +303,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
try {
|
||||
// 2. التحقق من صحة البيانات
|
||||
if (rideData.isEmpty || !rideData.containsKey('16')) {
|
||||
print("❌ Socket Error: Invalid Ride Data.");
|
||||
Log.print("❌ Socket Error: Invalid Ride Data.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -311,25 +320,26 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
// 4. إغلاق النافذة (إن وجدت بالخطأ) والتنقل
|
||||
try {
|
||||
if (await TripOverlayPlugin.isOverlayActive()) {
|
||||
print("📲 Closing Overlay because App took control via Socket");
|
||||
Log.print("📲 Closing Overlay because App took control via Socket");
|
||||
await TripOverlayPlugin.hideOverlay();
|
||||
}
|
||||
} catch (e) {
|
||||
print("Overlay check error: $e");
|
||||
Log.print("Overlay check error: $e");
|
||||
}
|
||||
|
||||
if (Get.currentRoute != '/OrderRequestPage') {
|
||||
print("🚀 Socket: Navigating to OrderRequestPage...");
|
||||
Log.print("🚀 Socket: Navigating to OrderRequestPage...");
|
||||
Get.toNamed('/OrderRequestPage', arguments: {
|
||||
'myListString': jsonEncode(driverList),
|
||||
'DriverList': driverList,
|
||||
'body': 'New Trip Request via Socket ⚡'
|
||||
});
|
||||
} else {
|
||||
print("⚠️ User is already on OrderRequestPage. Skipping navigation.");
|
||||
Log.print(
|
||||
"⚠️ User is already on OrderRequestPage. Skipping navigation.");
|
||||
}
|
||||
} catch (e) {
|
||||
print("❌ Socket Navigation Error: $e");
|
||||
Log.print("❌ Socket Navigation Error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,8 +384,8 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
// Debug print to verify
|
||||
// print('🚀 Emitting Location: $payload');
|
||||
// DebugLog.print to verify
|
||||
//Log.print('🚀 Emitting Location: $payload');
|
||||
|
||||
if (socket != null && socket!.connected) {
|
||||
socket!.emit('update_location', payload);
|
||||
@@ -437,11 +447,11 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
update();
|
||||
emitLocationToSocket(pos, heading, speed);
|
||||
await _saveBehaviorIfMoved(pos, now, currentSpeed: speed);
|
||||
}, onError: (e) => print('❌ Location Stream Error: $e'));
|
||||
}, onError: (e) => Log.print('❌ Location Stream Error: $e'));
|
||||
}
|
||||
|
||||
Future<void> stopLocationUpdates() async {
|
||||
print("🛑 Stopping Location Updates...");
|
||||
Log.print("🛑 Stopping Location Updates...");
|
||||
|
||||
_locSub?.cancel();
|
||||
_locSub = null;
|
||||
@@ -525,7 +535,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
payload: {'driver_id': driverId, 'batch_data': jsonEncode(batch)},
|
||||
);
|
||||
} catch (e) {
|
||||
print('❌ Failed to upload batch: $e');
|
||||
Log.print('❌ Failed to upload batch: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +569,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
}, TableName.behavior);
|
||||
_lastSqlLoc = pos;
|
||||
} catch (e) {
|
||||
print('SQLite Error: $e');
|
||||
Log.print('SQLite Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,7 +604,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
interval: 1000,
|
||||
distanceFilter: 10);
|
||||
} catch (e) {
|
||||
print("Warning: $e");
|
||||
Log.print("Warning: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -633,7 +643,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
return await location.getLocation();
|
||||
}
|
||||
} catch (e) {
|
||||
print('❌ FAILED to get single location: $e');
|
||||
Log.print('❌ FAILED to get single location: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class NetGuard {
|
||||
// اختباري خفيف عبر TCP (80/443) — 400ms timeout
|
||||
final port = mustReach.scheme == 'http' ? 80 : 443;
|
||||
final socket = await Socket.connect(host, port,
|
||||
timeout: const Duration(milliseconds: 400));
|
||||
timeout: const Duration(seconds: 1));
|
||||
socket.destroy();
|
||||
} catch (_) {
|
||||
return false;
|
||||
|
||||
@@ -433,14 +433,58 @@ class HomeCaptainController extends GetxController {
|
||||
return totalDuration;
|
||||
}
|
||||
|
||||
void startPeriodicExecution() {
|
||||
Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||||
await getCaptainDurationOnToday();
|
||||
Timer? _localDurationTimer;
|
||||
RxString totalDurationDisplay = "00:00:00".obs; // لعرض الوقت في الواجهة
|
||||
Duration _currentDuration = Duration.zero; // لتخزين الوقت ككائن Duration
|
||||
|
||||
void startPeriodicExecution() async {
|
||||
await getCaptainDurationOnToday();
|
||||
String? initialDurationStr = totalDurationToday;
|
||||
|
||||
if (initialDurationStr != null) {
|
||||
// تحويل النص (01:20:30) إلى كائن Duration
|
||||
List<String> parts = initialDurationStr.split(':');
|
||||
_currentDuration = Duration(
|
||||
hours: int.parse(parts[0]),
|
||||
minutes: int.parse(parts[1]),
|
||||
seconds: int.parse(parts[2]),
|
||||
);
|
||||
|
||||
// بدء العداد المحلي
|
||||
_startLocalClock();
|
||||
}
|
||||
|
||||
// Timer.periodic(const Duration(seconds: 30), (timer) async {
|
||||
// await getCaptainDurationOnToday();
|
||||
// });
|
||||
}
|
||||
|
||||
void _startLocalClock() {
|
||||
_localDurationTimer?.cancel();
|
||||
_localDurationTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
// زيادة ثانية واحدة محلياً
|
||||
_currentDuration += const Duration(seconds: 1);
|
||||
|
||||
// تحديث النص المعروض في الواجهة (Formatting)
|
||||
totalDurationDisplay.value = _formatDuration(_currentDuration);
|
||||
|
||||
// اختيارياً: كل 5 دقائق فقط، قم بتحديث القيمة من السيرفر للتأكد من المزامنة
|
||||
if (timer.tick % 300 == 0) {
|
||||
getCaptainDurationOnToday();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||
String hours = twoDigits(duration.inHours);
|
||||
String minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
String seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return "$hours:$minutes:$seconds";
|
||||
}
|
||||
|
||||
void stopTimer() {
|
||||
timer?.cancel();
|
||||
_localDurationTimer?.cancel();
|
||||
}
|
||||
|
||||
getlocation() async {
|
||||
|
||||
@@ -22,11 +22,11 @@ class PaymobPayout extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
// var dec = await CRUD()
|
||||
// .postWallet(link: AppLink.paymobPayoutDriverWallet, payload: {
|
||||
@@ -116,11 +116,11 @@ class PaymobPayout extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
var body = {
|
||||
"issuer": "bank_card",
|
||||
@@ -187,11 +187,11 @@ class PaymobPayout extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
await payToDriverWallet(amount, issuer, msisdn);
|
||||
} else {
|
||||
@@ -213,11 +213,11 @@ class PaymobPayout extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
await payToDriverBankAccount(amount, bankCardNumber, bankCode);
|
||||
} else {
|
||||
|
||||
@@ -438,11 +438,11 @@ class PaymentController extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
final PaymobResponse? response = await PaymobPayment.instance.pay(
|
||||
context: context,
|
||||
@@ -589,11 +589,12 @@ class PaymentController extends GetxController {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason: 'Use Touch ID or Face ID to confirm payment',
|
||||
// options: AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
// )
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
final PaymobResponseWallet? response =
|
||||
await PaymobPaymentWallet.instance.pay(
|
||||
|
||||
@@ -32,10 +32,10 @@ class _PayoutScreenState extends State<PayoutScreen> {
|
||||
// 1. طلب المصادقة البيومترية
|
||||
bool didAuthenticate = await _localAuth.authenticate(
|
||||
localizedReason: 'استخدم بصمة الإصبع لتأكيد عملية السحب',
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
),
|
||||
// options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
// ),
|
||||
);
|
||||
|
||||
if (didAuthenticate && mounted) {
|
||||
|
||||
@@ -310,12 +310,12 @@ class WalletCaptainRefactored extends StatelessWidget {
|
||||
if (isAvailable) {
|
||||
// Authenticate the user
|
||||
bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
localizedReason:
|
||||
'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
));
|
||||
localizedReason:
|
||||
'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
// options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
sensitiveTransaction: true,
|
||||
);
|
||||
if (didAuthenticate) {
|
||||
if (double.parse(controller.amountFromBudgetController.text) <
|
||||
double.parse(controller.totalAmountVisa)) {
|
||||
|
||||
Reference in New Issue
Block a user