diff --git a/lib/features/app_layout/bloc/nav_cubit.dart b/lib/features/app_layout/bloc/nav_cubit.dart index 87a1967..ebcf53a 100644 --- a/lib/features/app_layout/bloc/nav_cubit.dart +++ b/lib/features/app_layout/bloc/nav_cubit.dart @@ -16,6 +16,13 @@ class NavCubit extends Cubit { NavCubit() : super(NavInitial()); static NavCubit of(context) => BlocProvider.of(context); + + //functoin to do the important work when the user logs out + static clear() { + pageIndex = 0; + pageController.jumpToPage(0); + } + static int pageIndex = 0; static Map> appBarActions = { @@ -139,11 +146,10 @@ class NavCubit extends Cubit { const MenuView(), ]; - final PageController pageController = PageController(); + static final PageController pageController = PageController(); void updatePageIndex(int index) { pageIndex = index; - print('index: $index'); pageController.animateToPage(index, duration: const Duration(milliseconds: 150), curve: Curves.easeIn); emit(NavChangePage()); diff --git a/lib/features/app_layout/bloc/spaces_cubit.dart b/lib/features/app_layout/bloc/spaces_cubit.dart index 995a165..f9c7e50 100644 --- a/lib/features/app_layout/bloc/spaces_cubit.dart +++ b/lib/features/app_layout/bloc/spaces_cubit.dart @@ -10,7 +10,11 @@ part 'spaces_state.dart'; class SpacesCubit extends Cubit { SpacesCubit() : super(SpacesInitial()) { - fetchSpaces(); + fetchSpaces().then((value) { + if (selectedSpace != null) { + fetchRooms(selectedSpace!); + } + }); } static SpacesCubit get(context) => BlocProvider.of(context); @@ -83,12 +87,24 @@ class SpacesCubit extends Cubit { emit(SpacesLoading()); try { spaces = await SpacesAPI.getSpaces(); + spaces.isNotEmpty ? selectSpace(spaces.first) : null; emit(SpacesLoaded(spaces)); } on DioException catch (e) { - emit(SpacesError(e.message ?? "Something went wrong")); - throw ServerFailure.fromDioError(e); - } catch (e) { - emit(SpacesError(e.toString())); + emit(SpacesError(ServerFailure.fromDioError(e).errMessage)); + } + } + + fetchRooms(SpaceModel space) async { + emit(SpaceRoomsLoading()); + try { + space.rooms = await SpacesAPI.getRooms(space.id!); + if (space.rooms != null) { + emit(SpaceRoomsLoaded(space.rooms!)); + } else { + emit(SpaceRoomsError("No rooms found")); + } + } on DioException catch (e) { + emit(SpacesError(ServerFailure.fromDioError(e).errMessage)); } } } diff --git a/lib/features/app_layout/bloc/spaces_state.dart b/lib/features/app_layout/bloc/spaces_state.dart index f10d2b9..8fd63d4 100644 --- a/lib/features/app_layout/bloc/spaces_state.dart +++ b/lib/features/app_layout/bloc/spaces_state.dart @@ -18,6 +18,18 @@ class SpacesError extends SpacesState { SpacesError(this.errMessage); } +class SpaceRoomsLoading extends SpacesLoading {} + +class SpaceRoomsLoaded extends SpacesLoading { + final List rooms; + + SpaceRoomsLoaded(this.rooms); +} + +class SpaceRoomsError extends SpacesError { + SpaceRoomsError(super.errMessage); +} + class SpacesSelected extends SpacesState { final SpaceModel space; diff --git a/lib/features/app_layout/model/space_model.dart b/lib/features/app_layout/model/space_model.dart index f94be8e..7205ebf 100644 --- a/lib/features/app_layout/model/space_model.dart +++ b/lib/features/app_layout/model/space_model.dart @@ -3,7 +3,7 @@ import 'package:syncrow_app/features/devices/model/room_model.dart'; class SpaceModel { final int? id; final String? name; - final List? rooms; + late List? rooms; SpaceModel({ required this.id, diff --git a/lib/features/app_layout/view/app_layout.dart b/lib/features/app_layout/view/app_layout.dart index 788fe9a..bce053e 100644 --- a/lib/features/app_layout/view/app_layout.dart +++ b/lib/features/app_layout/view/app_layout.dart @@ -6,6 +6,7 @@ import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.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_nav_bar.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class AppLayout extends StatelessWidget { @@ -13,30 +14,51 @@ class AppLayout extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return AnnotatedRegion( - value: SystemUiOverlayStyle( - statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), - statusBarIconBrightness: Brightness.light, - ), - child: SafeArea( - child: BlocBuilder( - builder: (context, state) { - return Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: state is SpacesLoaded ? DefaultAppBar(context) : null, - body: const AppBody(), - bottomNavigationBar: - state is SpacesLoaded ? const DefaultNavBar() : null, - ); - }, - ), - ), - ); - }, + return BlocProvider( + create: (context) => SpacesCubit(), + child: BlocListener( + listener: (context, state) { + if (state is SpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errMessage), + ), + ); + Navigator.of(context) + .popUntil((route) => route.settings.name == Routes.authLogin); + } + }, + child: BlocBuilder( + builder: (context, state) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: SafeArea( + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: + state is! SpacesLoading || state is! SpaceRoomsLoading + ? const DefaultAppBar() + : null, + body: const AppBody(), + bottomNavigationBar: + state is! SpacesLoading || state is! SpaceRoomsLoading + ? const DefaultNavBar() + : null, + ); + }, + ), + ), + ); + }, + ), + ), ); } } diff --git a/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart b/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart index 0d07911..b5b1ab3 100644 --- a/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart +++ b/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart @@ -28,10 +28,10 @@ class AppBarHomeDropdown extends StatelessWidget { underline: const SizedBox.shrink(), padding: const EdgeInsets.all(0), borderRadius: BorderRadius.circular(20), - value: SpacesCubit.get(context).selectedSpace, + value: SpacesCubit.get(context).selectedSpace!.id, items: SpacesCubit.spaces.map((space) { return DropdownMenuItem( - value: space, + value: space.id, child: SizedBox( width: 100, child: Row( @@ -50,7 +50,7 @@ class AppBarHomeDropdown extends StatelessWidget { const SizedBox(width: 5), Expanded( child: BodyMedium( - text: space.name ?? "", + text: space.name ?? "??", style: context.bodyMedium.copyWith( fontSize: 15, color: ColorsManager.textPrimaryColor, @@ -64,7 +64,10 @@ class AppBarHomeDropdown extends StatelessWidget { ); }).toList(), onChanged: (value) { - SpacesCubit.get(context).selectSpace(value!); + if (value != null) { + SpacesCubit.get(context).selectSpace(SpacesCubit.spaces + .firstWhere((element) => element.id == value)); + } }, ), ); diff --git a/lib/features/app_layout/view/widgets/app_body.dart b/lib/features/app_layout/view/widgets/app_body.dart index da10b5f..49cec88 100644 --- a/lib/features/app_layout/view/widgets/app_body.dart +++ b/lib/features/app_layout/view/widgets/app_body.dart @@ -15,39 +15,38 @@ class AppBody extends StatelessWidget { return BlocBuilder( builder: (context, state) { return Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, - ), - fit: BoxFit.cover, - opacity: 0.4, + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.imagesBackground, ), + fit: BoxFit.cover, + opacity: 0.4, ), - child: BlocConsumer( - listener: (context, state) { - if (state is SpacesError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.errMessage), - ), - ); - } - }, - builder: (context, state) { - return state is! SpacesLoading - ? PageView( - physics: const NeverScrollableScrollPhysics(), - controller: NavCubit.of(context).pageController, - children: NavCubit.of(context).pages, - ) - : const Center(child: CircularProgressIndicator()); - }, - ) - // NavCubit.of(context).currentPage, - ); + ), + child: BlocConsumer( + listener: (context, state) { + if (state is SpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errMessage), + ), + ); + } + }, + builder: (context, state) { + return state is! SpacesLoading || state is! SpaceRoomsLoading + ? PageView( + physics: const NeverScrollableScrollPhysics(), + controller: NavCubit.pageController, + children: NavCubit.of(context).pages, + ) + : const Center(child: CircularProgressIndicator()); + }, + ), + ); }, ); } diff --git a/lib/features/app_layout/view/widgets/default_app_bar.dart b/lib/features/app_layout/view/widgets/default_app_bar.dart index e280bc1..1eb8beb 100644 --- a/lib/features/app_layout/view/widgets/default_app_bar.dart +++ b/lib/features/app_layout/view/widgets/default_app_bar.dart @@ -4,9 +4,7 @@ import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { - const DefaultAppBar(this.context, {super.key}); - - final BuildContext context; + const DefaultAppBar({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index e78775b..49a924d 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.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/app_layout/bloc/nav_cubit.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/user_model.dart'; @@ -20,7 +21,7 @@ class AuthCubit extends Cubit { TextEditingController passwordController = TextEditingController(); bool isPasswordVisible = false; - GlobalKey formKey = GlobalKey(); + static GlobalKey formKey = GlobalKey(); void changePasswordVisibility() { isPasswordVisible = !isPasswordVisible; @@ -54,15 +55,16 @@ class AuthCubit extends Cubit { 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); emit(AuthSuccess()); } else { emit(AuthError('Something went wrong')); } } on DioException catch (e) { - emit(AuthError(e.message ?? "Something went wrong")); - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).errMessage)); } } @@ -71,9 +73,10 @@ class AuthCubit extends Cubit { try { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.delete(key: Token.loginAccessTokenKey); + NavCubit.clear(); emit(AuthLoggedOut()); } on DioException catch (e) { - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).errMessage)); } } } diff --git a/lib/features/auth/model/token.dart b/lib/features/auth/model/token.dart index 5f3b22b..0c428a7 100644 --- a/lib/features/auth/model/token.dart +++ b/lib/features/auth/model/token.dart @@ -10,9 +10,26 @@ class Token { final String accessToken; final String refreshToken; + //{ + // "email": "test@test.com", + // "userId": 3, + // "uuid": "563e22d2-cb30-46d3-8c48-fa7d762342f0", + // "sessionId": "f76aa067-c915-4921-b04d-9fbc71c4965a", + // "iat": 1710137435, + // "exp": 1710137735 + // } + + final String sessionId; + + final int iat; + final int exp; + Token.emptyConstructor() : accessToken = '', - refreshToken = ''; + refreshToken = '', + sessionId = '', + iat = 0, + exp = 0; bool get accessTokenIsNotEmpty => accessToken.isNotEmpty; @@ -23,9 +40,16 @@ class Token { Token( this.accessToken, this.refreshToken, + this.sessionId, + this.iat, + this.exp, ); - Token.refreshToken(this.refreshToken) : accessToken = ''; + Token.refreshToken(this.refreshToken) + : accessToken = '', + sessionId = '', + iat = 0, + exp = 0; factory Token.fromJson(Map json) { //save token to secure storage @@ -34,15 +58,16 @@ class Token { key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? ''); //create token object ? - return Token( - json[loginAccessTokenKey] ?? '', json[loginRefreshTokenKey] ?? ''); + return Token(json[loginAccessTokenKey] ?? '', + json[loginRefreshTokenKey] ?? '', '', 0, 0); } - Map toJson() => {loginRefreshTokenKey: refreshToken}; + Map refreshTokenToJson() => + {loginRefreshTokenKey: refreshToken}; Map accessTokenToJson() => {loginAccessTokenKey: accessToken}; - Map decodeToken() { + static Map decodeToken(String accessToken) { final parts = accessToken.split('.'); if (parts.length != 3) { throw Exception('invalid access token'); diff --git a/lib/features/auth/model/user_model.dart b/lib/features/auth/model/user_model.dart index beafad5..5491d73 100644 --- a/lib/features/auth/model/user_model.dart +++ b/lib/features/auth/model/user_model.dart @@ -1,6 +1,7 @@ import 'package:syncrow_app/features/auth/model/token.dart'; class UserModel { + static String userUuidKey = 'userUuid'; final String? uuid; final String? email; final String? name; @@ -36,13 +37,9 @@ class UserModel { //uuid to json - Map uuIdAsJson() => { - 'userUuid': uuid, - }; - //from token factory UserModel.fromToken(Token token) { - Map tempJson = token.decodeToken(); + Map tempJson = Token.decodeToken(token.accessToken); return UserModel( uuid: tempJson['uuid'].toString(), diff --git a/lib/features/auth/view/widgets/login/login_button.dart b/lib/features/auth/view/widgets/login/login_button.dart deleted file mode 100644 index 3e2f7e5..0000000 --- a/lib/features/auth/view/widgets/login/login_button.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; -import 'package:syncrow_app/features/shared_widgets/default_button.dart'; - -class LoginButton extends StatelessWidget { - const LoginButton({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: DefaultButton( - isDone: state is AuthSuccess, - isLoading: state is AuthLoading, - customButtonStyle: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - Colors.black.withOpacity(.25), - ), - foregroundColor: MaterialStateProperty.all( - Colors.white, - ), - ), - child: const Text( - 'Login', - ), - onPressed: () { - if (AuthCubit.get(context).formKey.currentState!.validate()) { - AuthCubit.get(context).login(); - FocusScope.of(context).unfocus(); - } - }, - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/features/auth/view/widgets/login/login_form.dart b/lib/features/auth/view/widgets/login/login_form.dart index 29984bc..3447f3e 100644 --- a/lib/features/auth/view/widgets/login/login_form.dart +++ b/lib/features/auth/view/widgets/login/login_form.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/auth/view/widgets/login/forget_password.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/utils/resource_manager/styles_manager.dart'; @@ -11,10 +13,11 @@ class LoginForm extends StatelessWidget { @override Widget build(BuildContext context) { + var formKey = GlobalKey(); return BlocBuilder( builder: (context, state) { return Form( - key: AuthCubit.get(context).formKey, + key: formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -40,7 +43,7 @@ class LoginForm extends StatelessWidget { return null; }, onTapOutside: (event) { - AuthCubit.get(context).formKey.currentState!.validate(); + formKey.currentState!.validate(); }, decoration: defaultInputDecoration(context, hint: "Example@email.com"), @@ -65,12 +68,44 @@ class LoginForm extends StatelessWidget { return null; }, onTapOutside: (event) { - AuthCubit.get(context).formKey.currentState!.validate(); + formKey.currentState!.validate(); }, obscureText: !AuthCubit.get(context).isPasswordVisible, decoration: defaultInputDecoration(context, hint: "At least 8 characters"), ), + const SizedBox(height: 10), + // const LoginUserAgreement(), + const ForgetPassword(), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: DefaultButton( + isDone: state is AuthSuccess, + isLoading: state is AuthLoading, + customButtonStyle: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Colors.black.withOpacity(.25), + ), + foregroundColor: MaterialStateProperty.all( + Colors.white, + ), + ), + child: const Text( + 'Login', + ), + onPressed: () { + if (formKey.currentState!.validate()) { + AuthCubit.get(context).login(); + FocusScope.of(context).unfocus(); + } + }, + ), + ), + ], + ) ], ), ), diff --git a/lib/features/auth/view/widgets/login/login_view.dart b/lib/features/auth/view/widgets/login/login_view.dart index 3520e39..34c00ab 100644 --- a/lib/features/auth/view/widgets/login/login_view.dart +++ b/lib/features/auth/view/widgets/login/login_view.dart @@ -3,8 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/dont_have_an_account.dart'; -import 'package:syncrow_app/features/auth/view/widgets/login/forget_password.dart'; -import 'package:syncrow_app/features/auth/view/widgets/login/login_button.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_divider.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_form.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_with_google_facebook.dart'; @@ -94,11 +92,6 @@ class LoginView extends StatelessWidget { height: 20, ), const LoginForm(), - const SizedBox(height: 10), - // const LoginUserAgreement(), - const ForgetPassword(), - const SizedBox(height: 10), - const LoginButton(), const LoginDivider(), const LoginWithGoogleFacebook(), const DontHaveAnAccount(), diff --git a/lib/features/devices/model/room_model.dart b/lib/features/devices/model/room_model.dart index 921eb36..3937f17 100644 --- a/lib/features/devices/model/room_model.dart +++ b/lib/features/devices/model/room_model.dart @@ -1,10 +1,10 @@ import 'package:syncrow_app/features/devices/model/device_category_model.dart'; class RoomModel { - final String id; - final String name; + final int? id; + final String? name; - final List categories; + final List? categories; RoomModel({ required this.id, @@ -22,8 +22,8 @@ class RoomModel { factory RoomModel.fromJson(Map json) { return RoomModel( - id: json['id'], - name: json['name'], + id: json['roomId'], + name: json['roomName'], categories: json['devices'], ); } diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 8b39984..4db3d30 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -19,12 +19,17 @@ class DevicesViewBody extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => DevicesCubit(), - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { - //TODO : move to NavigationCubit - return state is DevicesLoading - ? const Center(child: CircularProgressIndicator()) - : Padding( + return BlocBuilder( + builder: (context, state) { + print( + "length : ${SpacesCubit.get(context).selectedSpace!.rooms!.length}"); + //TODO : move to NavigationCubit + if (state is DevicesLoading) { + return const Center(child: CircularProgressIndicator()); + } else { + return Padding( padding: EdgeInsets.only( top: Constants.appBarHeight, bottom: Constants.bottomNavBarHeight, @@ -46,16 +51,21 @@ class DevicesViewBody extends StatelessWidget { }, children: [ const WizardPage(), - ...SpacesCubit.get(context) - .selectedSpace! - .rooms! - .map( - (room) { - return RoomPage( - room: room, - ); - }, - ) + if (SpacesCubit.get(context).selectedSpace != null) + if (SpacesCubit.get(context) + .selectedSpace! + .rooms != + null) + ...SpacesCubit.get(context) + .selectedSpace! + .rooms! + .map( + (room) { + return RoomPage( + room: room, + ); + }, + ) ], ), ), @@ -66,7 +76,11 @@ class DevicesViewBody extends StatelessWidget { child: SmoothPageIndicator( controller: SpacesCubit.get(context).devicesPageController, - count: 3, + count: SpacesCubit.get(context) + .selectedSpace! + .rooms! + .length + + 1, effect: const WormEffect( paintStyle: PaintingStyle.stroke, dotHeight: 8, @@ -77,6 +91,9 @@ class DevicesViewBody extends StatelessWidget { ], ), ); + } + }, + ); }, ), ); diff --git a/lib/features/devices/view/widgets/room_page.dart b/lib/features/devices/view/widgets/room_page.dart index 4c0455a..7f97b81 100644 --- a/lib/features/devices/view/widgets/room_page.dart +++ b/lib/features/devices/view/widgets/room_page.dart @@ -14,7 +14,7 @@ class RoomPage extends StatelessWidget { @override Widget build(BuildContext context) { List devices = []; - for (var category in room.categories) { + for (var category in room.categories ?? []) { devices.addAll(category.devices); } return Padding( diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index b7de8bd..c046d78 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -40,30 +40,33 @@ class RoomsSlider extends StatelessWidget { ), ), ), - ...SpacesCubit.get(context).selectedSpace!.rooms!.map( - (room) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: InkWell( - onTap: () { - SpacesCubit.get(context).roomSliderPageChanged( - SpacesCubit.get(context) - .selectedSpace! - .rooms! - .indexOf(room)); - }, - child: TitleMedium( - text: room.name, - style: context.titleMedium.copyWith( - fontSize: 25, - color: SpacesCubit.get(context).selectedRoom == room - ? ColorsManager.textPrimaryColor - : ColorsManager.textPrimaryColor - .withOpacity(.2), + if (SpacesCubit.get(context).selectedSpace != null) + if (SpacesCubit.get(context).selectedSpace!.rooms != null) + ...SpacesCubit.get(context).selectedSpace!.rooms!.map( + (room) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: InkWell( + onTap: () { + SpacesCubit.get(context).roomSliderPageChanged( + SpacesCubit.get(context) + .selectedSpace! + .rooms! + .indexOf(room)); + }, + child: TitleMedium( + text: room.name!, + style: context.titleMedium.copyWith( + fontSize: 25, + color: SpacesCubit.get(context).selectedRoom == + room + ? ColorsManager.textPrimaryColor + : ColorsManager.textPrimaryColor + .withOpacity(.2), + ), + ), ), ), - ), - ), - ) + ) ], ), ); diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index 5186421..0c65933 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -10,50 +10,77 @@ class SplashView extends StatelessWidget { @override Widget build(BuildContext context) { - //TODO remove this delay - Future.value().then((value) async { - var isLoggedIn = await const FlutterSecureStorage() - .read(key: Token.loginAccessTokenKey) != - null; - if (isLoggedIn) { - Navigator.pushReplacementNamed(context, Routes.homeRoute); - } else { - Navigator.pushReplacementNamed(context, Routes.authLogin); - } - }); - return Scaffold( - body: Stack( - alignment: Alignment.center, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, + return FutureBuilder( + future: getTokenAndValidate(context), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data ?? false) { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacementNamed(context, Routes.homeRoute); + }); + } else { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacementNamed(context, Routes.authLogin); + }); + } + } + return Scaffold( + body: Stack( + alignment: Alignment.center, + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.imagesBackground, + ), + fit: BoxFit.cover, + ), ), - fit: BoxFit.cover, ), - ), - ), - Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage(Assets.imagesVector), - fit: BoxFit.cover, - opacity: 0.9, + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(Assets.imagesVector), + fit: BoxFit.cover, + opacity: 0.9, + ), + ), ), - ), + SvgPicture.asset( + Assets.imagesLogo, + width: 240, + ) + ], ), - SvgPicture.asset( - Assets.imagesLogo, - width: 240, - ) - ], - ), + ); + }, ); } + + Future getTokenAndValidate(BuildContext context) async { + return await const FlutterSecureStorage() + .read(key: Token.loginAccessTokenKey) + .then((value) { + if (value == null) { + return false; + } + print("Decoding token Started"); + var tokenData = Token.decodeToken(value ?? ""); + print("checking token data"); + + if (tokenData.containsKey('exp')) { + var exp = tokenData['exp'] ?? 0; + var currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; + print('time: $currentTime exp: $exp'); + return currentTime < exp; + } else { + return false; + } + }); + } } diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index b3da647..72246f3 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -16,6 +16,12 @@ class Assets { static const String iconsDevices = 'assets/icons/Devices.svg'; static const String iconsDevicesFill = 'assets/icons/Devices-fill.svg'; static const String iconsDoorLock = 'assets/icons/doorLock.svg'; + static const String iconsDoorLockLinkage = 'assets/icons/DoorLockLinkage.svg'; + static const String iconsDoorLockLock = 'assets/icons/DoorLockLock.svg'; + static const String iconsDoorLockMembers = 'assets/icons/DoorLockMembers.svg'; + static const String iconsDoorLockPassword = + 'assets/icons/DoorLockPassword.svg'; + static const String iconsDoorLockRecords = 'assets/icons/DoorLockRecords.svg'; static const String iconsFacebook = 'assets/icons/Facebook.svg'; static const String iconsFan0 = 'assets/icons/fan-0.svg'; static const String iconsFan1 = 'assets/icons/fan-1.svg'; diff --git a/lib/my_app.dart b/lib/my_app.dart index b92aa33..154b9e6 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; -import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; @@ -30,9 +29,6 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => NavCubit(), ), - BlocProvider( - create: (context) => SpacesCubit(), - ), BlocProvider( create: (context) => DevicesCubit(), ), diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 7259ebb..4233712 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -12,4 +12,5 @@ abstract class ApiEndpoints { // Spaces static const String spaces = '$baseUrl/home'; + static const String rooms = '$baseUrl/room'; } diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index 06d062b..70f502c 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.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/verify_code.dart'; @@ -11,21 +10,22 @@ class AuthenticationAPI { path: ApiEndpoints.verifyOtp, body: data.toJson(), showServerMessage: false, - expectedResponseModel: (json) { - Token token = Token.fromJson(json); - return token; - }); + expectedResponseModel: (json) => Token.fromJson(json)); return response; } static Future loginWithEmail( {required LoginWithEmailModel model}) async { - final response = await HTTPService().post( - path: ApiEndpoints.login, - body: model.toJson(), - showServerMessage: false, - expectedResponseModel: (json) => Token.fromJson(json['data'])); - debugPrint("response: $response"); - return response; + try { + final response = await HTTPService().post( + path: ApiEndpoints.login, + body: model.toJson(), + showServerMessage: false, + expectedResponseModel: (json) => Token.fromJson(json['data'])); + // debugPrint("response: $response"); + return response; + } catch (e) { + rethrow; + } } } diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index cb018c7..fa22beb 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:syncrow_app/features/auth/model/token.dart'; class HTTPInterceptor extends InterceptorsWrapper { // @override @@ -11,9 +12,9 @@ class HTTPInterceptor extends InterceptorsWrapper { @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { - //pass the token from the flutter secure storage to the request header - - options.headers['Authorization'] = 'Bearer ${AuthCubit.token.accessToken}'; + var storage = FlutterSecureStorage(); + var token = await storage.read(key: Token.loginAccessTokenKey); + options.headers['Authorization'] = 'Bearer $token'; super.onRequest(options, handler); } diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index ecaeae7..531d4ba 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -17,8 +17,8 @@ class HTTPService { baseUrl: ApiEndpoints.baseUrl, receiveDataWhenStatusError: true, followRedirects: false, - connectTimeout: const Duration(seconds: 5), - receiveTimeout: const Duration(seconds: 5), + connectTimeout: const Duration(seconds: 30), + receiveTimeout: const Duration(seconds: 30), ), ); @@ -62,8 +62,8 @@ class HTTPService { queryParameters: queryParameters, options: options, ); - debugPrint("status code is ${response.statusCode}"); - debugPrint("response data is ${response.data}"); + // debugPrint("status code is ${response.statusCode}"); + // debugPrint("response data is ${response.data}"); return expectedResponseModel(response.data); } catch (error) { rethrow; diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index dad5df9..060ca78 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -1,26 +1,19 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/auth/model/user_model.dart'; +import 'package:syncrow_app/features/devices/model/room_model.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; class SpacesAPI { - // static Future loginWithEmail( - // {required LoginWithEmailModel model}) async { - // final response = await HTTPService().post( - // path: ApiEndpoints.login, - // body: model.toJson(), - // showServerMessage: false, - // expectedResponseModel: (json) { - // Token token = Token.fromJson(json['data']); - // return token; - // }); - // debugPrint("response: $response"); - // return response; - // } - static Future> getSpaces() async { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); final response = await HTTPService().get( - path: "${ApiEndpoints.spaces}/${AuthCubit.user!.uuid}", + path: ApiEndpoints.spaces, + queryParameters: { + "userUuid": uuid, + }, showServerMessage: false, expectedResponseModel: (json) { List spaces = []; @@ -32,4 +25,21 @@ class SpacesAPI { ); return response; } + + //get rooms by space id + static Future> getRooms(int spaceId) async { + 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; + } }