Update: 2026-06-12 01:23:54
This commit is contained in:
@@ -279,9 +279,7 @@ class RegisterCaptainController extends GetxController {
|
||||
// box.read(BoxName.driverID).toString(),
|
||||
// box.read(BoxName.emailDriver).toString(),
|
||||
// );
|
||||
// Get.offAll(() => SyrianCardAI());
|
||||
Get.to(() => RegistrationView());
|
||||
// } else {
|
||||
// Get.snackbar('title', 'message');
|
||||
// }
|
||||
}
|
||||
@@ -313,7 +311,7 @@ class RegisterCaptainController extends GetxController {
|
||||
if (formKey.currentState!.validate()) {
|
||||
isLoading = true;
|
||||
update();
|
||||
Get.to(() => AiPage());
|
||||
Get.to(() => RegistrationView());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import 'package:siro_driver/constant/box_name.dart';
|
||||
import 'package:siro_driver/controller/auth/captin/login_captin_controller.dart';
|
||||
import 'package:siro_driver/controller/functions/crud.dart';
|
||||
import 'package:siro_driver/main.dart';
|
||||
import 'package:siro_driver/views/auth/captin/cards/sms_signup.dart';
|
||||
import 'package:siro_driver/views/home/on_boarding_page.dart';
|
||||
import 'package:siro_driver/views/widgets/error_snakbar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
|
||||
import '../../views/auth/captin/ai_page.dart';
|
||||
import '../functions/add_error.dart';
|
||||
import '../functions/encrypt_decrypt.dart';
|
||||
|
||||
class GoogleSignInHelper {
|
||||
static final GoogleSignIn _googleSignIn = GoogleSignIn.instance;
|
||||
|
||||
// متغير ثابت لحفظ حالة المستخدم محلياً كبديل لخاصية currentUser المحذوفة
|
||||
static GoogleSignInAccount? _cachedUser;
|
||||
|
||||
static Future<GoogleSignInAccount?> signIn() async {
|
||||
try {
|
||||
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') {
|
||||
Get.to(() => AiPage());
|
||||
}
|
||||
}
|
||||
return googleUser;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<GoogleSignInAccount?> signInFromLogin() async {
|
||||
try {
|
||||
final GoogleSignInAccount? googleUser =
|
||||
await _googleSignIn.authenticate();
|
||||
|
||||
if (googleUser != null) {
|
||||
_cachedUser = googleUser; // حفظ الجلسة في الكاش المحلي
|
||||
await _handleSignUp(googleUser);
|
||||
|
||||
final driverID =
|
||||
(box.read(BoxName.driverID)?.toString()) ?? 'Unknown ID';
|
||||
final emailDriver =
|
||||
(box.read(BoxName.emailDriver)?.toString()) ?? 'Unknown Email';
|
||||
|
||||
print('Driver ID: $driverID');
|
||||
print('Driver Email: $emailDriver');
|
||||
|
||||
await Get.find<LoginDriverController>()
|
||||
.loginWithGoogleCredential(driverID, emailDriver);
|
||||
}
|
||||
|
||||
return googleUser;
|
||||
} catch (error, stackTrace) {
|
||||
mySnackeBarError('$error');
|
||||
CRUD.addError(error.toString(), stackTrace.toString(),
|
||||
'GoogleSignInAccount?> signInFromLogin()');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _handleSignUp(GoogleSignInAccount user) async {
|
||||
box.write(BoxName.driverID, (user.id) ?? 'Unknown ID');
|
||||
box.write(BoxName.emailDriver, (user.email) ?? 'Unknown Email');
|
||||
}
|
||||
|
||||
static Future<void> signOut() async {
|
||||
try {
|
||||
await _googleSignIn.signOut();
|
||||
} catch (error) {
|
||||
// التعامل مع الخطأ بصمت إذا كانت جلسة جوجل فارغة مسبقاً
|
||||
} finally {
|
||||
_cachedUser = null; // مسح الكاش المحلي بشكل إلزامي
|
||||
await _handleSignOut();
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _handleSignOut() async {
|
||||
box.erase();
|
||||
storage.deleteAll();
|
||||
Get.offAll(OnBoardingPage());
|
||||
}
|
||||
|
||||
// استخدام الكاش المحلي بدلاً من استدعاء المكتبة
|
||||
static GoogleSignInAccount? getCurrentUser() {
|
||||
return _cachedUser;
|
||||
}
|
||||
}
|
||||
@@ -264,12 +264,23 @@ class RegistrationController extends GetxController {
|
||||
|
||||
/// خريطة لتخزين روابط المستندات بعد الرفع
|
||||
final Map<String, String> docUrls = {
|
||||
'driver_license_front': '',
|
||||
'id_front': '',
|
||||
'id_back': '',
|
||||
'driver_license': '',
|
||||
'driver_license_back': '',
|
||||
'profile_picture': '',
|
||||
'criminal_record': '',
|
||||
'car_license_front': '',
|
||||
'car_license_back': '',
|
||||
};
|
||||
|
||||
String getCriminalRecordTitle() {
|
||||
String currentCountry = box.read(BoxName.countryCode) ?? 'Jordan';
|
||||
if (currentCountry == 'Syria') return 'لا حكم عليه'.tr;
|
||||
if (currentCountry == 'Egypt') return 'فيش وتشبيه'.tr;
|
||||
return 'عدم محكومية'.tr;
|
||||
}
|
||||
|
||||
/// التصرّف العام لاختيار/قص/ضغط/رفع الصورة حسب type
|
||||
Future<void> choosImage(String link, String imageType) async {
|
||||
try {
|
||||
@@ -329,58 +340,66 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String
|
||||
/// ترفع الملف وترجع رابط الصورة النهائي كـ String مع إعادة المحاولة في حال فشل الاتصال
|
||||
Future<String> uploadImage(
|
||||
File file, Map<String, String> data, String link) async {
|
||||
final uri = Uri.parse(link);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
int maxRetries = 3;
|
||||
int attempt = 0;
|
||||
while (attempt < maxRetries) {
|
||||
attempt++;
|
||||
try {
|
||||
final uri = Uri.parse(link);
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
|
||||
// الهيدرز (كما عندك)
|
||||
final headers = <String, String>{
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
final headers = <String, String>{
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
|
||||
// اسم الملف: driverID.jpg (اختياري)
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
final forcedName = '${box.read(BoxName.driverID) ?? 'image'}.jpg';
|
||||
|
||||
// إضافة الملف (من المسار مباشرة أسلم من الـ stream)
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image', // تأكد أنه نفس اسم الحقل على السيرفر
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'image',
|
||||
file.path,
|
||||
filename: forcedName,
|
||||
),
|
||||
);
|
||||
|
||||
// الحقول الإضافية
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
data.forEach((k, v) => request.fields[k] = v);
|
||||
|
||||
// الإرسال
|
||||
final streamed = await request.send();
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
// المهلة الزمنية 120 ثانية لتناسب الاتصالات الضعيفة
|
||||
final streamed = await request.send().timeout(const Duration(seconds: 120));
|
||||
final res = await http.Response.fromStream(streamed);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to upload image: ${res.statusCode} - ${res.body}');
|
||||
}
|
||||
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
Log.print("⚠️ [Image Upload Attempt $attempt Failed] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
}
|
||||
}
|
||||
|
||||
// نحاول استخراج رابط الصورة من أكثر من مفتاح محتمل
|
||||
final body = jsonDecode(res.body);
|
||||
final String? url = body['url'] ??
|
||||
body['file_link'] ??
|
||||
body['image_url'] ??
|
||||
(body['data'] is Map ? body['data']['url'] : null);
|
||||
|
||||
if (url == null || url.isEmpty) {
|
||||
// لو السيرفر يرجع هيكل مختلف، عدّل هنا المفتاح حسب استجابتك الفعلية
|
||||
throw Exception(
|
||||
'Upload succeeded but no image URL found in response: ${res.body}');
|
||||
}
|
||||
|
||||
return url;
|
||||
throw Exception('Upload failed after $maxRetries attempts');
|
||||
}
|
||||
|
||||
Future<File> compressImage(File file) async {
|
||||
@@ -398,7 +417,7 @@ class RegistrationController extends GetxController {
|
||||
return File(result!.path);
|
||||
}
|
||||
|
||||
// دالة رفع إلى السيرفر السوري: ترجع file_url (Signed URL)
|
||||
// دالة رفع إلى السيرفر السوري مع مهلة 120 ثانية وإعادة محاولة تلقائية في حال الفشل
|
||||
Future<String> uploadToSyria({
|
||||
required String docType,
|
||||
required File file,
|
||||
@@ -406,79 +425,71 @@ class RegistrationController extends GetxController {
|
||||
required String authHeader,
|
||||
required String hmacHeader,
|
||||
required String driverId,
|
||||
Duration timeout = const Duration(seconds: 60),
|
||||
Duration timeout = const Duration(seconds: 120),
|
||||
http.Client? clientOverride,
|
||||
}) async {
|
||||
final client = clientOverride ?? http.Client();
|
||||
try {
|
||||
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||
final parts = mime.split('/');
|
||||
|
||||
final req = http.MultipartRequest('POST', syrianUploadUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': authHeader,
|
||||
'X-HMAC-Auth': hmacHeader,
|
||||
});
|
||||
|
||||
req.fields['driver_id'] = driverId;
|
||||
req.fields['doc_type'] = docType;
|
||||
|
||||
req.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'file',
|
||||
file.path,
|
||||
filename: p.basename(file.path),
|
||||
contentType: MediaType(parts.first, parts.last),
|
||||
),
|
||||
);
|
||||
|
||||
// ====== الطباعة قبل الإرسال ======
|
||||
// Log.print('--- Syrian Upload Request ---');
|
||||
// Log.print('URL: $syrianUploadUri');
|
||||
// // Log.print('Method: POST');
|
||||
// // Log.print('Headers: ${req.headers}');
|
||||
// Log.print('Fields: ${req.fields}');
|
||||
// // Log.print(
|
||||
// // 'File: ${file.path} (${await file.length()} bytes, mime: $mime)');
|
||||
// Log.print('-----------------------------');
|
||||
|
||||
// الإرسال
|
||||
final streamed = await client.send(req).timeout(timeout);
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
// ====== الطباعة بعد الاستجابة ======
|
||||
// Log.print('--- Syrian Upload Response ---');
|
||||
Log.print('Status: ${resp.statusCode}');
|
||||
// Log.print('Headers: ${resp.headers}');
|
||||
// Log.print('Body: ${resp.body}');
|
||||
// Log.print('-------------------------------');
|
||||
|
||||
Map<String, dynamic> j = {};
|
||||
int maxRetries = 3;
|
||||
int attempt = 0;
|
||||
while (attempt < maxRetries) {
|
||||
attempt++;
|
||||
final client = clientOverride ?? http.Client();
|
||||
try {
|
||||
j = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||
final parts = mime.split('/');
|
||||
|
||||
final req = http.MultipartRequest('POST', syrianUploadUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': authHeader,
|
||||
'X-HMAC-Auth': hmacHeader,
|
||||
});
|
||||
|
||||
req.fields['driver_id'] = driverId;
|
||||
req.fields['doc_type'] = docType;
|
||||
|
||||
req.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'file',
|
||||
file.path,
|
||||
filename: p.basename(file.path),
|
||||
contentType: MediaType(parts.first, parts.last),
|
||||
),
|
||||
);
|
||||
|
||||
final streamed = await client.send(req).timeout(timeout);
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
Log.print('--- Syrian Upload Response (Attempt $attempt) ---');
|
||||
Log.print('Status: ${resp.statusCode}');
|
||||
|
||||
Map<String, dynamic> j = {};
|
||||
try {
|
||||
j = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
Log.print('⚠️ Failed to parse JSON: $e');
|
||||
}
|
||||
|
||||
final statusOk = j['status'] == 'success';
|
||||
final fileUrl = (j['file_url'] ?? j['message']?['file_url'])?.toString();
|
||||
|
||||
if (resp.statusCode == 200 &&
|
||||
statusOk &&
|
||||
(fileUrl?.isNotEmpty ?? false)) {
|
||||
return fileUrl!;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'❌ Syrian upload failed ($docType): ${j['message'] ?? resp.body}');
|
||||
} catch (e) {
|
||||
Log.print('⚠️ Failed to parse JSON: $e');
|
||||
Log.print("⚠️ [Syria Upload Attempt $attempt Failed] Error: $e");
|
||||
if (attempt >= maxRetries) {
|
||||
rethrow;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: attempt * 2));
|
||||
} finally {
|
||||
if (clientOverride == null) client.close();
|
||||
}
|
||||
|
||||
// التحمّل لشكلين من الـ JSON:
|
||||
final statusOk = j['status'] == 'success';
|
||||
final fileUrl = (j['file_url'] ?? j['message']?['file_url'])?.toString();
|
||||
final fileName =
|
||||
(j['file_name'] ?? j['message']?['file_name'])?.toString();
|
||||
|
||||
if (resp.statusCode == 200 &&
|
||||
statusOk &&
|
||||
(fileUrl?.isNotEmpty ?? false)) {
|
||||
// Log.print(
|
||||
// '✅ Syrian upload success: $fileUrl (file: ${fileName ?? "-"})');
|
||||
return fileUrl!;
|
||||
}
|
||||
|
||||
throw Exception(
|
||||
'❌ Syrian upload failed ($docType): ${j['message'] ?? resp.body}');
|
||||
} finally {
|
||||
if (clientOverride == null) client.close();
|
||||
}
|
||||
throw Exception('Syrian upload failed after $maxRetries attempts');
|
||||
}
|
||||
|
||||
Future<void> submitRegistration() async {
|
||||
@@ -490,19 +501,24 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
// 1) تحقق من وجود الروابط
|
||||
final driverFrontUrl = docUrls['driver_license_front'];
|
||||
final driverBackUrl = docUrls['driver_license_back'];
|
||||
final idFrontUrl = docUrls['id_front'];
|
||||
final idBackUrl = docUrls['id_back'];
|
||||
final driverLicenseUrl = docUrls['driver_license'];
|
||||
final driverLicenseBackUrl = docUrls['driver_license_back'];
|
||||
final profilePicUrl = docUrls['profile_picture'];
|
||||
final criminalRecordUrl = docUrls['criminal_record'];
|
||||
final carFrontUrl = docUrls['car_license_front'];
|
||||
final carBackUrl = docUrls['car_license_back'];
|
||||
Log.print(driverFrontUrl.toString());
|
||||
Log.print(driverBackUrl.toString());
|
||||
Log.print(carFrontUrl.toString());
|
||||
Log.print(carBackUrl.toString());
|
||||
|
||||
if (driverFrontUrl == null ||
|
||||
driverBackUrl == null ||
|
||||
carFrontUrl == null ||
|
||||
carBackUrl == null) {
|
||||
final isSyria = box.read(BoxName.countryCode) == 'Syria';
|
||||
|
||||
if (idFrontUrl == null || idFrontUrl.isEmpty ||
|
||||
idBackUrl == null || idBackUrl.isEmpty ||
|
||||
driverLicenseUrl == null || driverLicenseUrl.isEmpty ||
|
||||
(isSyria && (driverLicenseBackUrl == null || driverLicenseBackUrl.isEmpty)) ||
|
||||
profilePicUrl == null || profilePicUrl.isEmpty ||
|
||||
carFrontUrl == null || carFrontUrl.isEmpty ||
|
||||
carBackUrl == null || carBackUrl.isEmpty) {
|
||||
mySnackbarWarning('Please wait for all documents to finish uploading before registering.'.tr);
|
||||
return;
|
||||
}
|
||||
@@ -607,10 +623,14 @@ class RegistrationController extends GetxController {
|
||||
}
|
||||
|
||||
// --- روابط الصور ---
|
||||
_addField(fields, 'driver_license_front', driverFrontUrl!);
|
||||
_addField(fields, 'driver_license_back', driverBackUrl!);
|
||||
_addField(fields, 'car_license_front', carFrontUrl!);
|
||||
_addField(fields, 'car_license_back', carBackUrl!);
|
||||
_addField(fields, 'id_front', idFrontUrl);
|
||||
_addField(fields, 'id_back', idBackUrl);
|
||||
_addField(fields, 'driver_license', driverLicenseUrl);
|
||||
if (isSyria) _addField(fields, 'driver_license_back', driverLicenseBackUrl);
|
||||
_addField(fields, 'profile_picture', profilePicUrl);
|
||||
_addField(fields, 'criminal_record', criminalRecordUrl);
|
||||
_addField(fields, 'car_license_front', carFrontUrl);
|
||||
_addField(fields, 'car_license_back', carBackUrl);
|
||||
|
||||
req.fields.addAll(fields);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user