362 lines
11 KiB
Dart
362 lines
11 KiB
Dart
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:onufitness/constants/api_endpoints.dart';
|
|
import 'package:onufitness/models/user_details_response_model.dart';
|
|
import 'package:onufitness/services/api_services/base_api_services.dart';
|
|
import 'package:onufitness/utils/custom_sneakbar.dart';
|
|
|
|
class EditAccountController extends GetxController {
|
|
// User data
|
|
UserDetailsResponseModel userDetailsResponseModel =
|
|
UserDetailsResponseModel();
|
|
|
|
final ImagePicker _picker = ImagePicker();
|
|
var profileImage = Rx<XFile?>(null);
|
|
var isUploadingImage = false.obs;
|
|
|
|
TextEditingController firstNameController = TextEditingController();
|
|
TextEditingController lastNameController = TextEditingController();
|
|
TextEditingController aboutMeController = TextEditingController();
|
|
TextEditingController facebookController = TextEditingController();
|
|
TextEditingController instagramController = TextEditingController();
|
|
TextEditingController linkedInController = TextEditingController();
|
|
TextEditingController twitterController = TextEditingController();
|
|
TextEditingController phoneNumberController = TextEditingController();
|
|
|
|
// Form Data
|
|
var selectedDate = "".obs;
|
|
var selectedGender = 'Male'.obs;
|
|
var selectedCountryCode = 'IN'.obs;
|
|
|
|
// Loading states
|
|
var isUpdating = false.obs;
|
|
var isLoading = false.obs;
|
|
|
|
@override
|
|
void onInit() {
|
|
firstNameController = TextEditingController();
|
|
lastNameController = TextEditingController();
|
|
aboutMeController = TextEditingController();
|
|
facebookController = TextEditingController();
|
|
instagramController = TextEditingController();
|
|
linkedInController = TextEditingController();
|
|
twitterController = TextEditingController();
|
|
phoneNumberController = TextEditingController();
|
|
super.onInit();
|
|
loadUserData();
|
|
}
|
|
|
|
RxBool fetchUserDetailsLoading = false.obs;
|
|
Future<bool> fetchUserDetails() async {
|
|
fetchUserDetailsLoading(true);
|
|
update();
|
|
try {
|
|
final response = await ApiBase.getRequest(
|
|
extendedURL: ApiUrl.fetchUserDetails,
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
userDetailsResponseModel = userDetailsResponseModelFromJson(
|
|
response.body,
|
|
);
|
|
_populateFormFields();
|
|
update();
|
|
|
|
return true;
|
|
} else {
|
|
customSnackbar(title: "Error", message: "Failed to fetch user details");
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
log("Exception: $e");
|
|
customSnackbar(title: "Error", message: "Failed to fetch user details");
|
|
return false;
|
|
} finally {
|
|
fetchUserDetailsLoading(false);
|
|
update();
|
|
}
|
|
}
|
|
|
|
Future<void> loadUserData() async {
|
|
isLoading.value = true;
|
|
|
|
final success = await fetchUserDetails();
|
|
|
|
if (success) {
|
|
_populateFormFields();
|
|
}
|
|
|
|
isLoading.value = false;
|
|
}
|
|
|
|
void _populateFormFields() {
|
|
final userData = userDetailsResponseModel.data;
|
|
if (userData == null) return;
|
|
|
|
firstNameController.text = userData.firstName ?? '';
|
|
lastNameController.text = userData.lastName ?? '';
|
|
aboutMeController.text = userData.aboutMe ?? '';
|
|
facebookController.text = userData.facebookSocialLink ?? '';
|
|
instagramController.text = userData.instagramSocialLink ?? '';
|
|
linkedInController.text = userData.linkedInSocialLink ?? '';
|
|
twitterController.text = userData.twitterSocialLink ?? '';
|
|
phoneNumberController.text = userData.phoneNumber ?? '';
|
|
|
|
selectedGender.value = userData.gender ?? 'Male';
|
|
selectedCountryCode.value = userData.countryCode ?? 'IN';
|
|
|
|
if (userData.dateOfBirth != null) {
|
|
selectedDate.value = DateFormat(
|
|
'yyyy-MM-dd',
|
|
).format(userData.dateOfBirth!);
|
|
}
|
|
}
|
|
|
|
// Simple age validation...........................................................................
|
|
bool _isOver18(DateTime birthDate) {
|
|
final now = DateTime.now();
|
|
final age = now.year - birthDate.year;
|
|
if (now.month < birthDate.month ||
|
|
(now.month == birthDate.month && now.day < birthDate.day)) {
|
|
return (age - 1) >= 18;
|
|
}
|
|
return age >= 18;
|
|
}
|
|
|
|
Future<void> pickDate(BuildContext context) async {
|
|
final DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now().subtract(Duration(days: 18 * 365)),
|
|
firstDate: DateTime(1800),
|
|
lastDate: DateTime.now(),
|
|
);
|
|
|
|
if (pickedDate != null) {
|
|
if (_isOver18(pickedDate)) {
|
|
selectedDate.value = DateFormat('yyyy-MM-dd').format(pickedDate);
|
|
} else {
|
|
customSnackbar(
|
|
title: "Age Restriction",
|
|
message: "You must be at least 18 years old to proceed.",
|
|
duration: 2,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//............................................................................................
|
|
Future<bool> updateUserProfile() async {
|
|
isUpdating.value = true;
|
|
|
|
try {
|
|
final requestBody = {
|
|
"firstName": firstNameController.text.trim(),
|
|
"lastName": lastNameController.text.trim(),
|
|
"aboutMe": aboutMeController.text.trim(),
|
|
"dateOfBirth": selectedDate.value,
|
|
"gender": selectedGender.value,
|
|
"facebookSocialLink": facebookController.text.trim(),
|
|
"linkedInSocialLink": linkedInController.text.trim(),
|
|
"instagramSocialLink": instagramController.text.trim(),
|
|
"twitterSocialLink": twitterController.text.trim(),
|
|
"phoneNumber": phoneNumberController.text.trim(),
|
|
"countryCode": selectedCountryCode.value,
|
|
};
|
|
|
|
final response = await ApiBase.patchRequest(
|
|
extendedURL: "/api/Users/update-user",
|
|
body: requestBody,
|
|
sendHeaders: true,
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 204) {
|
|
// Refresh user data after successful update
|
|
await fetchUserDetails();
|
|
update();
|
|
Get.back();
|
|
Future.delayed(const Duration(milliseconds: 300), () {
|
|
customSnackbar(
|
|
title: 'Success',
|
|
message: 'Profile updated successfully',
|
|
duration: 1,
|
|
);
|
|
});
|
|
return true;
|
|
} else {
|
|
customSnackbar(
|
|
title: 'Failed',
|
|
message: 'Failed to update profile details',
|
|
);
|
|
return false;
|
|
}
|
|
} catch (e) {
|
|
log('Update profile error: $e');
|
|
customSnackbar(
|
|
title: 'Failed',
|
|
message: 'Failed to update profile details',
|
|
);
|
|
return false;
|
|
} finally {
|
|
isUpdating.value = false;
|
|
}
|
|
}
|
|
|
|
Future<void> uploadProfilePictureAPIcall() async {
|
|
if (profileImage.value == null) return;
|
|
|
|
isUploadingImage.value = true;
|
|
|
|
try {
|
|
final userData = userDetailsResponseModel.data;
|
|
final userId = userData?.userId;
|
|
|
|
if (userId == null) {
|
|
customSnackbar(title: "Error", message: "User ID not found");
|
|
return;
|
|
}
|
|
|
|
final response = await ApiBase.postSingleFileRequest(
|
|
extendedURL: "${ApiUrl.uploadProfileImage}/$userId",
|
|
sendHeaders: false,
|
|
file: profileImage.value!,
|
|
fileNameKey: 'file',
|
|
);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
// Refresh user data to get updated profile image
|
|
await fetchUserDetails();
|
|
|
|
customSnackbar(
|
|
title: "Success",
|
|
message: "Image uploaded successfully",
|
|
duration: 1,
|
|
);
|
|
} else {
|
|
final errorMessage =
|
|
jsonDecode(response.body)["message"] ?? "Upload failed";
|
|
customSnackbar(title: "Error", message: errorMessage);
|
|
}
|
|
} catch (e) {
|
|
log("Upload image error: $e");
|
|
customSnackbar(title: "Error", message: "Failed to upload image");
|
|
} finally {
|
|
isUploadingImage.value = false;
|
|
}
|
|
}
|
|
|
|
// Getters for UI
|
|
String? get userProfileImageUrl =>
|
|
userDetailsResponseModel.data?.userProfilePic;
|
|
bool get hasNetworkImage => userProfileImageUrl?.isNotEmpty == true;
|
|
//........... Delete user Account............................................................................
|
|
RxBool isAccountDeleting = false.obs;
|
|
Future<bool> deleteUserAccount() async {
|
|
bool ret = false;
|
|
isAccountDeleting(true);
|
|
try {
|
|
final response = await ApiBase.patchRequest(
|
|
extendedURL: "/api/Users/delete-user-account",
|
|
);
|
|
log(response.body.toString());
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
ret = true;
|
|
} else {
|
|
ret = false;
|
|
customSnackbar(
|
|
title: "Deletion Failed",
|
|
message:
|
|
"We were unable to delete your account. Please try again later.",
|
|
duration: 2,
|
|
);
|
|
}
|
|
} catch (e) {
|
|
customSnackbar(
|
|
title: "Deletion Failed",
|
|
message:
|
|
"We were unable to delete your account. Please try again later.",
|
|
|
|
duration: 2,
|
|
);
|
|
ret = false;
|
|
} finally {
|
|
isAccountDeleting(false);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Form validation............................................................................
|
|
bool validateForm() {
|
|
if (firstNameController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "First Name empty",
|
|
message: "Please enter your first name",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (lastNameController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Last Name empty",
|
|
message: "Please enter your last name",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (phoneNumberController.text.trim().isEmpty) {
|
|
customSnackbar(
|
|
title: "Phone Number empty",
|
|
message: "Please enter your phone number",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (!isValidName(firstNameController.text.trim())) {
|
|
customSnackbar(
|
|
title: "First Name Input error",
|
|
message:
|
|
"First name should only contain letters, spaces, apostrophes, and hyphens",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if (!isValidName(lastNameController.text.trim())) {
|
|
customSnackbar(
|
|
title: "Last Name Input error",
|
|
message:
|
|
"Last name should only contain letters, spaces, apostrophes, and hyphens",
|
|
duration: 2,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool isValidName(String name) {
|
|
final nameRegex = RegExp(r"^[a-zA-Z\s\-\']+$");
|
|
return nameRegex.hasMatch(name) && name.length >= 2;
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
firstNameController.dispose();
|
|
lastNameController.dispose();
|
|
aboutMeController.dispose();
|
|
facebookController.dispose();
|
|
instagramController.dispose();
|
|
linkedInController.dispose();
|
|
twitterController.dispose();
|
|
phoneNumberController.dispose();
|
|
super.onClose();
|
|
}
|
|
}
|