Merge branch 'dev' into doorlock_interface

This commit is contained in:
Mohammad Salameh
2024-04-17 14:01:54 +03:00
9 changed files with 143 additions and 154 deletions

View File

@ -20,8 +20,9 @@ class AuthCubit extends Cubit<AuthState> {
static AuthCubit get(context) => BlocProvider.of(context); static AuthCubit get(context) => BlocProvider.of(context);
TextEditingController emailController = TextEditingController(); final TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController(); final TextEditingController passwordController = TextEditingController();
final loginFormKey = GlobalKey<FormState>();
bool isPasswordVisible = false; bool isPasswordVisible = false;
static GlobalKey<FormState> formKey = GlobalKey<FormState>(); static GlobalKey<FormState> formKey = GlobalKey<FormState>();
@ -41,6 +42,56 @@ class AuthCubit extends Cubit<AuthState> {
static UserModel? user; static UserModel? user;
static Token token = Token.emptyConstructor(); 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///////////////////////////////////// /////////////////////////////////////API CALLS/////////////////////////////////////
login() async { login() async {
emit(AuthLoginLoading()); emit(AuthLoginLoading());

View File

@ -13,9 +13,10 @@ class LoginForm extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var formKey = GlobalKey<FormState>(); var pressed = false;
return BlocBuilder<AuthCubit, AuthState>( return BlocBuilder<AuthCubit, AuthState>(
builder: (context, state) { builder: (context, state) {
final formKey = AuthCubit.get(context).loginFormKey;
return Form( return Form(
key: formKey, key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
@ -28,30 +29,23 @@ class LoginForm extends StatelessWidget {
fontColor: Colors.white, fontColor: Colors.white,
), ),
TextFormField( TextFormField(
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
autocorrect: false,
autofillHints: const [AutofillHints.email],
controller: AuthCubit.get(context).emailController, controller: AuthCubit.get(context).emailController,
validator: (value) { validator: (value) {
if (state is! AuthTokenError) { if (state is AuthTokenError && !pressed) {
if (value != null) { return 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';
}
} }
return null; return AuthCubit.get(context).emailAddressValidator(value);
}, },
onTapOutside: (event) { onTapOutside: (event) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}, },
onChanged: (value) {},
decoration: defaultInputDecoration(context, decoration: defaultInputDecoration(context,
hint: "Example@email.com"), hint: "Example@email.com"),
), ),
@ -61,30 +55,18 @@ class LoginForm extends StatelessWidget {
fontColor: Colors.white, fontColor: Colors.white,
), ),
TextFormField( TextFormField(
autovalidateMode: AutovalidateMode.disabled,
textInputAction: TextInputAction.done,
keyboardType: TextInputType.text,
scrollPadding: EdgeInsets.zero,
autocorrect: false,
autofillHints: const [AutofillHints.password],
controller: AuthCubit.get(context).passwordController, controller: AuthCubit.get(context).passwordController,
validator: (value) { validator: (value) {
if (state is! AuthTokenError) { if (state is AuthTokenError && !pressed) {
if (value != null) { return 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';
}
}
} }
return null; return AuthCubit.get(context).passwordValidator(value);
}, },
onTapOutside: (event) { onTapOutside: (event) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
@ -116,14 +98,11 @@ class LoginForm extends StatelessWidget {
'Login', 'Login',
), ),
onPressed: () { onPressed: () {
bool isValid = formKey.currentState!.validate(); pressed = true;
if (isValid) { if (formKey.currentState!.validate()) {
if ((state is! AuthLoading)) { if ((state is! AuthLoading)) {
print('login');
AuthCubit.get(context).login(); AuthCubit.get(context).login();
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
} else {
formKey.currentState!.save();
} }
} }
}, },

View File

@ -16,16 +16,11 @@ class AuthenticationAPI {
static Future<Token> loginWithEmail( static Future<Token> loginWithEmail(
{required LoginWithEmailModel model}) async { {required LoginWithEmailModel model}) async {
try { final response = await HTTPService().post(
final response = await HTTPService().post( path: ApiEndpoints.login,
path: ApiEndpoints.login, body: model.toJson(),
body: model.toJson(), showServerMessage: false,
showServerMessage: false, expectedResponseModel: (json) => Token.fromJson(json['data']));
expectedResponseModel: (json) => Token.fromJson(json['data'])); return response;
// developer.log("response: $response");
return response;
} catch (e) {
rethrow;
}
} }
} }

View File

@ -30,52 +30,41 @@ class DevicesAPI {
"pageSize": 100, "pageSize": 100,
"pageNo": 1 "pageNo": 1
}; };
try {
final response = await _httpService.get( final response = await _httpService.get(
path: ApiEndpoints.groups, path: ApiEndpoints.groups,
queryParameters: params, queryParameters: params,
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => expectedResponseModel: (json) =>
DevicesCategoryModel.fromJsonList(json['groups']), DevicesCategoryModel.fromJsonList(json['groups']),
); );
return response; return response;
} catch (e) {
rethrow;
}
} }
static Future<Map<String, dynamic>> getDeviceStatus(String deviceId) async { static Future<Map<String, dynamic>> getDeviceStatus(String deviceId) async {
try { final response = await _httpService.get(
final response = await _httpService.get( path: '${ApiEndpoints.deviceStatus}/$deviceId/functions/status',
path: '${ApiEndpoints.deviceStatus}/$deviceId/functions/status', showServerMessage: false,
showServerMessage: false, expectedResponseModel: (json) {
expectedResponseModel: (json) { return json;
return json; },
}, );
); return response;
return response;
} catch (e) {
rethrow;
}
} }
static Future<List<DeviceModel>> getDevicesByRoomId(int roomId) async { static Future<List<DeviceModel>> getDevicesByRoomId(int roomId) async {
try { final response = await _httpService.get(
final response = await _httpService.get( path: ApiEndpoints.devicesByRoom,
path: ApiEndpoints.devicesByRoom, queryParameters: {"roomId": roomId, "pageSize": 10},
queryParameters: {"roomId": roomId, "pageSize": 10}, showServerMessage: false,
showServerMessage: false, expectedResponseModel: (json) {
expectedResponseModel: (json) { List<DeviceModel> devices = [];
List<DeviceModel> devices = []; for (var device in json['devices']) {
for (var device in json['devices']) { devices.add(DeviceModel.fromJson(device));
devices.add(DeviceModel.fromJson(device)); }
} return devices;
return devices; },
}, );
); return response;
return response;
} catch (e) {
rethrow;
}
} }
} }

View File

@ -42,11 +42,6 @@ class HTTPInterceptor extends InterceptorsWrapper {
@override @override
void onError(DioException err, ErrorInterceptorHandler handler) async { 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); ServerFailure failure = ServerFailure.fromDioError(err);
CustomSnackBar.displaySnackBar(failure.toString()); CustomSnackBar.displaySnackBar(failure.toString());
var storage = const FlutterSecureStorage(); var storage = const FlutterSecureStorage();

View File

@ -74,11 +74,8 @@ class HTTPService {
data: body, data: body,
queryParameters: queryParameters, queryParameters: queryParameters,
); );
developer.log("status code is ${response.statusCode}");
return expectedResponseModel(response.data); return expectedResponseModel(response.data);
} catch (error) { } catch (error) {
developer.log("******* Error");
developer.log(error.toString());
rethrow; rethrow;
} }
} }
@ -89,21 +86,15 @@ class HTTPService {
Map<String, dynamic>? queryParameters, Map<String, dynamic>? queryParameters,
required T Function(dynamic) expectedResponseModel}) async { required T Function(dynamic) expectedResponseModel}) async {
try { try {
developer.log("download begins");
final response = await client.download( final response = await client.download(
path, path,
savePath, savePath,
onReceiveProgress: (current, total) { onReceiveProgress: (current, total) {},
developer.log("current = $current, while total = $total");
},
); );
developer.log("download ends");
return expectedResponseModel(response.data); return expectedResponseModel(response.data);
// return expectedResponseModel(response.data); // return expectedResponseModel(response.data);
} catch (error) { } catch (error) {
developer.log("******* Error");
developer.log("download error");
developer.log(error.toString());
rethrow; rethrow;
} }
} }
@ -121,8 +112,6 @@ class HTTPService {
); );
return expectedResponseModel(response.data); return expectedResponseModel(response.data);
} catch (error) { } catch (error) {
developer.log("******* Error");
developer.log(error.toString());
rethrow; rethrow;
} }
} }

