Files
syncrow-app/lib/features/auth/bloc/auth_cubit.dart
2024-06-05 03:42:00 +03:00

291 lines
8.8 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:syncrow_app/features/auth/model/login_with_email_model.dart';
import 'package:syncrow_app/features/auth/model/signup_model.dart';
import 'package:syncrow_app/features/auth/model/token.dart';
import 'package:syncrow_app/features/auth/model/user_model.dart';
import 'package:syncrow_app/navigation/navigation_service.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/services/api/authentication_api.dart';
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
part 'auth_state.dart';
class AuthCubit extends Cubit<AuthState> {
AuthCubit() : super(AuthInitial());
static AuthCubit get(context) => BlocProvider.of(context);
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
String fullName = '';
String email = '';
String signUpPassword = '';
String maskedEmail = '';
String otpCode = '';
final loginFormKey = GlobalKey<FormState>();
final signUpFormKey = GlobalKey<FormState>();
bool isPasswordVisible = false;
bool showValidationMessage = false;
static GlobalKey<FormState> formKey = GlobalKey<FormState>();
void changePasswordVisibility() {
isPasswordVisible = !isPasswordVisible;
emit(AuthPasswordVisibilityChanged());
}
bool agreeToTerms = false;
void changeAgreeToTerms() {
agreeToTerms = !agreeToTerms;
emit(AuthAgreeToTermsChanged());
}
static UserModel? user;
static Token token = Token.emptyConstructor();
setOtpCode(String value) {
otpCode = value;
}
/////////////////////////////////////VALIDATORS/////////////////////////////////////
String? passwordValidator(String? value) {
if (value != null) {
if (value.isEmpty) {
return 'Please enter your password';
}
if (value.isNotEmpty) {
if (!RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$')
.hasMatch(value)) {
return 'Password must contain at least:\n - one uppercase letter.\n - one lowercase letter.\n - one number. \n - special character';
}
}
}
return null;
}
String? fullNameValidator(String? value) {
if (value == null) return 'Full name is required';
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
return 'Full name must be between 2 and 30 characters long';
}
// Test if it contains anything but alphanumeric spaces and single quote
if (RegExp(r"/[^ a-zA-Z0-9-\']/").hasMatch(withoutExtraSpaces)) {
return 'Only alphanumeric characters, space, dash and single quote are allowed';
}
final parts = withoutExtraSpaces.split(' ');
if (parts.length < 2) return 'Full name must contain first and last names';
if (parts.length > 3) return 'Full name can at most contain 3 parts';
if (parts.any((part) => part.length < 2 || part.length > 30)) {
return 'Full name parts must be between 2 and 30 characters long';
}
return null;
}
String? reEnterPasswordCheck(String? value) {
passwordValidator(value);
if (signUpPassword == value) {
// if (signUpFormKey.currentState != null) {
// signUpFormKey.currentState!.save();
// }
return null;
} else {
return 'Passwords do not match';
}
}
String? emailAddressValidator(String? value) {
if (value != null && value.isNotEmpty && value != "") {
if (checkValidityOfEmail(value)) {
return null;
} else {
return 'Please enter a valid email';
}
} else {
return 'Email address is required';
}
}
bool checkValidityOfEmail(String? email) {
if (email != null) {
return RegExp(
r"^[a-zA-Z0-9]+([.!#$%&'*+/=?^_`{|}~-]?[a-zA-Z0-9]+)*@[a-zA-Z0-9]+([.-]?[a-zA-Z0-9]+)*\.[a-zA-Z0-9]{2,}$")
.hasMatch(email);
} else {
return false;
}
}
// Function to mask the email
String maskEmail(String email) {
final emailParts = email.split('@');
if (emailParts.length != 2) return email;
final localPart = emailParts[0];
final domainPart = emailParts[1];
if (localPart.length < 3) return email;
final start = localPart.substring(0, 2);
final end = localPart.substring(localPart.length - 1);
final maskedLocalPart = '$start******$end';
return '$maskedLocalPart@$domainPart';
}
/////////////////////////////////////API CALLS/////////////////////////////////////
login() async {
emit(AuthLoginLoading());
try {
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
CustomSnackBar.displaySnackBar('Please enter your credentials');
emit(AuthLoginError(message: 'Something went wrong'));
return;
}
token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel(
email: emailController.text.toLowerCase(),
password: passwordController.text,
),
);
} catch (failure) {
emit(AuthLoginError(message: failure.toString()));
return;
}
if (token.accessTokenIsNotEmpty) {
debugPrint('token: ${token.accessToken}');
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write(
key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString());
user = UserModel.fromToken(token);
emailController.clear();
passwordController.clear();
emit(AuthLoginSuccess());
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
signUp() async {
emit(AuthLoginLoading());
final response;
try {
List<String> userFullName = fullName.split(' ');
response = await AuthenticationAPI.signUp(
model: SignUpModel(
email: email.toLowerCase(),
password: signUpPassword,
firstName: userFullName[0],
lastName: userFullName[1]),
);
} catch (failure) {
emit(AuthLoginError(message: failure.toString()));
return;
}
if (response) {
maskedEmail = maskEmail(email);
await sendOtp();
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
sendOtp() async {
try {
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'});
emit(AuthSignUpSuccess());
} catch (_) {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
reSendOtp() async {
try {
await AuthenticationAPI.sendOtp(body: {'email': email, 'type': 'VERIFICATION'});
emit(ResendOtpSuccess());
} catch (_) {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
verifyOtp() async {
emit(AuthLoginLoading());
try {
final response = await AuthenticationAPI.verifyPassCode(
body: {'email': email, 'type': 'VERIFICATION', 'otpCode': otpCode});
if (response['statusCode'] == 200) {
emailController.text = email;
passwordController.text = signUpPassword;
await login();
emit(AuthOtpSuccess());
} else {
emit(AuthLoginError(message: 'Something went wrong'));
}
// if (otpCode == '654321') {
// emit(AuthOtpSuccess());
// } else {
// emit(AuthLoginError(message: 'Otp is not correct'));
// }
} catch (failure) {
emit(AuthLoginError(message: failure.toString()));
return;
}
}
logout() async {
emit(AuthLogoutLoading());
try {
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.delete(key: Token.loginAccessTokenKey);
NavigationService.navigatorKey.currentState!.pushNamedAndRemoveUntil(
Routes.splash,
(Route route) => false,
);
} catch (failure) {
emit(AuthLogoutError(message: failure.toString()));
return;
}
}
getTokenAndValidate() async {
emit(AuthTokenLoading());
final value = await const FlutterSecureStorage().read(key: Token.loginAccessTokenKey);
if (value == null || value.isEmpty) {
emit(AuthTokenError(message: "Token not found"));
return;
}
final tokenData = Token.decodeToken(value);
if (tokenData.containsKey('exp')) {
final exp = tokenData['exp'] ?? 0;
final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
if (currentTime < exp) {
emit(AuthTokenSuccess());
} else {
emit(AuthTokenError(message: "Token expired"));
}
} else {
emit(AuthTokenError(message: "Something went wrong"));
}
}
}