mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-07-15 09:45:22 +00:00
Refactor HTTPInterceptor and add CustomSnackBar helper
Refactor HTTPInterceptor to handle error responses and add a CustomSnackBar helper to display snack bars. This will improve error handling and user feedback in the application.
This commit is contained in:
@ -1,4 +1,3 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
@ -42,6 +41,14 @@ class HomeCubit extends Cubit<HomeState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_instance = null;
|
||||||
|
selectedSpace = null;
|
||||||
|
selectedRoom = null;
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
static HomeCubit get(context) => BlocProvider.of(context);
|
static HomeCubit get(context) => BlocProvider.of(context);
|
||||||
|
|
||||||
List<SpaceModel>? spaces;
|
List<SpaceModel>? spaces;
|
||||||
@ -124,8 +131,8 @@ class HomeCubit extends Cubit<HomeState> {
|
|||||||
selectedSpace = spaces!.first
|
selectedSpace = spaces!.first
|
||||||
: null;
|
: null;
|
||||||
emitSafe(GetSpacesLoaded(spaces!));
|
emitSafe(GetSpacesLoaded(spaces!));
|
||||||
} on DioException catch (e) {
|
} on ServerFailure catch (failure) {
|
||||||
emitSafe(GetSpacesError(ServerFailure.fromDioError(e).errMessage));
|
emitSafe(GetSpacesError(failure.errMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,17 +145,13 @@ class HomeCubit extends Cubit<HomeState> {
|
|||||||
} else {
|
} else {
|
||||||
emitSafe(GetSpaceRoomsError("No rooms found"));
|
emitSafe(GetSpaceRoomsError("No rooms found"));
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on ServerFailure catch (failure) {
|
||||||
emitSafe(GetSpacesError(ServerFailure.fromDioError(e).errMessage));
|
emitSafe(GetSpacesError(failure.errMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////// Nav ///////////////////////////////////////
|
/////////////////////////////////////// Nav ///////////////////////////////////////
|
||||||
|
|
||||||
static clear() {
|
|
||||||
pageIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pageIndex = 0;
|
static int pageIndex = 0;
|
||||||
|
|
||||||
static Map<String, List<Widget>> appBarActions = {
|
static Map<String, List<Widget>> appBarActions = {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||||
import 'package:syncrow_app/features/app_layout/view/widgets/app_body.dart';
|
import 'package:syncrow_app/features/app_layout/view/widgets/app_body.dart';
|
||||||
import 'package:syncrow_app/features/app_layout/view/widgets/default_app_bar.dart';
|
import 'package:syncrow_app/features/app_layout/view/widgets/default_app_bar.dart';
|
||||||
import 'package:syncrow_app/features/app_layout/view/widgets/default_nav_bar.dart';
|
import 'package:syncrow_app/features/app_layout/view/widgets/default_nav_bar.dart';
|
||||||
|
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||||
|
import 'package:syncrow_app/features/auth/model/token.dart';
|
||||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||||
|
|
||||||
@ -14,53 +17,74 @@ class AppLayout extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocConsumer<AuthCubit, AuthState>(
|
||||||
create: (context) => HomeCubit.getInstance(),
|
listener: (context, state) {
|
||||||
child: BlocConsumer<HomeCubit, HomeState>(
|
if (state is AuthError) {
|
||||||
listener: (context, state) {
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
if (state is GetSpacesError) {
|
SnackBar(
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
content: Text(state.message),
|
||||||
SnackBar(
|
|
||||||
content: Text(state.errMessage),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Navigator.of(context)
|
|
||||||
.popUntil((route) => route.settings.name == Routes.authLogin);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
builder: (context, state) {
|
|
||||||
return AnnotatedRegion(
|
|
||||||
value: SystemUiOverlayStyle(
|
|
||||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
|
||||||
statusBarIconBrightness: Brightness.light,
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
child: Scaffold(
|
|
||||||
backgroundColor: ColorsManager.backgroundColor,
|
|
||||||
extendBodyBehindAppBar: true,
|
|
||||||
extendBody: true,
|
|
||||||
appBar: HomeCubit.getInstance().spaces != null
|
|
||||||
? const DefaultAppBar()
|
|
||||||
: null,
|
|
||||||
body: const AppBody(),
|
|
||||||
bottomNavigationBar: const DefaultNavBar(),
|
|
||||||
// floatingActionButton: FloatingActionButton(
|
|
||||||
// onPressed: () {
|
|
||||||
// Navigator.push(
|
|
||||||
// context,
|
|
||||||
// CustomPageRoute(
|
|
||||||
// builder: (context) =>
|
|
||||||
// const ThreeGangSwitchesView(),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// child: const Icon(Icons.arrow_forward_ios_sharp),
|
|
||||||
// ),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
Navigator.of(context)
|
||||||
),
|
.popUntil((route) => route.settings.name == Routes.authLogin);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (context, state) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => HomeCubit.getInstance(),
|
||||||
|
child: BlocConsumer<HomeCubit, HomeState>(
|
||||||
|
listener: (context, state) async {
|
||||||
|
if (state is GetSpacesError) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(state.errMessage),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.of(context).popUntil(
|
||||||
|
(route) => route.settings.name == Routes.authLogin);
|
||||||
|
}
|
||||||
|
String? token = await const FlutterSecureStorage()
|
||||||
|
.read(key: Token.loginAccessTokenKey);
|
||||||
|
if (token == null) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
Navigator.of(context).popAndPushNamed(Routes.authLogin);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
builder: (context, state) {
|
||||||
|
return AnnotatedRegion(
|
||||||
|
value: SystemUiOverlayStyle(
|
||||||
|
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||||
|
statusBarIconBrightness: Brightness.light,
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: ColorsManager.backgroundColor,
|
||||||
|
extendBodyBehindAppBar: true,
|
||||||
|
extendBody: true,
|
||||||
|
appBar: HomeCubit.getInstance().spaces != null
|
||||||
|
? const DefaultAppBar()
|
||||||
|
: null,
|
||||||
|
body: const AppBody(),
|
||||||
|
bottomNavigationBar: const DefaultNavBar(),
|
||||||
|
// floatingActionButton: FloatingActionButton(
|
||||||
|
// onPressed: () {
|
||||||
|
// Navigator.push(
|
||||||
|
// context,
|
||||||
|
// CustomPageRoute(
|
||||||
|
// builder: (context) =>
|
||||||
|
// const ThreeGangSwitchesView(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// child: const Icon(Icons.arrow_forward_ios_sharp),
|
||||||
|
// ),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
|
||||||
|
|
||||||
import 'package:syncrow_app/features/auth/model/login_with_email_model.dart';
|
import 'package:syncrow_app/features/auth/model/login_with_email_model.dart';
|
||||||
import 'package:syncrow_app/features/auth/model/token.dart';
|
import 'package:syncrow_app/features/auth/model/token.dart';
|
||||||
import 'package:syncrow_app/features/auth/model/user_model.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/services/api/authentication_api.dart';
|
||||||
import 'package:syncrow_app/services/api/network_exception.dart';
|
import 'package:syncrow_app/services/api/network_exception.dart';
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class AuthCubit extends Cubit<AuthState> {
|
|||||||
static Token token = Token.emptyConstructor();
|
static Token token = Token.emptyConstructor();
|
||||||
|
|
||||||
login() async {
|
login() async {
|
||||||
emit(AuthLoading());
|
emit(AuthLoginLoading());
|
||||||
try {
|
try {
|
||||||
token = await AuthenticationAPI.loginWithEmail(
|
token = await AuthenticationAPI.loginWithEmail(
|
||||||
model: LoginWithEmailModel(
|
model: LoginWithEmailModel(
|
||||||
@ -60,24 +60,27 @@ class AuthCubit extends Cubit<AuthState> {
|
|||||||
key: UserModel.userUuidKey,
|
key: UserModel.userUuidKey,
|
||||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||||
user = UserModel.fromToken(token);
|
user = UserModel.fromToken(token);
|
||||||
emit(AuthSuccess());
|
emailController.clear();
|
||||||
|
passwordController.clear();
|
||||||
|
emit(AuthLoginSuccess());
|
||||||
} else {
|
} else {
|
||||||
emit(AuthError('Something went wrong'));
|
emit(AuthLoginError(message: 'Something went wrong'));
|
||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on ServerFailure catch (failure) {
|
||||||
emit(AuthError(ServerFailure.fromDioError(e).toString()));
|
emit(AuthError(message: failure.errMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logout() async {
|
logout() async {
|
||||||
emit(AuthLoading());
|
emit(AuthLogoutLoading());
|
||||||
try {
|
try {
|
||||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
await storage.delete(key: Token.loginAccessTokenKey);
|
await storage.delete(key: Token.loginAccessTokenKey);
|
||||||
HomeCubit.clear();
|
NavigationService.navigatorKey.currentState!
|
||||||
emit(AuthLoggedOut());
|
.popAndPushNamed(Routes.authLogin);
|
||||||
} on DioException catch (e) {
|
emit(AuthLogoutSuccess());
|
||||||
emit(AuthError(ServerFailure.fromDioError(e).errMessage));
|
} on ServerFailure catch (failure) {
|
||||||
|
emit(AuthError(message: failure.errMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +90,7 @@ class AuthCubit extends Cubit<AuthState> {
|
|||||||
await const FlutterSecureStorage().read(key: Token.loginAccessTokenKey);
|
await const FlutterSecureStorage().read(key: Token.loginAccessTokenKey);
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
emit(AuthTokenError("Token not found"));
|
emit(AuthTokenError(message: "Token not found"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,10 +103,17 @@ class AuthCubit extends Cubit<AuthState> {
|
|||||||
if (currentTime < exp) {
|
if (currentTime < exp) {
|
||||||
emit(AuthTokenSuccess());
|
emit(AuthTokenSuccess());
|
||||||
} else {
|
} else {
|
||||||
emit(AuthTokenError("Token expired"));
|
emit(AuthTokenError(message: "Token expired"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emit(AuthTokenError("Something went wrong"));
|
emit(AuthTokenError(message: "Something went wrong"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void logUserOut() async {
|
||||||
|
user = null;
|
||||||
|
token = Token.emptyConstructor();
|
||||||
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
|
await storage.delete(key: Token.loginAccessTokenKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,21 +32,25 @@ class LoginForm extends StatelessWidget {
|
|||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (state is! AuthTokenError) {
|
if (state is! AuthTokenError) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.isEmpty) {
|
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';
|
return 'Please enter your email';
|
||||||
}
|
}
|
||||||
//Regex for email validation
|
} else {
|
||||||
if (!RegExp(
|
return 'Please enter your email';
|
||||||
r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$')
|
|
||||||
.hasMatch(value)) {
|
|
||||||
return 'Please enter a valid email';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onTapOutside: (event) {
|
onTapOutside: (event) {
|
||||||
formKey.currentState!.validate();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
decoration: defaultInputDecoration(context,
|
decoration: defaultInputDecoration(context,
|
||||||
hint: "Example@email.com"),
|
hint: "Example@email.com"),
|
||||||
@ -62,9 +66,19 @@ class LoginForm extends StatelessWidget {
|
|||||||
if (state is! AuthTokenError) {
|
if (state is! AuthTokenError) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
if (value.length < 6) {
|
return null;
|
||||||
return 'Password must be at least 8 characters';
|
//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 {
|
} else {
|
||||||
return 'Please enter your password';
|
return 'Please enter your password';
|
||||||
}
|
}
|
||||||
@ -73,7 +87,7 @@ class LoginForm extends StatelessWidget {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onTapOutside: (event) {
|
onTapOutside: (event) {
|
||||||
formKey.currentState!.validate();
|
FocusScope.of(context).unfocus();
|
||||||
},
|
},
|
||||||
obscureText: !AuthCubit.get(context).isPasswordVisible,
|
obscureText: !AuthCubit.get(context).isPasswordVisible,
|
||||||
decoration: defaultInputDecoration(context,
|
decoration: defaultInputDecoration(context,
|
||||||
@ -88,8 +102,16 @@ class LoginForm extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
isDone: state is AuthSuccess,
|
isDone: state is AuthLoginSuccess,
|
||||||
isLoading: state is AuthLoading,
|
isLoading: state is AuthLoading,
|
||||||
|
// enabled: AuthCubit.get(context)
|
||||||
|
// .emailController
|
||||||
|
// .text
|
||||||
|
// .isNotEmpty &&
|
||||||
|
// AuthCubit.get(context)
|
||||||
|
// .passwordController
|
||||||
|
// .text
|
||||||
|
// .isNotEmpty,
|
||||||
customButtonStyle: ButtonStyle(
|
customButtonStyle: ButtonStyle(
|
||||||
backgroundColor: MaterialStateProperty.all(
|
backgroundColor: MaterialStateProperty.all(
|
||||||
Colors.black.withOpacity(.25),
|
Colors.black.withOpacity(.25),
|
||||||
@ -103,8 +125,10 @@ class LoginForm extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (formKey.currentState!.validate()) {
|
if (formKey.currentState!.validate()) {
|
||||||
AuthCubit.get(context).login();
|
if (state is! AuthLoginLoading) {
|
||||||
FocusScope.of(context).unfocus();
|
AuthCubit.get(context).login();
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -31,12 +31,9 @@ class DevicesCubit extends Cubit<DevicesState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool _isClosed = false;
|
|
||||||
|
|
||||||
static DevicesCubit? _instance;
|
static DevicesCubit? _instance;
|
||||||
static DevicesCubit getInstance() {
|
static DevicesCubit getInstance() {
|
||||||
print('device cubit instance found : ${_instance != null}');
|
|
||||||
print('selected space : ${HomeCubit.getInstance().selectedSpace != null}');
|
|
||||||
return _instance ??= DevicesCubit._();
|
return _instance ??= DevicesCubit._();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +41,6 @@ class DevicesCubit extends Cubit<DevicesState> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() {
|
||||||
_isClosed = true;
|
|
||||||
_instance = null;
|
_instance = null;
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
@ -272,56 +268,37 @@ class DevicesCubit extends Cubit<DevicesState> {
|
|||||||
emitSafe(DeviceControlError('Failed to control the device'));
|
emitSafe(DeviceControlError('Failed to control the device'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} on DioException catch (e) {
|
} on ServerFailure catch (failure) {
|
||||||
emitSafe(DeviceControlError(ServerFailure.fromDioError(e).errMessage));
|
emitSafe(DeviceControlError(failure.errMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchGroups(int spaceId) async {
|
fetchGroups(int spaceId) async {
|
||||||
if (_isClosed) return;
|
emitSafe(DevicesCategoriesLoading());
|
||||||
|
allCategories = await DevicesAPI.fetchGroups(spaceId);
|
||||||
try {
|
emitSafe(DevicesCategoriesSuccess());
|
||||||
emitSafe(DevicesCategoriesLoading());
|
|
||||||
allCategories = await DevicesAPI.fetchGroups(spaceId);
|
|
||||||
emitSafe(DevicesCategoriesSuccess());
|
|
||||||
} on DioException catch (error) {
|
|
||||||
emitSafe(
|
|
||||||
DevicesCategoriesError(ServerFailure.fromDioError(error).errMessage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchDevicesByRoomId(int? roomId) async {
|
fetchDevicesByRoomId(int? roomId) async {
|
||||||
if (_isClosed) return;
|
|
||||||
if (roomId == null) return;
|
if (roomId == null) return;
|
||||||
|
|
||||||
try {
|
emitSafe(GetDevicesLoading());
|
||||||
emitSafe(GetDevicesLoading());
|
int roomIndex = HomeCubit.getInstance()
|
||||||
int roomIndex = HomeCubit.getInstance()
|
.selectedSpace!
|
||||||
.selectedSpace!
|
.rooms!
|
||||||
.rooms!
|
.indexWhere((element) => element.id == roomId);
|
||||||
.indexWhere((element) => element.id == roomId);
|
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices =
|
||||||
HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices =
|
await SpacesAPI.getDevicesByRoomId(roomId);
|
||||||
await SpacesAPI.getDevicesByRoomId(roomId);
|
//get status for each device
|
||||||
//get status for each device
|
for (var device
|
||||||
for (var device in HomeCubit.getInstance()
|
in HomeCubit.getInstance().selectedSpace!.rooms![roomIndex].devices!) {
|
||||||
.selectedSpace!
|
getDevicesStatues(device.id!, roomIndex);
|
||||||
.rooms![roomIndex]
|
|
||||||
.devices!) {
|
|
||||||
getDevicesStatues(device.id!, roomIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
emitSafe(GetDevicesSuccess());
|
|
||||||
} on DioException catch (error) {
|
|
||||||
emitSafe(
|
|
||||||
GetDevicesError(ServerFailure.fromDioError(error).errMessage),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitSafe(GetDevicesSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
getDevicesStatues(String deviceId, int roomIndex, {String? code}) async {
|
getDevicesStatues(String deviceId, int roomIndex, {String? code}) async {
|
||||||
if (_isClosed) return;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
emitSafe(GetDeviceStatusLoading(code: code));
|
emitSafe(GetDeviceStatusLoading(code: code));
|
||||||
int deviceIndex = HomeCubit.getInstance()
|
int deviceIndex = HomeCubit.getInstance()
|
||||||
@ -345,9 +322,9 @@ class DevicesCubit extends Cubit<DevicesState> {
|
|||||||
.devices![deviceIndex]
|
.devices![deviceIndex]
|
||||||
.status = statuses;
|
.status = statuses;
|
||||||
emitSafe(GetDeviceStatusSuccess(code: code));
|
emitSafe(GetDeviceStatusSuccess(code: code));
|
||||||
} on DioException catch (error) {
|
} on ServerFailure catch (failure) {
|
||||||
emitSafe(
|
emitSafe(
|
||||||
GetDeviceStatusError(ServerFailure.fromDioError(error).errMessage),
|
GetDeviceStatusError(failure.errMessage),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||||
|
import 'package:syncrow_app/navigation/navigation_service.dart';
|
||||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||||
import 'package:syncrow_app/utils/resource_manager/theme_manager.dart';
|
import 'package:syncrow_app/utils/resource_manager/theme_manager.dart';
|
||||||
@ -21,6 +22,8 @@ class MyApp extends StatelessWidget {
|
|||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => AuthCubit(),
|
create: (context) => AuthCubit(),
|
||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
|
navigatorKey: NavigationService.navigatorKey,
|
||||||
|
scaffoldMessengerKey: NavigationService.snackbarKey,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager.primaryColor,
|
||||||
title: 'Syncrow App',
|
title: 'Syncrow App',
|
||||||
|
7
lib/navigation/navigation_service.dart
Normal file
7
lib/navigation/navigation_service.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NavigationService {
|
||||||
|
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
static GlobalKey<ScaffoldMessengerState>? snackbarKey =
|
||||||
|
GlobalKey<ScaffoldMessengerState>();
|
||||||
|
}
|
@ -1,46 +1,63 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||||
import 'package:syncrow_app/features/auth/model/token.dart';
|
import 'package:syncrow_app/features/auth/model/token.dart';
|
||||||
|
import 'package:syncrow_app/navigation/navigation_service.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:developer' as developer;
|
||||||
|
import 'package:syncrow_app/services/api/network_exception.dart';
|
||||||
|
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||||
|
|
||||||
class HTTPInterceptor extends InterceptorsWrapper {
|
class HTTPInterceptor extends InterceptorsWrapper {
|
||||||
// @override
|
@override
|
||||||
// void onResponse(Response response, ResponseInterceptorHandler handler) async {
|
@override
|
||||||
// // Pass the response to the next interceptor or response handler.
|
void onResponse(Response response, ResponseInterceptorHandler handler) async {
|
||||||
// return handler.next(response);
|
return handler.next(response);
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
@override
|
@override
|
||||||
void onRequest(
|
void onRequest(
|
||||||
RequestOptions options, RequestInterceptorHandler handler) async {
|
RequestOptions options, RequestInterceptorHandler handler) async {
|
||||||
var storage = const FlutterSecureStorage();
|
var storage = const FlutterSecureStorage();
|
||||||
var token = await storage.read(key: Token.loginAccessTokenKey);
|
var token = await storage.read(key: Token.loginAccessTokenKey);
|
||||||
|
// options.headers['Authorization'] = 'Bearer $token';
|
||||||
options.headers['Authorization'] = 'Bearer $token';
|
options.headers['Authorization'] = 'Bearer ${'${token!}123'}';
|
||||||
super.onRequest(options, handler);
|
super.onRequest(options, handler);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// @override
|
@override
|
||||||
// void onError(DioException err, ErrorInterceptorHandler handler) async {
|
void onError(DioException err, ErrorInterceptorHandler handler) async {
|
||||||
// // TODO: Implement error handling logic.
|
developer.log('Error Message: ${err.message}');
|
||||||
// // This method is called when an error occurs during a request.
|
developer.log('Error res Code: ${err.response?.statusCode}');
|
||||||
// super.onError(err, handler);
|
developer.log('Error res Data: ${err.response?.data}');
|
||||||
// }
|
developer.log('Error res status message: ${err.response?.statusMessage}');
|
||||||
//
|
|
||||||
// /// Validates the response and returns true if it is successful (status code 2xx).
|
ServerFailure failure = ServerFailure.fromDioError(err);
|
||||||
// Future<bool> validateResponse(Response response) async {
|
CustomSnackBar.displaySnackBar(failure.toString());
|
||||||
// if (response.statusCode != null) {
|
var storage = const FlutterSecureStorage();
|
||||||
// if (response.statusCode! >= 200 && response.statusCode! < 300) {
|
var token = await storage.read(key: Token.loginAccessTokenKey);
|
||||||
// // If the response status code is within the successful range (2xx),
|
if (err.response?.statusCode == 401 && token != null) {
|
||||||
// // return true indicating a successful response.
|
await AuthCubit.get(NavigationService.navigatorKey.currentContext!)
|
||||||
// return true;
|
.logout();
|
||||||
// } else {
|
super.onError(err, handler);
|
||||||
// // If the response status code is not within the successful range (2xx),
|
}
|
||||||
// // return false indicating an unsuccessful response.
|
}
|
||||||
// return false;
|
|
||||||
// }
|
/// Validates the response and returns true if it is successful (status code 2xx).
|
||||||
// } else {
|
Future<bool> validateResponse(Response response) async {
|
||||||
// // If the response status code is null, return false indicating an unsuccessful response.
|
if (response.statusCode != null) {
|
||||||
// return false;
|
if (response.statusCode! >= 200 && response.statusCode! < 300) {
|
||||||
// }
|
// If the response status code is within the successful range (2xx),
|
||||||
// }
|
// return true indicating a successful response.
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If the response status code is not within the successful range (2xx),
|
||||||
|
// return false indicating an unsuccessful response.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the response status code is null, return false indicating an unsuccessful response.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +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'].toString()
|
dioError.response!.data['message']);
|
||||||
// message
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case DioExceptionType.cancel:
|
case DioExceptionType.cancel:
|
||||||
return ServerFailure("The request to ApiServer was canceled");
|
return ServerFailure("The request to ApiServer was canceled");
|
||||||
@ -50,31 +48,27 @@ class ServerFailure extends Failure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ServerFailure.fromResponse(int statusCode, dynamic response) {
|
factory ServerFailure.fromResponse(int? statusCode, dynamic response) {
|
||||||
if (statusCode == 401 || statusCode == 403) {
|
switch (statusCode) {
|
||||||
return ServerFailure(response);
|
case 401:
|
||||||
} else if (statusCode == 400) {
|
case 403:
|
||||||
//response is list of errors
|
return ServerFailure(response);
|
||||||
List<String> errors = [];
|
case 400:
|
||||||
response.forEach((element) {
|
List<String> errors = [];
|
||||||
errors.add(element);
|
if (response['message'] is List) {
|
||||||
});
|
for (var error in response['message']) {
|
||||||
return ServerFailure(errors.join('\n'));
|
errors.add(error);
|
||||||
} else if (statusCode == 404) {
|
}
|
||||||
return ServerFailure("Your request not found, Please try later!");
|
} else {
|
||||||
} else if (statusCode == 500) {
|
errors.add(response['message']);
|
||||||
return ServerFailure(response);
|
}
|
||||||
} else {
|
return ServerFailure(errors.join('\n'));
|
||||||
return ServerFailure("Opps there was an Error, Please try again!");
|
case 404:
|
||||||
|
return ServerFailure("Your request not found, Please try later!");
|
||||||
|
case 500:
|
||||||
|
return ServerFailure(response);
|
||||||
|
default:
|
||||||
|
return ServerFailure("Opps there was an Error, Please try again!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResponseFailure extends Failure {
|
|
||||||
ResponseFailure(super.errMessage);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return errMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
40
lib/utils/helpers/snack_bar.dart
Normal file
40
lib/utils/helpers/snack_bar.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_app/navigation/navigation_service.dart';
|
||||||
|
|
||||||
|
class CustomSnackBar {
|
||||||
|
static displaySnackBar(String message) {
|
||||||
|
final key = NavigationService.snackbarKey;
|
||||||
|
if (key != null) {
|
||||||
|
final snackBar = SnackBar(content: Text(message));
|
||||||
|
key.currentState?.clearSnackBars();
|
||||||
|
key.currentState?.showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static greenSnackBar(String message) {
|
||||||
|
final key = NavigationService.snackbarKey;
|
||||||
|
BuildContext? currentContext = key?.currentContext;
|
||||||
|
if (key != null && currentContext != null) {
|
||||||
|
final snackBar = SnackBar(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
content: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
message,
|
||||||
|
style: Theme.of(currentContext).textTheme.bodySmall!.copyWith(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.w500, color: Colors.green),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
key.currentState?.showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user