View File

@ -32,7 +32,7 @@ class ServerFailure extends Failure {
// var document = parser.parse(dioError.response!.data.toString()); // var document = parser.parse(dioError.response!.data.toString());
// var message = document.body!.text; // var message = document.body!.text;
return ServerFailure.fromResponse(dioError.response!.statusCode!, return ServerFailure.fromResponse(dioError.response!.statusCode!,
dioError.response!.data['message']); dioError.response?.data['message'] ?? "Error");
} }
case DioExceptionType.cancel: case DioExceptionType.cancel:
return ServerFailure("The request to ApiServer was canceled"); 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) { switch (statusCode) {
case 401: case 401:
case 403: case 403:
return ServerFailure(response); return ServerFailure(responseMessage);
case 400: case 400:
List<String> errors = []; List<String> errors = [];
if (response['message'] is List) { if (responseMessage is List) {
for (var error in response['message']) { for (var error in responseMessage) {
errors.add(error); errors.add(error);
} }
} else { } else {
errors.add(response['message']); return ServerFailure(responseMessage);
} }
return ServerFailure(errors.join('\n')); return ServerFailure(errors.join('\n'));
case 404: case 404:
return ServerFailure("Your request not found, Please try later!"); return ServerFailure("Your request not found, Please try later!");
case 500: case 500:
return ServerFailure(response); return ServerFailure(responseMessage);
default: default:
return ServerFailure("Opps there was an Error, Please try again!"); return ServerFailure("Opps there was an Error, Please try again!");
} }

