From 95f7ade9e6f6d4d85d86f74ec84cd642ce6ea52f Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 16 Apr 2024 15:45:04 +0300 Subject: [PATCH 1/4] removed try-catch blocks from auth_api.dart, devices_api.dart and spaces_api.dart files --- lib/services/api/authentication_api.dart | 17 ++---- lib/services/api/devices_api.dart | 71 ++++++++++-------------- lib/services/api/spaces_api.dart | 52 ++++++++--------- 3 files changed, 58 insertions(+), 82 deletions(-) diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index f83b99c..a5e81e4 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -16,16 +16,11 @@ class AuthenticationAPI { static Future loginWithEmail( {required LoginWithEmailModel model}) async { - try { - final response = await HTTPService().post( - path: ApiEndpoints.login, - body: model.toJson(), - showServerMessage: false, - expectedResponseModel: (json) => Token.fromJson(json['data'])); - // developer.log("response: $response"); - return response; - } catch (e) { - rethrow; - } + final response = await HTTPService().post( + path: ApiEndpoints.login, + body: model.toJson(), + showServerMessage: false, + expectedResponseModel: (json) => Token.fromJson(json['data'])); + return response; } } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 31bd309..97c5588 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -30,52 +30,41 @@ class DevicesAPI { "pageSize": 100, "pageNo": 1 }; - try { - final response = await _httpService.get( - path: ApiEndpoints.groups, - queryParameters: params, - showServerMessage: false, - expectedResponseModel: (json) => - DevicesCategoryModel.fromJsonList(json['groups']), - ); - return response; - } catch (e) { - rethrow; - } + + final response = await _httpService.get( + path: ApiEndpoints.groups, + queryParameters: params, + showServerMessage: false, + expectedResponseModel: (json) => + DevicesCategoryModel.fromJsonList(json['groups']), + ); + return response; } static Future> getDeviceStatus(String deviceId) async { - try { - final response = await _httpService.get( - path: '${ApiEndpoints.deviceStatus}/$deviceId/functions/status', - showServerMessage: false, - expectedResponseModel: (json) { - return json; - }, - ); - return response; - } catch (e) { - rethrow; - } + final response = await _httpService.get( + path: '${ApiEndpoints.deviceStatus}/$deviceId/functions/status', + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; } static Future> getDevicesByRoomId(int roomId) async { - try { - final response = await _httpService.get( - path: ApiEndpoints.devicesByRoom, - queryParameters: {"roomId": roomId, "pageSize": 10}, - showServerMessage: false, - expectedResponseModel: (json) { - List devices = []; - for (var device in json['devices']) { - devices.add(DeviceModel.fromJson(device)); - } - return devices; - }, - ); - return response; - } catch (e) { - rethrow; - } + final response = await _httpService.get( + path: ApiEndpoints.devicesByRoom, + queryParameters: {"roomId": roomId, "pageSize": 10}, + showServerMessage: false, + expectedResponseModel: (json) { + List devices = []; + for (var device in json['devices']) { + devices.add(DeviceModel.fromJson(device)); + } + return devices; + }, + ); + return response; } } diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index f05df9f..3aa95dd 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -12,39 +12,31 @@ class SpacesAPI { static Future> getSpaces() async { var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); - try { - final response = await _httpService.get( - path: ApiEndpoints.spaces, - queryParameters: { - "userUuid": uuid, - }, - showServerMessage: false, - expectedResponseModel: (json) => SpaceModel.fromJsonList(json), - ); - return response; - } catch (e) { - rethrow; - } + final response = await _httpService.get( + path: ApiEndpoints.spaces, + queryParameters: { + "userUuid": uuid, + }, + showServerMessage: false, + expectedResponseModel: (json) => SpaceModel.fromJsonList(json), + ); + return response; } //get rooms by space id static Future> getRoomsBySpaceId(int spaceId) async { - try { - final response = await _httpService.get( - path: ApiEndpoints.rooms, - queryParameters: {"homeId": spaceId}, - showServerMessage: false, - expectedResponseModel: (json) { - List rooms = []; - for (var room in json) { - rooms.add(RoomModel.fromJson(room)); - } - return rooms; - }, - ); - return response; - } catch (e) { - rethrow; - } + final response = await _httpService.get( + path: ApiEndpoints.rooms, + queryParameters: {"homeId": spaceId}, + showServerMessage: false, + expectedResponseModel: (json) { + List rooms = []; + for (var room in json) { + rooms.add(RoomModel.fromJson(room)); + } + return rooms; + }, + ); + return response; } } From 214c4bb749770bcf0a194b1eec13a83fc8471231 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 16 Apr 2024 15:45:32 +0300 Subject: [PATCH 2/4] Remove unnecessary developer log statements --- lib/services/api/http_interceptor.dart | 5 ----- lib/services/api/http_service.dart | 15 ++------------- lib/utils/helpers/misc_string_helpers.dart | 1 - 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index baae847..7f3b8be 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -42,11 +42,6 @@ class HTTPInterceptor extends InterceptorsWrapper { @override void onError(DioException err, ErrorInterceptorHandler handler) async { - // developer.log('Error Message: ${err.message}'); - // developer.log('Error res Code: ${err.response?.statusCode}'); - // developer.log('Error res Data: ${err.response?.data}'); - // developer.log('Error res status message: ${err.response?.statusMessage}'); - ServerFailure failure = ServerFailure.fromDioError(err); CustomSnackBar.displaySnackBar(failure.toString()); var storage = const FlutterSecureStorage(); diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index 414741d..dc995ff 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -74,11 +74,8 @@ class HTTPService { data: body, queryParameters: queryParameters, ); - developer.log("status code is ${response.statusCode}"); return expectedResponseModel(response.data); } catch (error) { - developer.log("******* Error"); - developer.log(error.toString()); rethrow; } } @@ -89,21 +86,15 @@ class HTTPService { Map? queryParameters, required T Function(dynamic) expectedResponseModel}) async { try { - developer.log("download begins"); final response = await client.download( path, savePath, - onReceiveProgress: (current, total) { - developer.log("current = $current, while total = $total"); - }, + onReceiveProgress: (current, total) {}, ); - developer.log("download ends"); + return expectedResponseModel(response.data); // return expectedResponseModel(response.data); } catch (error) { - developer.log("******* Error"); - developer.log("download error"); - developer.log(error.toString()); rethrow; } } @@ -121,8 +112,6 @@ class HTTPService { ); return expectedResponseModel(response.data); } catch (error) { - developer.log("******* Error"); - developer.log(error.toString()); rethrow; } } diff --git a/lib/utils/helpers/misc_string_helpers.dart b/lib/utils/helpers/misc_string_helpers.dart index 0b9a2c5..616e8a4 100644 --- a/lib/utils/helpers/misc_string_helpers.dart +++ b/lib/utils/helpers/misc_string_helpers.dart @@ -6,7 +6,6 @@ class StringHelpers { static String enhanceFileName(File file) { var fileName = " "; final filePath = file.path; - developer.log(filePath); final fileStringArray = filePath.split("/"); fileName = fileStringArray.last; if (fileName.length > 20) { From fd03d29e93c5d3e757e837d23f43f03838bb61a1 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 16 Apr 2024 15:47:03 +0300 Subject: [PATCH 3/4] Avoided null value in network_exception.dart when calling fromResponse --- lib/services/api/network_exception.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/services/api/network_exception.dart b/lib/services/api/network_exception.dart index 2b61e28..d8f8d23 100644 --- a/lib/services/api/network_exception.dart +++ b/lib/services/api/network_exception.dart @@ -32,7 +32,7 @@ class ServerFailure extends Failure { // var document = parser.parse(dioError.response!.data.toString()); // var message = document.body!.text; return ServerFailure.fromResponse(dioError.response!.statusCode!, - dioError.response!.data['message']); + dioError.response?.data['message'] ?? "Error"); } case DioExceptionType.cancel: return ServerFailure("The request to ApiServer was canceled"); @@ -48,25 +48,25 @@ class ServerFailure extends Failure { } } - factory ServerFailure.fromResponse(int? statusCode, dynamic response) { + factory ServerFailure.fromResponse(int? statusCode, dynamic responseMessage) { switch (statusCode) { case 401: case 403: - return ServerFailure(response); + return ServerFailure(responseMessage); case 400: List errors = []; - if (response['message'] is List) { - for (var error in response['message']) { + if (responseMessage is List) { + for (var error in responseMessage) { errors.add(error); } } else { - errors.add(response['message']); + return ServerFailure(responseMessage); } return ServerFailure(errors.join('\n')); case 404: return ServerFailure("Your request not found, Please try later!"); case 500: - return ServerFailure(response); + return ServerFailure(responseMessage); default: return ServerFailure("Opps there was an Error, Please try again!"); } From 62667802584df6bd794ad44078bb1e8efa7092b8 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 16 Apr 2024 16:44:17 +0300 Subject: [PATCH 4/4] Update form key to be a final field and add validators for email and password - Make the form key a final field in the AuthCubit class - Add validators for email and password fields in the AuthCubit class - Update the LoginForm widget to use the form key from AuthCubit class - Add validators for email and password fields in the LoginForm widget --- lib/features/auth/bloc/auth_cubit.dart | 55 ++++++++++++++- .../auth/view/widgets/login/login_form.dart | 67 +++++++------------ 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 9e46276..0c2fcdf 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -20,8 +20,9 @@ class AuthCubit extends Cubit { static AuthCubit get(context) => BlocProvider.of(context); - TextEditingController emailController = TextEditingController(); - TextEditingController passwordController = TextEditingController(); + final TextEditingController emailController = TextEditingController(); + final TextEditingController passwordController = TextEditingController(); + final loginFormKey = GlobalKey(); bool isPasswordVisible = false; static GlobalKey formKey = GlobalKey(); @@ -41,6 +42,56 @@ class AuthCubit extends Cubit { static UserModel? user; static Token token = Token.emptyConstructor(); + +/////////////////////////////////////VALIDATORS///////////////////////////////////// + String? passwordValidator(String? value) { + if (value != null) { + if (value.isNotEmpty) { + if (value.length >= 6) { + return null; + //TODO uncomment this code when the password validation is needed + // if (RegExp( + // r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$') + // .hasMatch(value)) { + // return null; + // } else { + // return 'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character'; + // } + } else { + return 'Password must be at least 6 characters'; + } + } else { + return 'Please enter your password'; + } + } + return null; + } + + String? emailAddressValidator(String? value) { + if (value != null && value.isNotEmpty && value != "") { + if (checkValidityOfEmail(value)) { + if (loginFormKey.currentState != null) { + loginFormKey.currentState!.save(); + } + 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; + } + } + /////////////////////////////////////API CALLS///////////////////////////////////// login() async { emit(AuthLoginLoading()); diff --git a/lib/features/auth/view/widgets/login/login_form.dart b/lib/features/auth/view/widgets/login/login_form.dart index 4fe33ee..5296e23 100644 --- a/lib/features/auth/view/widgets/login/login_form.dart +++ b/lib/features/auth/view/widgets/login/login_form.dart @@ -13,9 +13,10 @@ class LoginForm extends StatelessWidget { @override Widget build(BuildContext context) { - var formKey = GlobalKey(); + var pressed = false; return BlocBuilder( builder: (context, state) { + final formKey = AuthCubit.get(context).loginFormKey; return Form( key: formKey, autovalidateMode: AutovalidateMode.onUserInteraction, @@ -28,30 +29,23 @@ class LoginForm extends StatelessWidget { fontColor: Colors.white, ), TextFormField( + autovalidateMode: AutovalidateMode.disabled, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + scrollPadding: EdgeInsets.zero, + autocorrect: false, + autofillHints: const [AutofillHints.email], controller: AuthCubit.get(context).emailController, validator: (value) { - if (state is! AuthTokenError) { - if (value != null) { - if (value.isNotEmpty) { - if (RegExp( - r'^[a-zA-Z0-9._-]+@{1}[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$') - .hasMatch(value)) { - return null; - } else { - return 'Please enter a valid email'; - } - } else { - return 'Please enter your email'; - } - } else { - return 'Please enter your email'; - } + if (state is AuthTokenError && !pressed) { + return null; } - return null; + return AuthCubit.get(context).emailAddressValidator(value); }, onTapOutside: (event) { FocusScope.of(context).unfocus(); }, + onChanged: (value) {}, decoration: defaultInputDecoration(context, hint: "Example@email.com"), ), @@ -61,30 +55,18 @@ class LoginForm extends StatelessWidget { fontColor: Colors.white, ), TextFormField( + autovalidateMode: AutovalidateMode.disabled, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + scrollPadding: EdgeInsets.zero, + autocorrect: false, + autofillHints: const [AutofillHints.password], controller: AuthCubit.get(context).passwordController, validator: (value) { - if (state is! AuthTokenError) { - if (value != null) { - if (value.isNotEmpty) { - return null; - //TODO: uncomment this when the backend is ready - // if (value.length > 8) { - // if (RegExp( - // r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$') - // .hasMatch(value)) { - // return null; - // } else { - // return 'Password must contain at least one uppercase letter, one lowercase letter, one number and one special character'; - // } - // } else { - // return 'Password must be at least 8 characters'; - // } - } else { - return 'Please enter your password'; - } - } + if (state is AuthTokenError && !pressed) { + return null; } - return null; + return AuthCubit.get(context).passwordValidator(value); }, onTapOutside: (event) { FocusScope.of(context).unfocus(); @@ -116,14 +98,11 @@ class LoginForm extends StatelessWidget { 'Login', ), onPressed: () { - bool isValid = formKey.currentState!.validate(); - if (isValid) { + pressed = true; + if (formKey.currentState!.validate()) { if ((state is! AuthLoading)) { - print('login'); AuthCubit.get(context).login(); FocusScope.of(context).unfocus(); - } else { - formKey.currentState!.save(); } } },