2026-04-03-pre map libra

This commit is contained in:
Hamza-Ayed
2026-04-04 00:12:33 +03:00
parent 0376a835ce
commit cbb1620874
70 changed files with 933 additions and 787 deletions

View File

@@ -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 {

View File

@@ -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());
// }
// }