287 lines
9.6 KiB
Dart
287 lines
9.6 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:math';
|
|
|
|
import 'package:Tripz/constant/colors.dart';
|
|
import 'package:Tripz/controller/auth/login_controller.dart';
|
|
import 'package:Tripz/controller/functions/add_error.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:Tripz/constant/links.dart';
|
|
import 'package:Tripz/constant/style.dart';
|
|
import 'package:Tripz/controller/functions/crud.dart';
|
|
|
|
import '../../constant/box_name.dart';
|
|
import '../../main.dart';
|
|
import '../../print.dart';
|
|
import '../../views/auth/verify_email_page.dart';
|
|
|
|
class RegisterController extends GetxController {
|
|
// --- Keys ---
|
|
final formKey = GlobalKey<FormState>(); // For main registration form
|
|
final formKey3 = GlobalKey<FormState>(); // For OTP verification form
|
|
|
|
// --- Text Controllers ---
|
|
TextEditingController firstNameController = TextEditingController();
|
|
TextEditingController lastNameController = TextEditingController();
|
|
TextEditingController emailController = TextEditingController();
|
|
TextEditingController phoneController = TextEditingController();
|
|
TextEditingController passwordController = TextEditingController();
|
|
TextEditingController siteController = TextEditingController();
|
|
TextEditingController verifyCode = TextEditingController();
|
|
|
|
// --- State Management ---
|
|
bool isLoading = false;
|
|
bool isOtpSent = false;
|
|
int remainingTime = 300; // 5 minutes in seconds
|
|
Timer? _timer;
|
|
|
|
// --- User Data ---
|
|
String birthDate = 'Birth Date'.tr;
|
|
String gender = 'Male'.tr;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_timer?.cancel();
|
|
super.onClose();
|
|
}
|
|
|
|
void _startTimer() {
|
|
_timer?.cancel(); // Cancel any existing timer
|
|
remainingTime = 300;
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (remainingTime > 0) {
|
|
remainingTime--;
|
|
update(); // Update UI to show countdown
|
|
} else {
|
|
timer.cancel();
|
|
isOtpSent = false;
|
|
update(); // Update UI to allow resend
|
|
}
|
|
});
|
|
}
|
|
|
|
/// STEP 1: Send OTP to the user's phone number via SMS or WhatsApp.
|
|
/// This function ONLY sends the OTP. It does NOT register the user.
|
|
Future<void> sendOtpMessage() async {
|
|
final String phoneNumber = phoneController.text.trim();
|
|
if (phoneNumber.isEmpty) {
|
|
Get.snackbar('Error'.tr, 'Please enter a phone number.'.tr,
|
|
backgroundColor: AppColor.redColor);
|
|
return;
|
|
}
|
|
|
|
// A simple check for general phone number validity (can be improved)
|
|
if (phoneNumber.length < 10) {
|
|
Get.snackbar('Error'.tr, 'The phone number seems too short.'.tr,
|
|
backgroundColor: AppColor.redColor);
|
|
return;
|
|
}
|
|
|
|
isLoading = true;
|
|
update();
|
|
|
|
try {
|
|
// Determine if the number is Egyptian to choose the sending method.
|
|
// International format: +20 followed by 10 digits (total 13 chars)
|
|
// Local format: 11 digits starting with 010, 011, 012, or 015.
|
|
final bool isEgyptian =
|
|
(phoneNumber.startsWith('+20') && phoneNumber.length == 13) ||
|
|
(RegExp(r'^01[0125]\d{8}$').hasMatch(phoneNumber));
|
|
|
|
final String method = isEgyptian ? 'sms' : 'whatsapp';
|
|
final String successMessage = isEgyptian
|
|
? 'An OTP has been sent to your phone via SMS.'.tr
|
|
: 'An OTP has been sent to your phone via WhatsApp.'.tr;
|
|
|
|
Log.print(
|
|
'Phone number identified as ${isEgyptian ? "Egyptian" : "International"}. Attempting to send OTP via $method.');
|
|
|
|
// API call to your backend to send the OTP.
|
|
// We assume the backend uses the 'method' parameter to decide how to send the OTP.
|
|
// If your backend uses different endpoints, you can change the 'link' parameter instead.
|
|
var response = await CRUD().post(
|
|
link: AppLink.sendVerifyOtpMessage,
|
|
payload: {
|
|
'phone_number': phoneNumber,
|
|
'method': method, // 'sms' or 'whatsapp'
|
|
},
|
|
);
|
|
|
|
Log.print('Send OTP Response: $response');
|
|
|
|
if (response != 'failure') {
|
|
isOtpSent = true;
|
|
_startTimer();
|
|
// After successfully sending the OTP, you should navigate to the
|
|
// OTP verification screen. Example:
|
|
// Get.to(() => OTPScreen());
|
|
Get.snackbar('Success'.tr, successMessage,
|
|
backgroundColor: AppColor.greenColor);
|
|
} else {
|
|
Get.snackbar('Error'.tr, 'Failed to send OTP. Please try again.'.tr,
|
|
backgroundColor: AppColor.redColor);
|
|
}
|
|
} catch (e) {
|
|
addError(e.toString(), 'passenger send OTP');
|
|
Get.snackbar('Error'.tr, 'An unexpected error occurred.'.tr,
|
|
backgroundColor: Colors.redAccent);
|
|
} finally {
|
|
isLoading = false;
|
|
update();
|
|
}
|
|
}
|
|
|
|
/// STEP 2: Verify the entered OTP and, if successful, register the user.
|
|
Future<void> verifyOtpAndRegister() async {
|
|
if (formKey3.currentState?.validate() != true) {
|
|
Get.snackbar('Error'.tr, "Please enter the verification code.".tr,
|
|
backgroundColor: AppColor.redColor);
|
|
return;
|
|
}
|
|
|
|
isLoading = true;
|
|
update();
|
|
|
|
try {
|
|
// First, verify the OTP with the backend
|
|
var verificationResponse = await CRUD().post(
|
|
link: AppLink.verifyOtpPassenger,
|
|
payload: {
|
|
'phone_number': phoneController.text.trim(),
|
|
'token': verifyCode.text.trim(),
|
|
},
|
|
);
|
|
|
|
if (verificationResponse != 'failure') {
|
|
// --- OTP VERIFICATION SUCCESSFUL ---
|
|
// Now, proceed with user registration.
|
|
Log.print("OTP Verified. Proceeding to registration.");
|
|
await _registerVerifiedUser();
|
|
} else {
|
|
// --- OTP VERIFICATION FAILED ---
|
|
Get.snackbar('Error'.tr, "Invalid verification code.".tr,
|
|
backgroundColor: Colors.redAccent);
|
|
}
|
|
} catch (e) {
|
|
addError(e.toString(), 'passenger verify OTP & sign up');
|
|
Get.snackbar('Error'.tr, "Something went wrong. Please try again.".tr,
|
|
backgroundColor: Colors.redAccent);
|
|
} finally {
|
|
isLoading = false;
|
|
update();
|
|
}
|
|
}
|
|
|
|
/// Private helper function to handle the actual registration logic.
|
|
/// This should ONLY be called after a successful OTP verification.
|
|
Future<void> _registerVerifiedUser() async {
|
|
try {
|
|
box.write(BoxName.phone, (phoneController.text.trim()));
|
|
var nameParts =
|
|
(box.read(BoxName.name) ?? "Unknown User").toString().split(' ');
|
|
var firstName = nameParts.isNotEmpty ? nameParts[0] : 'unknown';
|
|
var lastName =
|
|
nameParts.length > 1 ? nameParts.sublist(1).join(' ') : 'user';
|
|
|
|
var payload = {
|
|
'id': box.read(BoxName.passengerID),
|
|
'phone': (phoneController.text.trim()),
|
|
'email': box.read(BoxName.email),
|
|
'password': 'unknown', // This seems to be by design for social logins
|
|
'gender': 'unknown',
|
|
'birthdate': '2002-01-01',
|
|
'site': box.read(BoxName.passengerPhotoUrl) ?? 'unknown',
|
|
'first_name': firstName,
|
|
'last_name': lastName,
|
|
};
|
|
|
|
var registrationResponse = await CRUD().post(
|
|
link: AppLink.signUp,
|
|
payload: payload,
|
|
);
|
|
|
|
if (registrationResponse != 'failure') {
|
|
// --- REGISTRATION SUCCESSFUL ---
|
|
|
|
// This part for multi-server can be kept as is.
|
|
|
|
// Save user state
|
|
box.write(BoxName.isVerified, '1');
|
|
box.write(BoxName.isFirstTime, '0');
|
|
|
|
// Log the user in
|
|
Get.put(LoginController()).loginUsingCredentials(
|
|
box.read(BoxName.passengerID).toString(),
|
|
box.read(BoxName.email).toString(),
|
|
);
|
|
} else {
|
|
Get.snackbar(
|
|
'Error'.tr, "The email or phone number is already registered.".tr,
|
|
backgroundColor: Colors.redAccent);
|
|
}
|
|
} catch (e) {
|
|
addError(e.toString(), 'passenger _registerVerifiedUser');
|
|
Get.snackbar('Error'.tr, "An error occurred during registration.".tr,
|
|
backgroundColor: Colors.redAccent);
|
|
}
|
|
}
|
|
|
|
// --- Other existing functions (unchanged) ---
|
|
|
|
getBirthDate() {
|
|
Get.defaultDialog(
|
|
title: 'Select Date'.tr,
|
|
titleStyle: AppStyle.title,
|
|
content: SizedBox(
|
|
width: 300,
|
|
child: CalendarDatePicker(
|
|
initialDate: DateTime.now().subtract(const Duration(days: 14 * 365)),
|
|
firstDate: DateTime.parse('1940-06-01'),
|
|
lastDate: DateTime.now().subtract(const Duration(days: 14 * 365)),
|
|
onDateChanged: (date) {
|
|
birthDate = date.toString().split(' ')[0];
|
|
update();
|
|
Get.back();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void changeGender(String value) {
|
|
gender = value;
|
|
update();
|
|
}
|
|
|
|
/// This is the old registration method, likely for a standard email/password form.
|
|
/// It seems separate from the OTP flow, so I'm leaving it as is.
|
|
void registerWithEmailPassword() async {
|
|
if (formKey.currentState!.validate()) {
|
|
var res = await CRUD().post(link: AppLink.signUp, payload: {
|
|
'first_name': firstNameController.text.toString(),
|
|
'last_name': lastNameController.text.toString(),
|
|
'email': emailController.text.toString(),
|
|
'phone': phoneController.text.toString(),
|
|
'password': passwordController.text.toString(),
|
|
'gender': 'yet',
|
|
'site': siteController.text,
|
|
'birthdate': birthDate,
|
|
});
|
|
if (jsonDecode(res)['status'] == 'success') {
|
|
int randomNumber = Random().nextInt(100000) + 1;
|
|
await CRUD().post(link: AppLink.sendVerifyEmail, payload: {
|
|
'email': emailController.text,
|
|
'token': randomNumber.toString(),
|
|
});
|
|
Get.to(() => const VerifyEmailPage());
|
|
}
|
|
}
|
|
}
|
|
}
|