View File

@ -12,39 +12,31 @@ class SpacesAPI {
static Future<List<SpaceModel>> getSpaces() async { static Future<List<SpaceModel>> getSpaces() async {
var uuid = var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey); await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
try { final response = await _httpService.get(
final response = await _httpService.get( path: ApiEndpoints.spaces,
path: ApiEndpoints.spaces, queryParameters: {
queryParameters: { "userUuid": uuid,
"userUuid": uuid, },
}, showServerMessage: false,
showServerMessage: false, expectedResponseModel: (json) => SpaceModel.fromJsonList(json),
expectedResponseModel: (json) => SpaceModel.fromJsonList(json), );
); return response;
return response;
} catch (e) {
rethrow;
}
} }
//get rooms by space id //get rooms by space id
static Future<List<RoomModel>> getRoomsBySpaceId(int spaceId) async { static Future<List<RoomModel>> getRoomsBySpaceId(int spaceId) async {
try { final response = await _httpService.get(
final response = await _httpService.get( path: ApiEndpoints.rooms,
path: ApiEndpoints.rooms, queryParameters: {"homeId": spaceId},
queryParameters: {"homeId": spaceId}, showServerMessage: false,
showServerMessage: false, expectedResponseModel: (json) {
expectedResponseModel: (json) { List<RoomModel> rooms = [];
List<RoomModel> rooms = []; for (var room in json) {
for (var room in json) { rooms.add(RoomModel.fromJson(room));
rooms.add(RoomModel.fromJson(room)); }
} return rooms;
return rooms; },
}, );
); return response;
return response;
} catch (e) {
rethrow;
}
} }
} }

View File

@ -6,7 +6,6 @@ class StringHelpers {
static String enhanceFileName(File file) { static String enhanceFileName(File file) {
var fileName = " "; var fileName = " ";
final filePath = file.path; final filePath = file.path;
developer.log(filePath);
final fileStringArray = filePath.split("/"); final fileStringArray = filePath.split("/");
fileName = fileStringArray.last; fileName = fileStringArray.last;
if (fileName.length > 20) { if (fileName.length > 20) {