diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ecd784a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,48 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Iphone 15 Pro Max", + "request": "launch", + "type": "dart", + "deviceId": "0147FC23-3D6C-406A-BE2C-9E67BAF3DA9B" + }, + { + "name": "Iphone SE", + "request": "launch", + "type": "dart", + "deviceId": "DB3BAF09-EF89-4A31-8DD8-A81DC346CB43", + + }, + { + "name": "syncrow-app", + "request": "launch", + "type": "dart" + }, + { + "name": "syncrow-app (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "syncrow-app (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ], + "compounds": [ + { + "name": "All Device", + "configurations": [ + "Iphone 15 Pro Max", + "Iphone SE" + ] + } + ] +} \ No newline at end of file diff --git a/assets/icons/3GangSwitch.svg b/assets/icons/3GangSwitch.svg new file mode 100644 index 0000000..ecb9992 --- /dev/null +++ b/assets/icons/3GangSwitch.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/DoorLockLinkage.svg b/assets/icons/DoorLockLinkage.svg new file mode 100644 index 0000000..b9730e3 --- /dev/null +++ b/assets/icons/DoorLockLinkage.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/assets/icons/DoorLockLock.svg b/assets/icons/DoorLockLock.svg new file mode 100644 index 0000000..931e17f --- /dev/null +++ b/assets/icons/DoorLockLock.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/DoorLockMembers.svg b/assets/icons/DoorLockMembers.svg new file mode 100644 index 0000000..f7a60fb --- /dev/null +++ b/assets/icons/DoorLockMembers.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/icons/DoorLockPassword.svg b/assets/icons/DoorLockPassword.svg new file mode 100644 index 0000000..583035b --- /dev/null +++ b/assets/icons/DoorLockPassword.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/DoorLockRecords.svg b/assets/icons/DoorLockRecords.svg new file mode 100644 index 0000000..125f3d9 --- /dev/null +++ b/assets/icons/DoorLockRecords.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/icons/Vector-1.svg b/assets/icons/Vector-1.svg new file mode 100644 index 0000000..ce3fd9d --- /dev/null +++ b/assets/icons/Vector-1.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/Vector.svg b/assets/icons/Vector.svg new file mode 100644 index 0000000..2b26ff9 --- /dev/null +++ b/assets/icons/Vector.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/assets/icons/gang.svg b/assets/icons/gang.svg new file mode 100644 index 0000000..85e6e9b --- /dev/null +++ b/assets/icons/gang.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/lightSwitchOff.svg b/assets/icons/lightSwitchOff.svg new file mode 100644 index 0000000..b982c13 --- /dev/null +++ b/assets/icons/lightSwitchOff.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/lightSwitchOn.svg b/assets/icons/lightSwitchOn.svg new file mode 100644 index 0000000..e0245bc --- /dev/null +++ b/assets/icons/lightSwitchOn.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/sensors.svg b/assets/icons/sensors.svg new file mode 100644 index 0000000..7fbb150 --- /dev/null +++ b/assets/icons/sensors.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/lib/features/app_layout/bloc/nav_cubit.dart b/lib/features/app_layout/bloc/nav_cubit.dart index 87a1967..b485ad8 100644 --- a/lib/features/app_layout/bloc/nav_cubit.dart +++ b/lib/features/app_layout/bloc/nav_cubit.dart @@ -16,6 +16,11 @@ class NavCubit extends Cubit { NavCubit() : super(NavInitial()); static NavCubit of(context) => BlocProvider.of(context); + + static clear() { + pageIndex = 0; + } + static int pageIndex = 0; static Map> appBarActions = { @@ -139,13 +144,9 @@ class NavCubit extends Cubit { const MenuView(), ]; - 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 c090e93..f2c5dbb 100644 --- a/lib/features/app_layout/bloc/spaces_cubit.dart +++ b/lib/features/app_layout/bloc/spaces_cubit.dart @@ -1,210 +1,27 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; -import 'package:syncrow_app/features/devices/model/curtain_model.dart'; -import 'package:syncrow_app/features/devices/model/device_category_model.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; import 'package:syncrow_app/features/devices/model/room_model.dart'; -import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; -import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; -import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; -import 'package:syncrow_app/features/devices/view/widgets/lights/lights_view.dart'; -import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_view.dart'; -import 'package:syncrow_app/generated/assets.dart'; -import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/services/api/network_exception.dart'; +import 'package:syncrow_app/services/api/spaces_api.dart'; part 'spaces_state.dart'; class SpacesCubit extends Cubit { - SpacesCubit() : super(SpacesInitial()); + SpacesCubit() : super(SpacesInitial()) { + fetchSpaces().then((value) { + if (selectedSpace != null) { + fetchRooms(selectedSpace!); + } + }); + } static SpacesCubit get(context) => BlocProvider.of(context); - static List spaces = [ - SpaceModel( - id: '0', - name: 'Home', - rooms: [ - RoomModel(id: '0', name: 'Living Room', categories: [ - DevicesCategoryModel( - devices: [ - ACModel( - name: "Living Room AC", - id: '0', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ], - icon: Assets.iconsAC, - name: 'ACs', - type: DeviceType.AC, - page: const ACsView(), - ), - DevicesCategoryModel( - devices: [ - LightModel( - name: "Living Room Light", - id: '0', - status: false, - color: 0, - brightness: 20, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - ], - icon: Assets.iconsLight, - name: 'Lights', - type: DeviceType.Lights, - page: const LightsView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsDoorLock, - name: 'Doors', - type: DeviceType.Door, - page: const DoorView(), - ), - DevicesCategoryModel( - devices: [ - CurtainModel( - openPercentage: 10, - id: "1", - name: "Living Room Curtain", - status: false, - type: DeviceType.Curtain, - image: '', - timer: null, - ), - ], - icon: Assets.iconsCurtain, - name: 'Curtains', - type: DeviceType.Curtain, - page: const CurtainView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsGateway, - name: 'Gateway', - type: DeviceType.Gateway, - page: const GateWayView(), - ), - ]), - RoomModel(id: '1', name: 'Bedroom', categories: [ - DevicesCategoryModel( - devices: [ - ACModel( - name: "Living Room AC", - id: '0', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ], - icon: Assets.iconsAC, - name: 'ACs', - type: DeviceType.AC, - page: const ACsView(), - ), - DevicesCategoryModel( - devices: [ - LightModel( - name: "Living Room Light", - id: '0', - status: false, - color: 0, - brightness: 20, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - ], - icon: Assets.iconsLight, - name: 'Lights', - type: DeviceType.Lights, - page: const LightsView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsDoorLock, - name: 'Doors', - type: DeviceType.Door, - page: const DoorView(), - ), - DevicesCategoryModel( - devices: [ - CurtainModel( - openPercentage: 10, - id: "1", - name: "Living Room Curtain", - status: false, - type: DeviceType.Curtain, - image: '', - timer: null, - ), - ], - icon: Assets.iconsCurtain, - name: 'Curtains', - type: DeviceType.Curtain, - page: const CurtainView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsGateway, - name: 'Gateway', - type: DeviceType.Gateway, - page: const GateWayView(), - ), - ]), - ], - ), - SpaceModel( - id: '1', - name: 'Office', - rooms: [], - ), - SpaceModel( - id: '2', - name: 'Parent\'s House', - rooms: [], - ), - ]; + static List spaces = []; - SpaceModel selectedSpace = spaces.first; + static SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null; RoomModel? selectedRoom; @@ -229,9 +46,9 @@ class SpacesCubit extends Cubit { if (index == 0) { unselectRoom(); } else { - selectedRoom = selectedSpace.rooms[index - 1]; + selectedRoom = selectedSpace!.rooms![index - 1]; + emit(RoomSelected(selectedRoom!)); } - emit(RoomSelected(selectedRoom!)); } devicesPageChanged(int index) { @@ -241,12 +58,12 @@ class SpacesCubit extends Cubit { curve: Curves.linear, ); - if (index == 0) { + if (index <= 0) { unselectRoom(); } else { - selectedRoom = selectedSpace.rooms[index - 1]; + selectedRoom = selectedSpace!.rooms![index - 1]; + emit(RoomSelected(selectedRoom!)); } - emit(RoomSelected(selectedRoom!)); } unselectRoom() { @@ -266,5 +83,28 @@ class SpacesCubit extends Cubit { emit(RoomUnSelected()); } -//TODO implement the methods to fetch the spaces from the API + fetchSpaces() async { + emit(SpacesLoading()); + try { + spaces = await SpacesAPI.getSpaces(); + spaces.isNotEmpty ? selectSpace(spaces.first) : null; + emit(SpacesLoaded(spaces)); + } on DioException catch (e) { + 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 9d9c527..8fd63d4 100644 --- a/lib/features/app_layout/bloc/spaces_state.dart +++ b/lib/features/app_layout/bloc/spaces_state.dart @@ -12,6 +12,24 @@ class SpacesLoaded extends SpacesState { SpacesLoaded(this.spaces); } +class SpacesError extends SpacesState { + final String errMessage; + + 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 4fb0c26..629a38f 100644 --- a/lib/features/app_layout/model/space_model.dart +++ b/lib/features/app_layout/model/space_model.dart @@ -1,9 +1,9 @@ import 'package:syncrow_app/features/devices/model/room_model.dart'; class SpaceModel { - final String id; - final String name; - final List rooms; + final int? id; + final String? name; + late List? rooms; SpaceModel({ required this.id, @@ -21,9 +21,13 @@ class SpaceModel { factory SpaceModel.fromJson(Map json) { return SpaceModel( - id: json['id'], - name: json['name'], - rooms: json['rooms'], + id: int.parse(json['homeId']), + name: json['homeName'], + rooms: [], ); } + + static List fromJsonList(List jsonList) { + return jsonList.map((item) => SpaceModel.fromJson(item)).toList(); + } } diff --git a/lib/features/app_layout/view/app_layout.dart b/lib/features/app_layout/view/app_layout.dart index ac465f4..dbb164c 100644 --- a/lib/features/app_layout/view/app_layout.dart +++ b/lib/features/app_layout/view/app_layout.dart @@ -2,9 +2,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.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/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/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class AppLayout extends StatelessWidget { @@ -12,25 +15,58 @@ 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: Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: DefaultAppBar(context), - body: const AppBody(), - bottomNavigationBar: const DefaultNavBar(), - ), - ), - ); - }, + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => SpacesCubit(), + ), + BlocProvider( + create: (context) => DevicesCubit(), + ), + ], + 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 6ee43a4..797f2d5 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.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 e98fa87..0ba7833 100644 --- a/lib/features/app_layout/view/widgets/app_body.dart +++ b/lib/features/app_layout/view/widgets/app_body.dart @@ -1,6 +1,7 @@ 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 '../../../../generated/assets.dart'; @@ -14,24 +15,34 @@ 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: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: NavCubit.of(context).pageController, - children: NavCubit.of(context).pages, - ) - // 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 + ? NavCubit.of(context).pages[NavCubit.pageIndex] + : 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..08924a6 100644 --- a/lib/features/app_layout/view/widgets/default_app_bar.dart +++ b/lib/features/app_layout/view/widgets/default_app_bar.dart @@ -1,30 +1,35 @@ 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/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) { return BlocBuilder( builder: (context, state) { - return Padding( - padding: const EdgeInsets.only( - top: 20, - ), - child: AppBar( - backgroundColor: Colors.transparent, - leadingWidth: 150, - toolbarHeight: Constants.appBarHeight, - leading: NavCubit.appBarLeading[ - NavCubit().bottomNavItems[NavCubit.pageIndex].label], - actions: NavCubit.appBarActions[ - NavCubit().bottomNavItems[NavCubit.pageIndex].label], - ), + return BlocBuilder( + builder: (context, state) { + return Padding( + padding: const EdgeInsets.only( + top: 20, + ), + child: AppBar( + backgroundColor: Colors.transparent, + leadingWidth: 150, + toolbarHeight: Constants.appBarHeight, + leading: SpacesCubit.spaces.isNotEmpty + ? NavCubit.appBarLeading[ + NavCubit().bottomNavItems[NavCubit.pageIndex].label] + : null, + actions: NavCubit.appBarActions[ + NavCubit().bottomNavItems[NavCubit.pageIndex].label], + ), + ); + }, ); }, ); diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 95d6115..cf98d3d 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -3,17 +3,19 @@ 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'; import 'package:syncrow_app/services/api/authentication_api.dart'; import 'package:syncrow_app/services/api/network_exception.dart'; -import 'package:syncrow_app/utils/resource_manager/constants.dart'; part 'auth_state.dart'; class AuthCubit extends Cubit { - AuthCubit() : super(AuthInitial()); + AuthCubit() : super(AuthInitial()) { + getTokenAndValidate(); + } static AuthCubit get(context) => BlocProvider.of(context); @@ -21,7 +23,7 @@ class AuthCubit extends Cubit { TextEditingController passwordController = TextEditingController(); bool isPasswordVisible = false; - GlobalKey formKey = GlobalKey(); + static GlobalKey formKey = GlobalKey(); void changePasswordVisibility() { isPasswordVisible = !isPasswordVisible; @@ -35,9 +37,9 @@ class AuthCubit extends Cubit { emit(AuthAgreeToTermsChanged()); } - UserModel? user; + static UserModel? user; - Token token = Token.emptyConstructor(); + static Token token = Token.emptyConstructor(); // (FlutterSecureStorage().read(key :'token')); @@ -54,15 +56,17 @@ class AuthCubit extends Cubit { if (token.accessTokenIsNotEmpty) { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.write( - key: Constants.tokenAccessKey, value: token.accessToken); - + 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) { - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).toString())); } } @@ -70,10 +74,37 @@ class AuthCubit extends Cubit { emit(AuthLoading()); try { FlutterSecureStorage storage = const FlutterSecureStorage(); - await storage.delete(key: Constants.tokenAccessKey); + await storage.delete(key: Token.loginAccessTokenKey); + NavCubit.clear(); emit(AuthLoggedOut()); } on DioException catch (e) { - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).errMessage)); + } + } + + getTokenAndValidate() async { + emit(AuthTokenLoading()); + final value = + await const FlutterSecureStorage().read(key: Token.loginAccessTokenKey); + + if (value == null) { + emit(AuthTokenError("Token not found")); + return; + } + + final tokenData = Token.decodeToken(value); + + if (tokenData.containsKey('exp')) { + final exp = tokenData['exp'] ?? 0; + final currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; + + if (currentTime < exp) { + emit(AuthTokenSuccess()); + } else { + emit(AuthTokenError("Token expired")); + } + } else { + emit(AuthTokenError("Something went wrong")); } } } diff --git a/lib/features/auth/bloc/auth_state.dart b/lib/features/auth/bloc/auth_state.dart index 8a370b0..e84791f 100644 --- a/lib/features/auth/bloc/auth_state.dart +++ b/lib/features/auth/bloc/auth_state.dart @@ -21,3 +21,13 @@ class AuthLoggedOut extends AuthState {} class AuthPasswordVisibilityChanged extends AuthState {} class AuthAgreeToTermsChanged extends AuthState {} + +class AuthTokenLoading extends AuthState {} + +class AuthTokenSuccess extends AuthState {} + +class AuthTokenError extends AuthState { + final String message; + + AuthTokenError(this.message); +} diff --git a/lib/features/auth/model/token.dart b/lib/features/auth/model/token.dart index c87bf82..0c428a7 100644 --- a/lib/features/auth/model/token.dart +++ b/lib/features/auth/model/token.dart @@ -4,37 +4,70 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:syncrow_app/utils/helpers/decode_base64.dart'; class Token { - static const String loginAccessToken = 'access_token'; - static const String loginRefreshToken = 'refreshToken'; + static const String loginAccessTokenKey = 'access_token'; + static const String loginRefreshTokenKey = 'refreshToken'; 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; + bool get refreshTokenIsNotEmpty => refreshToken.isNotEmpty; + + bool get isNotEmpty => accessToken.isNotEmpty && refreshToken.isNotEmpty; + 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 var storage = const FlutterSecureStorage(); - storage.write(key: loginAccessToken, value: json[loginAccessToken] ?? ''); + storage.write( + key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? ''); //create token object ? - return Token(json[loginAccessToken] ?? '', json[loginRefreshToken] ?? ''); + return Token(json[loginAccessTokenKey] ?? '', + json[loginRefreshTokenKey] ?? '', '', 0, 0); } - Map toJson() => {loginRefreshToken: refreshToken}; + Map refreshTokenToJson() => + {loginRefreshTokenKey: refreshToken}; - Map decodeToken() { + Map accessTokenToJson() => {loginAccessTokenKey: accessToken}; + + 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 4d003a5..5491d73 100644 --- a/lib/features/auth/model/user_model.dart +++ b/lib/features/auth/model/user_model.dart @@ -1,7 +1,8 @@ import 'package:syncrow_app/features/auth/model/token.dart'; class UserModel { - final String? id; + static String userUuidKey = 'userUuid'; + final String? uuid; final String? email; final String? name; final String? photoUrl; @@ -12,18 +13,8 @@ class UserModel { final bool? isAgreementAccepted; - //token decoded with jwt - //{ - // "email": "Test@Test.com", - // "userId": 2, - // "uuid": "e145438c-4c62-4535-a0f4-f77958f9f9f4", - // "sessionId": "0409a7a1-6ef5-42c5-b3a1-1f15c639b301", - // "iat": 1709711675, - // "exp": 1709711975 - // } - UserModel({ - required this.id, + required this.uuid, required this.email, required this.name, required this.photoUrl, @@ -34,7 +25,7 @@ class UserModel { factory UserModel.fromJson(Map json) { return UserModel( - id: json['id'], + uuid: json['id'], email: json['email'], name: json['name'], photoUrl: json['photoUrl'], @@ -44,12 +35,14 @@ class UserModel { ); } + //uuid to json + //from token factory UserModel.fromToken(Token token) { - Map tempJson = token.decodeToken(); + Map tempJson = Token.decodeToken(token.accessToken); return UserModel( - id: tempJson['uuid'].toString(), + uuid: tempJson['uuid'].toString(), email: tempJson['email'], name: null, photoUrl: null, @@ -61,7 +54,7 @@ class UserModel { Map toJson() { return { - 'id': id, + 'id': uuid, 'email': email, 'name': name, 'photoUrl': photoUrl, 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..fd3d7ab 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,12 @@ 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, + autovalidateMode: AutovalidateMode.onUserInteraction, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -40,7 +44,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 +69,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..06713c2 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'; @@ -35,40 +33,40 @@ class LoginView extends StatelessWidget { } }, builder: (context, state) { - return Scaffold( - body: Stack( - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, + return SafeArea( + child: Scaffold( + body: Stack( + 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, + ), ), ), - ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: Constants.defaultPadding, - ), - child: SingleChildScrollView( - child: SizedBox( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, + Padding( + padding: const EdgeInsets.only( + right: Constants.defaultPadding, + left: Constants.defaultPadding, + top: Constants.defaultPadding, + ), + child: SingleChildScrollView( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -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(), @@ -106,9 +99,9 @@ class LoginView extends StatelessWidget { ), ), ), - ), - ) - ], + ) + ], + ), ), ); }, diff --git a/lib/features/devices/bloc/ac/ac_cubit.dart b/lib/features/devices/bloc/ac/ac_cubit.dart deleted file mode 100644 index 876af46..0000000 --- a/lib/features/devices/bloc/ac/ac_cubit.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; -import 'package:syncrow_app/features/devices/model/device_category_model.dart'; -import 'package:syncrow_app/features/devices/model/device_model.dart'; - -part 'ac_state.dart'; - -class AcCubit extends Cubit { - AcCubit() : super(AcInitial()); - - static AcCubit get(context) => BlocProvider.of(context); - - static DevicesCategoryModel category = DevicesCubit.allCategories[0]; - - ACModel? getSelectedAC() { - for (var ac in category.devices) { - if (ac is ACModel && ac.isSelected) { - return ac; - } - } - return null; - } - - void setTempToAll(double temperature) { - for (DeviceModel ac in category.devices) { - if (ac is ACModel) { - if (ac.temperature != temperature && - ac.bounds.min <= temperature && - temperature <= ac.bounds.max) { - setACTemp(ac, temperature); - } - } - } - universalACTemp = temperature; - emit(ACsTempChanged(temperature)); - } - - void setACTemp(ACModel model, double temp) { - if (model.bounds.min <= temp && temp <= model.bounds.max) { - model.temperature = temp; - } - emit(ACsTempChanged(temp)); - } - - double getTemp(int index) { - var device = category.devices[index]; - if (device is ACModel) { - return device.temperature; - } - return 0.0; // or any default value you prefer - } - - static double universalACTemp = 20; -} diff --git a/lib/features/devices/bloc/ac/ac_state.dart b/lib/features/devices/bloc/ac/ac_state.dart deleted file mode 100644 index 4031a13..0000000 --- a/lib/features/devices/bloc/ac/ac_state.dart +++ /dev/null @@ -1,30 +0,0 @@ -part of 'ac_cubit.dart'; - -@immutable -abstract class AcState {} - -class AcInitial extends AcState {} - -class ACsStatusChanged extends AcState {} - -class SwitchACsOff extends AcState {} - -class SwitchACsOn extends AcState {} - -class ACsTempChanged extends AcState { - final double temperature; - - ACsTempChanged(this.temperature); -} - -class ACsAverageTemp extends AcState {} - -class ACsFanSpeedChanged extends AcState {} - -class ACsTempModeChanged extends AcState {} - -class ACTurnedOn extends AcState {} - -class ACTurnedOff extends AcState {} - -class ACSelected extends AcState {} diff --git a/lib/features/devices/bloc/curtains/curtains_cubit.dart b/lib/features/devices/bloc/curtains/curtains_cubit.dart deleted file mode 100644 index 99de110..0000000 --- a/lib/features/devices/bloc/curtains/curtains_cubit.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/device_category_model.dart'; - -part 'curtains_state.dart'; - -class CurtainsCubit extends Cubit { - CurtainsCubit() : super(CurtainsInitial()); - - static CurtainsCubit get(context) => BlocProvider.of(context); - - static DevicesCategoryModel category = DevicesCubit.allCategories[3]; -} diff --git a/lib/features/devices/bloc/curtains/curtains_state.dart b/lib/features/devices/bloc/curtains/curtains_state.dart deleted file mode 100644 index 5c30d86..0000000 --- a/lib/features/devices/bloc/curtains/curtains_state.dart +++ /dev/null @@ -1,5 +0,0 @@ -part of 'curtains_cubit.dart'; - -abstract class CurtainsState {} - -class CurtainsInitial extends CurtainsState {} diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 8b706a6..cebe17e 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,270 +1,87 @@ +// ignore_for_file: constant_identifier_names, unused_import + +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; -import 'package:syncrow_app/features/devices/model/curtain_model.dart'; +import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.dart'; +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/gateway/gateway_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/lights_view.dart'; -import 'package:syncrow_app/features/devices/view/widgets/screens/screens_view.dart'; +import 'package:syncrow_app/features/devices/view/widgets/lights_switches/light_switches.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_view.dart'; -import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/services/api/network_exception.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; part 'devices_state.dart'; class DevicesCubit extends Cubit { - DevicesCubit() : super(DevicesInitial()); + DevicesCubit() : super(DevicesInitial()) { + fetchGroups(SpacesCubit.selectedSpace!.id!); + } + bool _isClosed = false; + + @override + Future close() { + _isClosed = true; + return super.close(); + } static DevicesCubit get(context) => BlocProvider.of(context); - static List allCategories = [ - DevicesCategoryModel( - devices: [ - ACModel( - name: "Living Room AC", - id: '0', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ACModel( - name: "Master Bedroom AC", - id: '1', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ACModel( - name: "Kitchen AC", - id: '2', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ACModel( - name: "Bathroom AC", - id: '3', - status: false, - temperature: 20, - fanSpeed: 0, - tempMode: 0, - coolTo: 20, - type: DeviceType.AC, - image: '', - timer: null, - bounds: Bounds( - min: 20, - max: 30, - ), - ), - ], - icon: Assets.iconsAC, - name: 'ACs', - type: DeviceType.AC, - page: const ACsView(), - ), - DevicesCategoryModel( - devices: [ - LightModel( - name: "Living Room Light", - id: '0', - status: false, - color: 0, - brightness: 20, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - LightModel( - name: "Master Bedroom Light", - id: '1', - status: false, - color: 2, - brightness: 40, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - LightModel( - name: "Kitchen Light", - id: '2', - status: false, - color: 1, - brightness: 60, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - LightModel( - name: "Bathroom Light", - id: '3', - status: false, - color: 3, - brightness: 80, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - LightModel( - name: "Balcony Light", - id: '4', - status: false, - color: 4, - brightness: 100, - lightingMode: 1, - timer: null, - type: DeviceType.Lights, - image: '', - recentColors: [ - 0xFF83D9FF, - 0xFFFC3E81, - 0xFFC0FF66, - 0xFFFDC242, - ], - ), - ], - icon: Assets.iconsLight, - name: 'Lights', - type: DeviceType.Lights, - page: const LightsView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsDoorLock, - name: 'Doors', - type: DeviceType.Door, - page: const DoorView(), - ), - DevicesCategoryModel( - devices: [ - CurtainModel( - openPercentage: 10, - id: "1", - name: "Living Room Curtain", - status: false, - type: DeviceType.Curtain, - image: '', - timer: null, - ), - CurtainModel( - openPercentage: 20, - id: "2", - name: "Master Bedroom Curtain", - status: false, - type: DeviceType.Curtain, - image: '', - timer: null, - ), - ], - icon: Assets.iconsCurtain, - name: 'Curtains', - type: DeviceType.Curtain, - page: const CurtainView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsScreen, - name: 'Screens', - type: DeviceType.Screens, - page: const ScreensView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsGateway, - name: 'Gateway', - type: DeviceType.Gateway, - page: const GateWayView(), - ), - ]; + static List? allCategories; selectCategory(int index) { - for (var i = 0; i < allCategories.length; i++) { + for (var i = 0; i < allCategories!.length; i++) { if (i == index) { - allCategories[i].isSelected = true; + allCategories![i].isSelected = true; } else { - allCategories[i].isSelected = false; + allCategories![i].isSelected = false; } } emit(DevicesCategoryChanged()); } unselectAllCategories() { - for (var category in allCategories) { + for (var category in allCategories!) { category.isSelected = false; } emit(DevicesCategoryChanged()); } Widget? get chosenCategoryView { - for (var category in allCategories) { - if (category.isSelected) { - return category.page; + if (allCategories != null) { + for (var category in allCategories!) { + if (category.isSelected) { + switch (category.type) { + case DeviceType.AC: + return const ACsView(); + case DeviceType.Lights: + return const LightsView(); + case DeviceType.DoorLock: + return const DoorView(); + case DeviceType.Curtain: + return const CurtainView(); + case DeviceType.ThreeGang: + return const LightSwitchesView(); + case DeviceType.Gateway: + return const GateWayView(); + default: + return null; + } + } } } return null; } DevicesCategoryModel? get chosenCategory { - for (var category in allCategories) { + for (var category in allCategories!) { if (category.isSelected) { return category; } @@ -273,12 +90,14 @@ class DevicesCubit extends Cubit { } selectDevice(DeviceModel device) { - for (var category in allCategories) { - for (var device in category.devices) { - if (device.isSelected) { - category.isSelected = false; - emit(DeviceSelected()); - return; + for (var category in allCategories!) { + if (category.devices != null) { + for (var device in category.devices!) { + if (device.isSelected) { + category.isSelected = false; + emit(DeviceSelected()); + return; + } } } } @@ -287,10 +106,12 @@ class DevicesCubit extends Cubit { } DeviceModel? getSelectedDevice() { - for (var category in allCategories) { - for (var device in category.devices) { - if (device.isSelected) { - return device; + for (var category in allCategories!) { + if (category.devices != null) { + for (var device in category.devices!) { + if (device.isSelected) { + return device; + } } } } @@ -300,13 +121,17 @@ class DevicesCubit extends Cubit { changeCategorySwitchValue(DevicesCategoryModel category) { if (category.devicesStatus != null) { category.devicesStatus = !category.devicesStatus!; - for (var device in category.devices) { - device.status = category.devicesStatus; + if (category.devices != null) { + for (var device in category.devices!) { + device.status = category.devicesStatus; + } } } else { category.devicesStatus = true; - for (var device in category.devices) { - device.status = true; + if (category.devices != null) { + for (var device in category.devices!) { + device.status = true; + } } } updateDevicesStatus(category); @@ -316,72 +141,203 @@ class DevicesCubit extends Cubit { turnOnOffDevice(DeviceModel device) { device.status = !device.status!; - DevicesCategoryModel category = allCategories - .firstWhere((category) => category.devices.contains(device)); + DevicesCategoryModel category = allCategories!.firstWhere((category) { + if (category.devices != null) { + return category.devices!.contains(device); + } else { + return false; + } + }); updateDevicesStatus(category); emit(DeviceSwitchChanged()); } updateDevicesStatus(DevicesCategoryModel category) { - if (category.devices.isNotEmpty) { - bool? tempStatus = category.devices[0].status; - for (var ac in category.devices) { - //check if there any ac have a different status than the initial ==> turn off the universal switch - if (ac.status != tempStatus) { - category.devicesStatus = null; + if (category.devices != null) { + if (category.devices!.isNotEmpty) { + bool? tempStatus = category.devices![0].status; + for (var ac in category.devices!) { + //check if there any ac have a different status than the initial ==> turn off the universal switch + if (ac.status != tempStatus) { + category.devicesStatus = null; + emit(DeviceSwitchChanged()); + return; + } + category.devicesStatus = tempStatus; emit(DeviceSwitchChanged()); - return; } - category.devicesStatus = tempStatus; + } else { + category.devicesStatus = null; emit(DeviceSwitchChanged()); } - } else { - category.devicesStatus = null; - emit(DeviceSwitchChanged()); } } turnAllDevicesOff(DevicesCategoryModel category) { - if (category.devices.isNotEmpty) { - for (var device in category.devices) { - device.status = false; + if (category.devices != null) { + if (category.devices!.isNotEmpty) { + for (var device in category.devices!) { + device.status = false; + } + changeCategorySwitchValue(category); + updateDevicesStatus(category); + emit(CategorySwitchChanged()); } - changeCategorySwitchValue(category); - updateDevicesStatus(category); - emit(CategorySwitchChanged()); } } turnAllDevicesOn(DevicesCategoryModel category) { - if (category.devices.isNotEmpty) { - for (var device in category.devices) { - device.status = true; + if (category.devices != null) { + if (category.devices!.isNotEmpty) { + for (var device in category.devices!) { + device.status = true; + } + changeCategorySwitchValue(category); + updateDevicesStatus(category); + emit(CategorySwitchChanged()); } - changeCategorySwitchValue(category); - updateDevicesStatus(category); - emit(CategorySwitchChanged()); } } areAllDevicesOff(DevicesCategoryModel category) { - for (var device in category.devices) { - if (device.status ?? false) { - category.devicesStatus = false; - emit(CategorySwitchChanged()); - return; + if (category.devices != null) { + for (var device in category.devices!) { + if (device.status ?? false) { + category.devicesStatus = false; + emit(CategorySwitchChanged()); + return; + } } } } clearCategoriesSelection(BuildContext context) { - for (var category in allCategories) { + for (var category in allCategories!) { category.isSelected = false; - for (var device in category.devices) { - device.isSelected = false; + if (category.devices != null) { + for (var device in category.devices!) { + device.isSelected = false; + } } } Navigator.popUntil(context, (route) => route.isFirst); emit(DevicesCategoryChanged()); } + + deviceControl(DeviceControlModel control) async { + emit(DeviceControlLoading()); + try { + await DevicesAPI.controlDevice(control); + emit(DeviceControlSuccess()); + } on DioException catch (e) { + emit(DeviceControlError(ServerFailure.fromDioError(e).errMessage)); + } + } + + fetchGroups(int spaceId) async { + if (_isClosed) return; + + try { + emit(DevicesCategoriesLoading()); + allCategories = await DevicesAPI.fetchGroups(spaceId); + emit(DevicesCategoriesSuccess()); + } on DioException catch (error) { + emit( + DevicesCategoriesError(ServerFailure.fromDioError(error).errMessage), + ); + } + } + + ///Lights + onHorizontalDragUpdate(DeviceModel light, double dx, double screenWidth) { + double newBrightness = (dx / (screenWidth - 15) * 100); + if (newBrightness > 100) { + newBrightness = 100; + } else if (newBrightness < 0) { + newBrightness = 0; + } + // setBrightness(light, newBrightness); + } + + Map lightModes = { + 0: LightMode.Doze, + 1: LightMode.Relax, + 2: LightMode.Reading, + 3: LightMode.Energizing, + }; + +// setLightingMode(DeviceModel light, LightMode mode) { +// light.lightingMode = +// lightModes.entries.firstWhere((element) => element.value == mode).key; +// emit(LightModeChanged(mode)); +// } +// +// toggleLight(DeviceModel light) { +// light.status != null ? light.status = !light.status! : light.status = true; +// emit(LightToggled(light)); +// } +// +// setColor(DeviceModel light, int color) { +// light.color = color; +// emit(LightColorChanged(color)); +// } +// +// int getBrightness(DeviceModel light) { +// return light.brightness.toInt(); +// } + +// setBrightness(DeviceModel light, double value) { +// value = (value / 5).ceil() * 5; +// if (value != light.brightness) { +// light.brightness = value; +// emit(LightBrightnessChanged(value)); +// } +// } + + ///ACs +// DeviceModel? getSelectedAC() { +// for (var ac in category.devices) { +// if (ac is DeviceModel && ac.isSelected) { +// return ac; +// } +// } +// return null; +// } +// +// void setTempToAll(double temperature) { +// for (DeviceModel ac in category.devices) { +// if (ac is DeviceModel) { +// if (ac.temperature != temperature && +// ac.bounds.min <= temperature && +// temperature <= ac.bounds.max) { +// setACTemp(ac, temperature); +// } +// } +// } +// universalACTemp = temperature; +// emit(ACsTempChanged(temperature)); +// } +// +// void setACTemp(DeviceModel model, double temp) { +// if (model.bounds.min <= temp && temp <= model.bounds.max) { +// model.temperature = temp; +// } +// emit(ACsTempChanged(temp)); +// } +// +// double getTemp(int index) { +// var device = category.devices[index]; +// if (device is DeviceModel) { +// return device.temperature; +// } +// return 0.0; // or any default value you prefer +// } +} + +enum LightMode { + Doze, + Relax, + Reading, + Energizing, } diff --git a/lib/features/devices/bloc/devices_state.dart b/lib/features/devices/bloc/devices_state.dart index 4a6ba5d..b7b3fa7 100644 --- a/lib/features/devices/bloc/devices_state.dart +++ b/lib/features/devices/bloc/devices_state.dart @@ -13,6 +13,7 @@ class DevicesFailure extends DevicesState {} class ChangeIndex extends DevicesState {} +// Devices class DevicesCategoryChanged extends DevicesState {} class CategorySwitchChanged extends DevicesState {} @@ -20,3 +21,25 @@ class CategorySwitchChanged extends DevicesState {} class DeviceSwitchChanged extends DevicesState {} class DeviceSelected extends DevicesState {} + +// Device Control +class DeviceControlLoading extends DevicesState {} + +class DeviceControlSuccess extends DevicesState {} + +class DeviceControlError extends DevicesState { + final String errorMsg; + + DeviceControlError(this.errorMsg); +} + +// Categories +class DevicesCategoriesLoading extends DevicesState {} + +class DevicesCategoriesSuccess extends DevicesState {} + +class DevicesCategoriesError extends DevicesState { + final String errorMsg; + + DevicesCategoriesError(this.errorMsg); +} diff --git a/lib/features/devices/bloc/lights/lights_cubit.dart b/lib/features/devices/bloc/lights/lights_cubit.dart deleted file mode 100644 index 3df5821..0000000 --- a/lib/features/devices/bloc/lights/lights_cubit.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; - -part 'lights_state.dart'; - -class LightsCubit extends Cubit { - LightsCubit() : super(LightsInitial()); - - static LightsCubit get(context) => BlocProvider.of(context); - - Map lightModes = { - 0: LightMode.doze, - 1: LightMode.relax, - 2: LightMode.reading, - 3: LightMode.energizing, - }; - - setLightingMode(LightModel light, LightMode mode) { - light.lightingMode = - lightModes.entries.firstWhere((element) => element.value == mode).key; - emit(LightModeChanged(mode)); - } - - toggleLight(LightModel light) { - light.status != null ? light.status = !light.status! : light.status = true; - emit(LightToggled(light)); - } - - setColor(LightModel light, int color) { - light.color = color; - emit(LightColorChanged(color)); - } - - int getBrightness(LightModel light) { - return light.brightness.toInt(); - } - - setBrightness(LightModel light, double value) { - value = (value / 5).ceil() * 5; - if (value != light.brightness) { - light.brightness = value; - emit(LightBrightnessChanged(value)); - } - } - - onHorizontalDragUpdate(LightModel light, double dx, double screenWidth) { - double newBrightness = (dx / (screenWidth - 15) * 100); - if (newBrightness > 100) { - newBrightness = 100; - } else if (newBrightness < 0) { - newBrightness = 0; - } - setBrightness(light, newBrightness); - } -} - -enum LightMode { - doze, - relax, - reading, - energizing, -} diff --git a/lib/features/devices/bloc/lights/lights_state.dart b/lib/features/devices/bloc/lights/lights_state.dart deleted file mode 100644 index 0623b0c..0000000 --- a/lib/features/devices/bloc/lights/lights_state.dart +++ /dev/null @@ -1,39 +0,0 @@ -part of 'lights_cubit.dart'; - -abstract class LightsState {} - -class LightsInitial extends LightsState {} - -class LightsLoading extends LightsState {} - -class LightsSuccess extends LightsState {} - -class LightsFailure extends LightsState { - final String message; - - LightsFailure(this.message); -} - -class LightBrightnessChanged extends LightsState { - final double brightness; - - LightBrightnessChanged(this.brightness); -} - -class LightToggled extends LightsState { - final LightModel light; - - LightToggled(this.light); -} - -class LightColorChanged extends LightsState { - final int color; - - LightColorChanged(this.color); -} - -class LightModeChanged extends LightsState { - final LightMode mode; - - LightModeChanged(this.mode); -} diff --git a/lib/features/devices/model/ac_model.dart b/lib/features/devices/model/ac_model.dart deleted file mode 100644 index 737118d..0000000 --- a/lib/features/devices/model/ac_model.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; - -class ACModel extends DeviceModel { - late double temperature; - - late int fanSpeed; - - late int tempMode; - - late double coolTo; - - late Bounds bounds; - - ACModel({ - required this.temperature, - required this.fanSpeed, - required this.tempMode, - required this.coolTo, - required this.bounds, - required super.id, - required super.name, - required super.type, - required super.status, - required super.image, - required super.timer, - }); - - Map toJson() { - return { - 'temperature': temperature, - 'fanSpeed': fanSpeed, - 'tempMode': tempMode, - 'coolTo': coolTo, - 'id': id, - 'name': name, - 'status': status, - 'type': type, - 'image': image, - }; - } - - factory ACModel.fromJson(Map json) { - return ACModel( - id: json['id'], - name: json['name'], - status: json['status'], - temperature: json['temperature'], - fanSpeed: json['fanSpeed'], - tempMode: json['tempMode'], - type: json['type'], - image: json['image'], - timer: json['timer'], - coolTo: json['coolTo'], - bounds: Bounds.fromJson(json['bounds']), - ); - } -} - -class Bounds { - late double min; - late double max; - - Bounds({ - required this.min, - required this.max, - }); - - Map toJson() { - return { - 'min': min, - 'max': max, - }; - } - - factory Bounds.fromJson(Map json) { - return Bounds( - min: json['min'], - max: json['max'], - ); - } -} diff --git a/lib/features/devices/model/curtain_model.dart b/lib/features/devices/model/curtain_model.dart deleted file mode 100644 index 6abc311..0000000 --- a/lib/features/devices/model/curtain_model.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; - -class CurtainModel extends DeviceModel { - late int openPercentage; - - CurtainModel({ - required this.openPercentage, - required super.id, - required super.name, - required super.type, - required super.status, - required super.image, - required super.timer, - }); - - Map toJson() { - return { - 'openPercentage': openPercentage, - 'timer': timer, - 'id': id, - 'name': name, - 'status': status, - 'type': type, - 'image': image, - }; - } - - factory CurtainModel.fromJson(Map json) { - return CurtainModel( - id: json['id'], - name: json['name'], - status: json['status'], - openPercentage: json['openPercentage'], - timer: json['timer'], - type: json['type'], - image: json['image'], - ); - } -} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index c9f3303..1da69d8 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -1,38 +1,74 @@ // ignore_for_file: constant_identifier_names -import 'package:flutter/cupertino.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; class DevicesCategoryModel { - final String name; - final String icon; - - final Widget page; + int? id; + final String? name; + final String? icon; bool? devicesStatus; - final List devices; + final List? devices; - final DeviceType type; + final DeviceType? type; bool isSelected; DevicesCategoryModel( {this.isSelected = false, - required this.page, required this.type, required this.name, required this.icon, required this.devices}) { //sets the initial status of the devices - if (devices.isNotEmpty) { - bool tempStatus = devices.first.status ?? false; - for (var device in devices) { - if (device.status != tempStatus) { - devicesStatus = false; - break; + if (devices != null) { + if (devices!.isNotEmpty) { + bool tempStatus = devices!.first.status ?? false; + for (var device in devices!) { + if (device.status != tempStatus) { + devicesStatus = false; + break; + } } + devicesStatus = tempStatus; } - devicesStatus = tempStatus; + } else { + devicesStatus = false; } } + + DevicesCategoryModel.fromJson(Map json) + : name = json['groupName'], + id = json['groupId'], + type = deviceTypeMap[json['groupName']] ?? DeviceType.Other, + icon = deviceTypeIconMap[ + deviceTypeMap[json['groupName']] ?? DeviceType.Other] ?? + '', + devices = [], + isSelected = false; + + static List fromJsonList(List jsonList) { + return jsonList.map((item) => DevicesCategoryModel.fromJson(item)).toList(); + } } + +Map deviceTypeMap = { + 'Ceiling Presence Sensors': DeviceType.Sensors, + 'Wall presence sensor': DeviceType.Sensors, + 'Door Locks': DeviceType.DoorLock, + 'Gateways': DeviceType.Gateway, + 'ACs': DeviceType.AC, + '3Gang': DeviceType.Gang, +}; + +Map deviceTypeIconMap = { + DeviceType.AC: Assets.iconsAC, + DeviceType.Lights: Assets.iconsLight, + DeviceType.DoorLock: Assets.iconsDoorLock, + DeviceType.Curtain: Assets.iconsCurtain, + DeviceType.Gateway: Assets.iconsGateway, + DeviceType.Sensors: Assets.iconsSensors, + DeviceType.Gang: Assets.iconsGang, + DeviceType.Other: Assets.iconsAC, +}; diff --git a/lib/features/devices/model/device_control_model.dart b/lib/features/devices/model/device_control_model.dart new file mode 100644 index 0000000..8f9edf7 --- /dev/null +++ b/lib/features/devices/model/device_control_model.dart @@ -0,0 +1,27 @@ +class DeviceControlModel { + String? deviceId; + String? code; + bool? value; + + DeviceControlModel({ + required this.deviceId, + required this.code, + required this.value, + }); + + factory DeviceControlModel.fromJson(Map json) { + return DeviceControlModel( + deviceId: json['deviceId'], + code: json['code'], + value: json['value'], + ); + } + + Map toJson() { + return { + 'deviceId': deviceId, + 'code': code, + 'value': value, + }; + } +} diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 2c327f9..e14493e 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -1,17 +1,19 @@ +import 'package:syncrow_app/features/devices/model/function_model.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; -abstract class DeviceModel { - final String? id; +class DeviceModel { + final int? id; final String? name; final DeviceType? type; bool? status; final String? image; final double? timer; - late final String icon; bool isSelected = false; + late List functions; + DeviceModel({ required this.id, required this.name, @@ -19,6 +21,7 @@ abstract class DeviceModel { required this.status, required this.image, required this.timer, + required this.functions, }) { switch (type) { case DeviceType.AC: @@ -27,7 +30,7 @@ abstract class DeviceModel { case DeviceType.Lights: icon = Assets.iconsLight; break; - case DeviceType.Door: + case DeviceType.DoorLock: icon = Assets.iconsDoorLock; break; case DeviceType.Curtain: diff --git a/lib/features/devices/model/function_model.dart b/lib/features/devices/model/function_model.dart new file mode 100644 index 0000000..c187b32 --- /dev/null +++ b/lib/features/devices/model/function_model.dart @@ -0,0 +1,42 @@ +//{ +// "code": "switch_1", +// "desc": "switch 1", +// "name": "switch 1", +// "type": "Boolean", +// "values": "{}" +// } +class FunctionModel { + String? code; + String? desc; + String? name; + String? type; + String? values; + + FunctionModel({ + required this.code, + required this.desc, + required this.name, + required this.type, + required this.values, + }); + + factory FunctionModel.fromJson(Map json) { + return FunctionModel( + code: json['code'], + desc: json['desc'], + name: json['name'], + type: json['type'], + values: json['values'], + ); + } + + Map toJson() { + return { + 'code': code, + 'desc': desc, + 'name': name, + 'type': type, + 'values': values, + }; + } +} diff --git a/lib/features/devices/model/light_model.dart b/lib/features/devices/model/light_model.dart deleted file mode 100644 index ab2f258..0000000 --- a/lib/features/devices/model/light_model.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; - -class LightModel extends DeviceModel { - late double brightness; - late int color; - - late int lightingMode; - - late List recentColors; - - LightModel({ - required this.brightness, - required this.color, - required this.lightingMode, - required this.recentColors, - required super.id, - required super.name, - required super.type, - required super.status, - required super.image, - required super.timer, - }); - - Map toJson() { - return { - 'luminance': brightness, - 'color': color, - 'lightingMode': lightingMode, - 'timer': timer, - 'recentColors': recentColors, - 'id': id, - 'name': name, - 'status': status, - 'type': type, - 'image': image, - }; - } - - factory LightModel.fromJson(Map json) { - return LightModel( - id: json['id'], - name: json['name'], - status: json['status'], - brightness: json['luminance'], - color: json['color'], - lightingMode: json['lightingMode'], - timer: json['timer'], - type: json['type'], - image: json['image'], - recentColors: json['recentColors'], - ); - } -} 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/devices_view.dart b/lib/features/devices/view/devices_view.dart index 69358a1..18dc389 100644 --- a/lib/features/devices/view/devices_view.dart +++ b/lib/features/devices/view/devices_view.dart @@ -3,8 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; -class CategoriesView extends StatelessWidget { - const CategoriesView({super.key}); +class DevicesView extends StatelessWidget { + const DevicesView({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/features/devices/view/widgets/ACs/ac_interface.dart b/lib/features/devices/view/widgets/ACs/ac_interface.dart index aceaf69..e0af460 100644 --- a/lib/features/devices/view/widgets/ACs/ac_interface.dart +++ b/lib/features/devices/view/widgets/ACs/ac_interface.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_interface_controls.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_interface_temp_unit.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; @@ -10,9 +10,9 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; class AcInterface extends StatelessWidget { - const AcInterface({super.key, required this.acModel}); + const AcInterface({super.key, required this.deviceModel}); - final ACModel acModel; + final DeviceModel deviceModel; @override Widget build(BuildContext context) { @@ -30,7 +30,7 @@ class AcInterface extends StatelessWidget { backgroundColor: Colors.transparent, centerTitle: true, title: BodyLarge( - text: acModel.name ?? "", + text: deviceModel.name ?? "", fontColor: ColorsManager.primaryColor, fontWeight: FontsManager.bold, ), @@ -62,7 +62,7 @@ class AcInterface extends StatelessWidget { maxHeight: 380, ), child: AcInterfaceTempUnit( - acModel: acModel, + deviceModel: deviceModel, ), ), const SizedBox( @@ -73,7 +73,7 @@ class AcInterface extends StatelessWidget { maxHeight: 120, ), child: AcInterfaceControls( - acModel: acModel, + deviceModel: deviceModel, ), ), ], diff --git a/lib/features/devices/view/widgets/ACs/ac_interface_controls.dart b/lib/features/devices/view/widgets/ACs/ac_interface_controls.dart index 0d7e11c..16968a5 100644 --- a/lib/features/devices/view/widgets/ACs/ac_interface_controls.dart +++ b/lib/features/devices/view/widgets/ACs/ac_interface_controls.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_mode_control_unit.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; @@ -9,16 +9,16 @@ import '../../../../../generated/assets.dart'; class AcInterfaceControls extends StatelessWidget { const AcInterfaceControls({ super.key, - required this.acModel, + required this.deviceModel, }); - final ACModel acModel; + final DeviceModel deviceModel; @override Widget build(BuildContext context) { return Column( children: [ - ACModeControlUnit(model: acModel), + ACModeControlUnit(model: deviceModel), const SizedBox(height: 10), Row( children: [ diff --git a/lib/features/devices/view/widgets/ACs/ac_interface_temp_unit.dart b/lib/features/devices/view/widgets/ACs/ac_interface_temp_unit.dart index 9882d00..7dfabbd 100644 --- a/lib/features/devices/view/widgets/ACs/ac_interface_temp_unit.dart +++ b/lib/features/devices/view/widgets/ACs/ac_interface_temp_unit.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:sleek_circular_slider/sleek_circular_slider.dart'; -import 'package:syncrow_app/features/devices/bloc/AC/ac_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; @@ -11,21 +12,19 @@ import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; -import '../../../model/ac_model.dart'; - class AcInterfaceTempUnit extends StatelessWidget { const AcInterfaceTempUnit({ super.key, - required this.acModel, + required this.deviceModel, }); - final ACModel acModel; + final DeviceModel deviceModel; @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => AcCubit(), - child: BlocBuilder( + create: (context) => DevicesCubit(), + child: BlocBuilder( builder: (context, state) { return DefaultContainer( child: Column( @@ -55,7 +54,8 @@ class AcInterfaceTempUnit extends StatelessWidget { fontWeight: FontsManager.regular, ), modifier: (double value) { - return '${acModel.temperature.toStringAsFixed(1)}°C'; + // return '${DeviceModel.temperature.toStringAsFixed(1)}°C'; + return '${value.toStringAsFixed(1)}°C'; }, mainLabelStyle: context.titleLarge.copyWith( height: 0, @@ -72,15 +72,16 @@ class AcInterfaceTempUnit extends StatelessWidget { ), ), ), - min: acModel.bounds.min, - max: acModel.bounds.max, - initialValue: acModel.temperature, + // min: DeviceModel.bounds.min, + // max: DeviceModel.bounds.max, + // initialValue: DeviceModel.temperature, onChange: (value) { String valueAsString = value.toStringAsFixed(1); if (valueAsString.endsWith(".0") || valueAsString.endsWith(".5")) { value = double.parse(valueAsString); - AcCubit.get(context).setACTemp(acModel, value); + // DevicesCubit.get(context) + // .setACTemp(DeviceModel, value); } }, ), @@ -95,9 +96,9 @@ class AcInterfaceTempUnit extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setACTemp(acModel, acModel.coolTo); - acModel.coolTo -= .5; + // DevicesCubit.get(context) + // .setACTemp(DeviceModel, DeviceModel.coolTo); + // DeviceModel.coolTo -= .5; }, child: SvgPicture.asset( Assets.iconsMinus, @@ -107,7 +108,8 @@ class AcInterfaceTempUnit extends StatelessWidget { Column( children: [ BodyLarge( - text: "${acModel.coolTo}° C", + // text: "${DeviceModel.coolTo}° C", + text: '24° C', style: context.bodyLarge.copyWith( color: ColorsManager.primaryColor.withOpacity(0.6), @@ -125,9 +127,9 @@ class AcInterfaceTempUnit extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setACTemp(acModel, acModel.coolTo); - acModel.coolTo += .5; + // DevicesCubit.get(context) + // .setACTemp(DeviceModel, DeviceModel.coolTo); + // DeviceModel.coolTo += .5; }, child: SvgPicture.asset( Assets.iconsPlus, diff --git a/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart b/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart index aa19f2f..e1f9416 100644 --- a/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart +++ b/lib/features/devices/view/widgets/ACs/ac_mode_control_unit.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import '../../../../../generated/assets.dart'; @@ -11,7 +10,7 @@ class ACModeControlUnit extends StatefulWidget { required this.model, }); - final ACModel model; + final DeviceModel model; @override State createState() => _ACModeControlUnitState(); @@ -40,15 +39,15 @@ class _ACModeControlUnitState extends State { child: InkWell( onTap: () { setState(() { - widget.model.fanSpeed = - widget.model.fanSpeed == 3 ? 0 : widget.model.fanSpeed + 1; + // widget.model.fanSpeed = + // widget.model.fanSpeed == 3 ? 0 : widget.model.fanSpeed + 1; }); }, - child: DefaultContainer( + child: const DefaultContainer( height: 55, child: Center( - child: SvgPicture.asset(fanSpeeds[widget.model.fanSpeed]), - ), + // child: SvgPicture.asset(fanSpeeds[widget.model.fanSpeed]), + ), ), ), ), @@ -57,15 +56,16 @@ class _ACModeControlUnitState extends State { child: InkWell( onTap: () { setState(() { - widget.model.tempMode = - widget.model.tempMode == 2 ? 0 : widget.model.tempMode + 1; + // widget.model.tempMode = + // widget.model.tempMode == 2 ? 0 : widget.model.tempMode + 1; }); }, - child: DefaultContainer( + child: const DefaultContainer( height: 55, child: Center( - child: SvgPicture.asset(tempModes[widget.model.tempMode]), - ), + // child: SvgPicture.asset(tempModes[widget.model.tempMode]), + + ), ), ), ), diff --git a/lib/features/devices/view/widgets/ACs/ac_temp_widget.dart b/lib/features/devices/view/widgets/ACs/ac_temp_widget.dart index c3c4d4c..136c705 100644 --- a/lib/features/devices/view/widgets/ACs/ac_temp_widget.dart +++ b/lib/features/devices/view/widgets/ACs/ac_temp_widget.dart @@ -1,25 +1,26 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/bloc/AC/ac_cubit.dart';import 'package:syncrow_app/features/shared_widgets/default_container.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import '../../../../../generated/assets.dart'; -import '../../../model/ac_model.dart'; class ACTempWidget extends StatelessWidget { const ACTempWidget( - this.acModel, { + this.deviceModel, { super.key, }); - final ACModel acModel; + final DeviceModel deviceModel; @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return DefaultContainer( height: 60, @@ -30,8 +31,8 @@ class ACTempWidget extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setACTemp(acModel, acModel.temperature - 0.5); + // DevicesCubit.get(context) + // .setACTemp(DeviceModel, DeviceModel.temperature - 0.5); }, child: SvgPicture.asset( Assets.iconsMinus, @@ -39,7 +40,8 @@ class ACTempWidget extends StatelessWidget { ), ), BodyLarge( - text: "${acModel.temperature}° C", + // text: "${DeviceModel.temperature}° C", + text: '25 °C', style: context.bodyLarge.copyWith( color: ColorsManager.primaryColor.withOpacity(0.6), fontSize: 23, @@ -49,8 +51,8 @@ class ACTempWidget extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setACTemp(acModel, acModel.temperature + 0.5); + // DevicesCubit.get(context) + // .setACTemp(DeviceModel, DeviceModel.temperature + 0.5); }, child: SvgPicture.asset( Assets.iconsPlus, diff --git a/lib/features/devices/view/widgets/ACs/acs_list.dart b/lib/features/devices/view/widgets/ACs/acs_list.dart index 4b3c638..362118c 100644 --- a/lib/features/devices/view/widgets/ACs/acs_list.dart +++ b/lib/features/devices/view/widgets/ACs/acs_list.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/AC/ac_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_mode_control_unit.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_temp_widget.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/universal_ac_temp.dart'; @@ -9,8 +9,6 @@ import 'package:syncrow_app/features/devices/view/widgets/universal_switch.dart' import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; -import '../../../model/ac_model.dart'; - class ACsList extends StatelessWidget { const ACsList({ super.key, @@ -29,7 +27,7 @@ class ACsList extends StatelessWidget { const BodySmall(text: "All ACs"), const SizedBox(height: 5), UniversalSwitch( - category: AcCubit.category, + category: DevicesCubit.get(context).chosenCategory!, ), const SizedBox(height: 10), const UniversalACTemp(), @@ -40,9 +38,12 @@ class ACsList extends StatelessWidget { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.all(0), - itemCount: AcCubit.category.devices.length, + itemCount: + DevicesCubit.get(context).chosenCategory!.devices!.length, itemBuilder: (context, index) { - ACModel ac = AcCubit.category.devices[index] as ACModel; + DeviceModel ac = DevicesCubit.get(context) + .chosenCategory! + .devices![index] as DeviceModel; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -51,7 +52,11 @@ class ACsList extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, children: [ BodySmall( - text: AcCubit.category.devices[index].name ?? ""), + text: DevicesCubit.get(context) + .chosenCategory! + .devices![index] + .name ?? + ""), IconButton( onPressed: () { DevicesCubit.get(context).selectDevice(ac); @@ -70,19 +75,17 @@ class ACsList extends StatelessWidget { ], ), const SizedBox(height: 5), - if (AcCubit.category.devices[index] is ACModel) - DevicesDefaultSwitch( - model: ac, - ), + DevicesDefaultSwitch( + model: ac, + ), const SizedBox(height: 10), ACTempWidget( ac, ), const SizedBox(height: 10), - if (AcCubit.category.devices[index] is ACModel) - ACModeControlUnit( - model: ac, - ), + ACModeControlUnit( + model: ac, + ), const SizedBox(height: 10), ], ); diff --git a/lib/features/devices/view/widgets/ACs/acs_view.dart b/lib/features/devices/view/widgets/ACs/acs_view.dart index 2a73a10..fdf0fb2 100644 --- a/lib/features/devices/view/widgets/ACs/acs_view.dart +++ b/lib/features/devices/view/widgets/ACs/acs_view.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/AC/ac_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/acs_list.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/category_view_app_bar.dart'; @@ -18,15 +17,16 @@ class ACsView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => AcCubit(), + create: (context) => DevicesCubit(), child: BlocBuilder( builder: (context, state) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - ACModel? selectedAC; - if (DevicesCubit.get(context).getSelectedDevice() is ACModel) { - selectedAC = - DevicesCubit.get(context).getSelectedDevice() as ACModel; + DeviceModel? selectedAC; + if (DevicesCubit.get(context).getSelectedDevice() + is DeviceModel) { + selectedAC = DevicesCubit.get(context).getSelectedDevice() + as DeviceModel; } return AnnotatedRegion( value: SystemUiOverlayStyle( @@ -59,7 +59,7 @@ class ACsView extends StatelessWidget { ), child: SizedBox.expand( child: selectedAC != null - ? AcInterface(acModel: selectedAC) + ? AcInterface(deviceModel: selectedAC) : const ACsList(), ), ), diff --git a/lib/features/devices/view/widgets/ACs/category_view_app_bar.dart b/lib/features/devices/view/widgets/ACs/category_view_app_bar.dart index c1f845f..4216a2f 100644 --- a/lib/features/devices/view/widgets/ACs/category_view_app_bar.dart +++ b/lib/features/devices/view/widgets/ACs/category_view_app_bar.dart @@ -20,7 +20,7 @@ class CategoryViewAppBar extends StatelessWidget toolbarHeight: Constants.appBarHeight, centerTitle: true, title: DisplayMedium( - text: DevicesCubit.get(context).chosenCategory!.name, + text: DevicesCubit.get(context).chosenCategory!.name!, style: context.displayMedium.copyWith( color: ColorsManager.primaryColor, fontWeight: FontWeight.bold, diff --git a/lib/features/devices/view/widgets/ACs/universal_ac_temp.dart b/lib/features/devices/view/widgets/ACs/universal_ac_temp.dart index aa5d029..379dee1 100644 --- a/lib/features/devices/view/widgets/ACs/universal_ac_temp.dart +++ b/lib/features/devices/view/widgets/ACs/universal_ac_temp.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_app/features/devices/bloc/AC/ac_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import '../../../../../generated/assets.dart'; @@ -16,7 +16,7 @@ class UniversalACTemp extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return DefaultContainer( height: 60, @@ -27,8 +27,8 @@ class UniversalACTemp extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setTempToAll(AcCubit.universalACTemp - .5); + // DevicesCubit.get(context) + // .setTempToAll(DevicesCubit.universalACTemp - .5); }, child: SvgPicture.asset( Assets.iconsMinus, @@ -36,7 +36,8 @@ class UniversalACTemp extends StatelessWidget { ), ), BodyLarge( - text: "${AcCubit.universalACTemp}° C", + // text: "${DevicesCubit.universalACTemp}° C", + text: '25 °C', style: context.bodyLarge.copyWith( color: ColorsManager.primaryColor.withOpacity(0.6), fontSize: 23, @@ -46,8 +47,8 @@ class UniversalACTemp extends StatelessWidget { dimension: 24, child: InkWell( onTap: () { - AcCubit.get(context) - .setTempToAll(AcCubit.universalACTemp + .5); + // DevicesCubit.get(context) + // .setTempToAll(DevicesCubit.universalACTemp + .5); }, child: SvgPicture.asset( Assets.iconsPlus, diff --git a/lib/features/devices/view/widgets/curtains/curtain_list.dart b/lib/features/devices/view/widgets/curtains/curtain_list.dart index 5abb05e..eaf7f07 100644 --- a/lib/features/devices/view/widgets/curtains/curtain_list.dart +++ b/lib/features/devices/view/widgets/curtains/curtain_list.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/curtains/curtains_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; -import 'package:syncrow_app/features/devices/model/curtain_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/universal_switch.dart'; import 'package:syncrow_app/features/shared_widgets/devices_default_switch.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; @@ -26,7 +25,7 @@ class CurtainList extends StatelessWidget { const BodySmall(text: "All Curtains"), const SizedBox(height: 5), UniversalSwitch( - category: CurtainsCubit.category, + category: DevicesCubit.get(context).chosenCategory!, ), // other ACs controls @@ -34,22 +33,25 @@ class CurtainList extends StatelessWidget { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.all(0), - itemCount: CurtainsCubit.category.devices.length, + itemCount: + DevicesCubit.get(context).chosenCategory!.devices!.length, itemBuilder: (context, index) { - CurtainModel curtain = - CurtainsCubit.category.devices[index] as CurtainModel; + DeviceModel curtain = + DevicesCubit.get(context).chosenCategory!.devices![index]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 20), BodySmall( - text: - CurtainsCubit.category.devices[index].name ?? ""), + text: DevicesCubit.get(context) + .chosenCategory! + .devices![index] + .name ?? + ""), const SizedBox(height: 5), - if (CurtainsCubit.category.devices[index] is CurtainModel) - DevicesDefaultSwitch( - model: curtain, - ), + DevicesDefaultSwitch( + model: curtain, + ), ], ); }, diff --git a/lib/features/devices/view/widgets/curtains/curtain_view.dart b/lib/features/devices/view/widgets/curtains/curtain_view.dart index 27cdb76..baef4d3 100644 --- a/lib/features/devices/view/widgets/curtains/curtain_view.dart +++ b/lib/features/devices/view/widgets/curtains/curtain_view.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/curtains/curtains_cubit.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/category_view_app_bar.dart'; import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_list.dart'; @@ -15,47 +14,43 @@ class CurtainView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => CurtainsCubit(), + create: (context) => DevicesCubit(), child: BlocBuilder( builder: (context, state) { - return BlocBuilder( - 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: const CategoryViewAppBar(), - body: 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, - ), - ), - child: Padding( - padding: EdgeInsets.only( - top: Constants.appBarHeight, - left: Constants.defaultPadding, - right: Constants.defaultPadding, - ), - child: const CurtainList(), + 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: const CategoryViewAppBar(), + body: 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, ), ), + child: Padding( + padding: EdgeInsets.only( + top: Constants.appBarHeight, + left: Constants.defaultPadding, + right: Constants.defaultPadding, + ), + child: const CurtainList(), + ), ), - ); - }, + ), + ), ); }, ), diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 35858b7..c558633 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -19,12 +19,15 @@ 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) { + //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,13 +49,15 @@ class DevicesViewBody extends StatelessWidget { }, children: [ const WizardPage(), - ...SpacesCubit.get(context).selectedSpace.rooms.map( - (room) { - return RoomPage( - room: room, - ); - }, - ) + if (SpacesCubit.selectedSpace != null) + if (SpacesCubit.selectedSpace!.rooms != null) + ...SpacesCubit.selectedSpace!.rooms!.map( + (room) { + return RoomPage( + room: room, + ); + }, + ) ], ), ), @@ -63,7 +68,7 @@ class DevicesViewBody extends StatelessWidget { child: SmoothPageIndicator( controller: SpacesCubit.get(context).devicesPageController, - count: 3, + count: SpacesCubit.selectedSpace!.rooms!.length + 1, effect: const WormEffect( paintStyle: PaintingStyle.stroke, dotHeight: 8, @@ -74,6 +79,9 @@ class DevicesViewBody extends StatelessWidget { ], ), ); + } + }, + ); }, ), ); diff --git a/lib/features/devices/view/widgets/lights/light_brightness.dart b/lib/features/devices/view/widgets/lights/light_brightness.dart index ae64455..e6b1864 100644 --- a/lib/features/devices/view/widgets/lights/light_brightness.dart +++ b/lib/features/devices/view/widgets/lights/light_brightness.dart @@ -1,29 +1,28 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; -import '../../../model/light_model.dart'; - class LightBrightness extends StatelessWidget { const LightBrightness({ super.key, required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return GestureDetector( - onHorizontalDragUpdate: (details) => LightsCubit.get(context) - .onHorizontalDragUpdate(light, details.localPosition.dx, - MediaQuery.of(context).size.width - 15), + // onHorizontalDragUpdate: (details) => DevicesCubit().get(context) + // .onHorizontalDragUpdate(light, details.localPosition.dx, + // MediaQuery.of(context).size.width - 15), child: Stack( alignment: Alignment.center, children: [ @@ -36,22 +35,24 @@ class LightBrightness extends StatelessWidget { duration: const Duration(milliseconds: 0), child: Container( height: 60, - width: (MediaQuery.of(context).size.width - 30) * - light.brightness / - 100, + // width: (MediaQuery.of(context).size.width - 30) * + // light.brightness / + // 100, decoration: BoxDecoration( - color: ColorsManager.primaryColor.withOpacity(0.6), - borderRadius: light.brightness != 100 - ? const BorderRadius.only( - topLeft: Radius.circular(20), - bottomLeft: Radius.circular(20), - ) - : BorderRadius.circular(20), - ), + color: ColorsManager.primaryColor.withOpacity(0.6), + borderRadius: + // light.brightness != 100 ? + const BorderRadius.only( + topLeft: Radius.circular(20), + bottomLeft: Radius.circular(20), + ) + // : BorderRadius.circular(20), + ), ), ), BodyLarge( - text: "${light.brightness}%", + // text: "${light.brightness}%", + text: '100%', style: context.bodyLarge.copyWith( color: Colors.black, fontSize: 23, diff --git a/lib/features/devices/view/widgets/lights/light_interface.dart b/lib/features/devices/view/widgets/lights/light_interface.dart index 65e7101..e80ea44 100644 --- a/lib/features/devices/view/widgets/lights/light_interface.dart +++ b/lib/features/devices/view/widgets/lights/light_interface.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_contols.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_switch.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_timer.dart'; @@ -9,11 +9,11 @@ import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface class LightInterface extends StatelessWidget { const LightInterface({super.key, required this.light}); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return Padding( padding: const EdgeInsets.only(top: 70, right: 20, left: 20), diff --git a/lib/features/devices/view/widgets/lights/light_interface_contols.dart b/lib/features/devices/view/widgets/lights/light_interface_contols.dart index 5495da1..e41b1be 100644 --- a/lib/features/devices/view/widgets/lights/light_interface_contols.dart +++ b/lib/features/devices/view/widgets/lights/light_interface_contols.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_modes.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_recent_color.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface_slider.dart'; @@ -11,7 +11,7 @@ class LightInterfaceContols extends StatelessWidget { required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { diff --git a/lib/features/devices/view/widgets/lights/light_interface_modes.dart b/lib/features/devices/view/widgets/lights/light_interface_modes.dart index 93df1b9..eff94c2 100644 --- a/lib/features/devices/view/widgets/lights/light_interface_modes.dart +++ b/lib/features/devices/view/widgets/lights/light_interface_modes.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -12,7 +12,7 @@ class LightInterfaceModes extends StatelessWidget { required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { @@ -27,10 +27,10 @@ class LightInterfaceModes extends StatelessWidget { Wrap( spacing: 25, children: List.generate( - LightsCubit.get(context).lightModes.length, + DevicesCubit.get(context).lightModes.length, (index) => InkWell( - onTap: () => LightsCubit.get(context).setLightingMode( - light, LightsCubit.get(context).lightModes[index]!), + // onTap: () => DevicesCubit.get(context).setLightingMode( + // light, DevicesCubit.get(context).lightModes[index]!), child: Column( children: [ Container( diff --git a/lib/features/devices/view/widgets/lights/light_interface_recent_color.dart b/lib/features/devices/view/widgets/lights/light_interface_recent_color.dart index a27eeda..ccc58e7 100644 --- a/lib/features/devices/view/widgets/lights/light_interface_recent_color.dart +++ b/lib/features/devices/view/widgets/lights/light_interface_recent_color.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -15,7 +14,7 @@ class LightInterfaceRecentColor extends StatelessWidget { required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { @@ -31,19 +30,20 @@ class LightInterfaceRecentColor extends StatelessWidget { child: Row( children: [ ...List.generate( - light.recentColors.length > 4 ? 4 : light.recentColors.length, + // light.recentColors.length > 4 ? 4 : light.recentColors.length, + 4, (index) => InkWell( - onTap: () { - LightsCubit.get(context) - .setColor(light, light.recentColors[index]); - }, + // onTap: () { + // DevicesCubit.get(context) + // .setColor(light, light.recentColors[index]); + // }, child: Container( margin: const EdgeInsets.only(right: 10), width: 40, height: 40, decoration: ShapeDecoration( shape: const CircleBorder(), - color: Color(light.recentColors[index]), + // color: Color(light.recentColors[index]), ), ), ), diff --git a/lib/features/devices/view/widgets/lights/light_interface_slider.dart b/lib/features/devices/view/widgets/lights/light_interface_slider.dart index afb75b6..8c7e305 100644 --- a/lib/features/devices/view/widgets/lights/light_interface_slider.dart +++ b/lib/features/devices/view/widgets/lights/light_interface_slider.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/features/shared_widgets/united_text.dart'; import 'package:syncrow_app/utils/context_extension.dart'; @@ -13,7 +12,7 @@ class LightInterfaceSlider extends StatelessWidget { required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { @@ -29,7 +28,8 @@ class LightInterfaceSlider extends StatelessWidget { children: [ Expanded( child: UnitedText( - value: light.brightness.toString(), + // value: light.brightness.toString(), + value: '100', unit: "%", valueStyle: context.bodyMedium.copyWith( color: Colors.grey, @@ -57,9 +57,11 @@ class LightInterfaceSlider extends StatelessWidget { trackHeight: 5, ), child: Slider( - value: light.brightness, - onChanged: (value) => - LightsCubit.get(context).setBrightness(light, value), + // value: light.brightness, + value: 100, + onChanged: (value) { + // DevicesCubit.get(context).setBrightness(light, value); + }, min: 0, max: 100, ), diff --git a/lib/features/devices/view/widgets/lights/light_interface_switch.dart b/lib/features/devices/view/widgets/lights/light_interface_switch.dart index 691b201..aa8ca07 100644 --- a/lib/features/devices/view/widgets/lights/light_interface_switch.dart +++ b/lib/features/devices/view/widgets/lights/light_interface_switch.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:syncrow_app/features/devices/bloc/lights/lights_cubit.dart'; -import 'package:syncrow_app/features/devices/model/light_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart'; import 'package:syncrow_app/utils/context_extension.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -14,7 +13,7 @@ class LightInterfaceSwitch extends StatelessWidget { required this.light, }); - final LightModel light; + final DeviceModel light; @override Widget build(BuildContext context) { @@ -52,7 +51,7 @@ class LightInterfaceSwitch extends StatelessWidget { iconSize: MaterialStateProperty.all(25), ), onPressed: () { - LightsCubit.get(context).toggleLight(light); + // DevicesCubit.get(context).toggleLight(light); }, icon: const Icon( Icons.power_settings_new, diff --git a/lib/features/devices/view/widgets/lights/lights_list.dart b/lib/features/devices/view/widgets/lights/lights_list.dart index 84b6119..919ca0a 100644 --- a/lib/features/devices/view/widgets/lights/lights_list.dart +++ b/lib/features/devices/view/widgets/lights/lights_list.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/light_brightness.dart'; import '../../../../shared_widgets/devices_default_switch.dart'; import '../../../../shared_widgets/text_widgets/body_small.dart'; -import '../../../model/light_model.dart'; class LightsList extends StatelessWidget { const LightsList({ @@ -12,7 +12,7 @@ class LightsList extends StatelessWidget { required this.lights, }); - final List lights; + final List lights; @override Widget build(BuildContext context) { diff --git a/lib/features/devices/view/widgets/lights/lights_view.dart b/lib/features/devices/view/widgets/lights/lights_view.dart index efdadef..80d3a23 100644 --- a/lib/features/devices/view/widgets/lights/lights_view.dart +++ b/lib/features/devices/view/widgets/lights/lights_view.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/category_view_app_bar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/lights/light_interface.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/lights_view_list.dart'; - -import '../../../../../generated/assets.dart'; -import '../../../../../utils/resource_manager/color_manager.dart'; -import '../../../bloc/devices_cubit.dart'; -import '../../../bloc/lights/lights_cubit.dart'; -import '../../../model/light_model.dart'; -import 'light_interface.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class LightsView extends StatelessWidget { const LightsView({super.key}); @@ -17,19 +15,20 @@ class LightsView extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => LightsCubit(), + create: (context) => DevicesCubit(), child: BlocBuilder( builder: (context, state) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { - LightModel? selectedLight; - if (DevicesCubit.get(context).getSelectedDevice() is LightModel) { - selectedLight = - DevicesCubit.get(context).getSelectedDevice() as LightModel; + DeviceModel? selectedLight; + if (DevicesCubit.get(context).getSelectedDevice() + is DeviceModel) { + selectedLight = DevicesCubit.get(context).getSelectedDevice() + as DeviceModel; } - List lights = []; - for (var device in DevicesCubit.allCategories[1].devices) { - if (device is LightModel) { + List lights = []; + if (DevicesCubit.allCategories![1].devices != null) { + for (var device in DevicesCubit.allCategories![1].devices!) { lights.add(device); } } diff --git a/lib/features/devices/view/widgets/lights/lights_view_list.dart b/lib/features/devices/view/widgets/lights/lights_view_list.dart index 5f3608e..2348dd1 100644 --- a/lib/features/devices/view/widgets/lights/lights_view_list.dart +++ b/lib/features/devices/view/widgets/lights/lights_view_list.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/lights/lights_list.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; import '../../../../shared_widgets/text_widgets/body_small.dart'; import '../../../bloc/devices_cubit.dart'; -import '../../../model/light_model.dart'; import '../universal_switch.dart'; class LightsViewList extends StatelessWidget { @@ -13,7 +13,7 @@ class LightsViewList extends StatelessWidget { required this.lights, }); - final List lights; + final List lights; @override Widget build(BuildContext context) { @@ -30,7 +30,7 @@ class LightsViewList extends StatelessWidget { children: [ const BodySmall(text: "All Lights"), UniversalSwitch( - category: DevicesCubit.allCategories[1], + category: DevicesCubit.allCategories![1], ), LightsList(lights: lights), ], diff --git a/lib/features/devices/view/widgets/lights_switches/light_switch.dart b/lib/features/devices/view/widgets/lights_switches/light_switch.dart new file mode 100644 index 0000000..75ab87f --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switch.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class LightSwitch extends StatelessWidget { + const LightSwitch({ + super.key, + required this.control, + }); + + final DeviceControlModel control; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return state is DeviceControlLoading + ? const CircularProgressIndicator() + : InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () { + DevicesCubit.get(context) + .deviceControl(control) + .then((value) { + print('Device control response: $value'); + if (control.value ?? true) { + control.value = false; + } else { + control.value = true; + } + }); + }, + child: Stack( + alignment: !control.value! + ? Alignment.topCenter + : Alignment.bottomCenter, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: + const BorderRadius.all(Radius.circular(100.0)), + color: !control.value! + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.switchOffColor, + ), + width: 60, + height: 115, + ), + Padding( + padding: const EdgeInsets.all(5.0), + child: SizedBox.square( + dimension: 60, + child: SvgPicture.asset( + !control.value! + ? Assets.iconsLightSwitchOn + : Assets.iconsLightSwitchOff, + fit: BoxFit.fill, + ), + ), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/features/devices/view/widgets/lights_switches/light_switches.dart b/lib/features/devices/view/widgets/lights_switches/light_switches.dart new file mode 100644 index 0000000..95e91e3 --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switches.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/function_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/ACs/category_view_app_bar.dart'; +import 'package:syncrow_app/features/devices/view/widgets/lights_switches/light_switches_body.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; + +class LightSwitchesView extends StatelessWidget { + const LightSwitchesView({super.key}); + + @override + Widget build(BuildContext context) { + 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: const CategoryViewAppBar(), + body: 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, + ), + ), + child: Padding( + padding: EdgeInsets.only( + top: Constants.appBarHeight, + left: Constants.defaultPadding, + right: Constants.defaultPadding, + bottom: Constants.bottomNavBarHeight, + ), + child: LightSwitchesBody( + device: DeviceModel( + id: 2, + name: 'Bedroom Light', + type: DeviceType.Lights, + status: true, + timer: 0, + image: '', + functions: [ + FunctionModel( + code: 'switch_1', + desc: 'switch 1', + name: 'switch 1', + type: 'Boolean', + values: '{}'), + FunctionModel( + code: 'switch_2', + desc: 'switch 2', + name: 'switch 2', + type: 'Boolean', + values: '{}'), + FunctionModel( + code: 'switch_3', + desc: 'switch 3', + name: 'switch 3', + type: 'Boolean', + values: '{}'), + ]), + ), + ), + ); + }, + )), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart b/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart new file mode 100644 index 0000000..6815fd0 --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart @@ -0,0 +1,260 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/lights_switches/light_switch.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class LightSwitchesBody extends StatelessWidget { + const LightSwitchesBody({ + super.key, + required this.device, + }); + + final DeviceModel device; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Expanded(child: SizedBox.shrink()), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_1', + value: true, + ), + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Bedside Light", + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_2', + value: true, + ), + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Ceiling Light", + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_3', + value: true, + ), + ), + const SizedBox(height: 20), + const SizedBox( + width: 70, + child: BodySmall( + text: "Spotlight", + fontColor: ColorsManager.textPrimaryColor, + textAlign: TextAlign.center, + ), + ), + ], + ), + ], + ), + Expanded( + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + child: InkWell( + onTap: () {}, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(100), + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100), + ), + child: Center( + child: BodySmall( + text: "On", + style: context.bodyMedium.copyWith( + color: + ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 10), + BodySmall( + text: "All On", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + ), + ), + ], + ), + const SizedBox( + width: 20, + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + child: InkWell( + onTap: () {}, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(100), + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100), + ), + child: Center( + child: Icon( + Icons.access_time, + color: ColorsManager.primaryColorWithOpacity, + size: 25, + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 10), + BodySmall( + text: "Timer", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + ), + ), + ], + ), + const SizedBox( + width: 20, + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + elevation: 3, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + child: InkWell( + onTap: () {}, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(100), + ), + ), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(100), + ), + child: Center( + child: BodySmall( + text: "Off", + style: context.bodyMedium.copyWith( + color: + ColorsManager.primaryColorWithOpacity, + fontWeight: FontWeight.bold), + ), + ), + ), + ], + ), + ), + ), + const SizedBox(height: 10), + BodySmall( + text: "All Off", + style: context.bodyMedium.copyWith( + color: ColorsManager.textPrimaryColor, + ), + ), + ], + ), + ], + ), + ), + ), + ], + ); + } +} 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/room_page_switch.dart b/lib/features/devices/view/widgets/room_page_switch.dart index cfaafbe..1cbbc35 100644 --- a/lib/features/devices/view/widgets/room_page_switch.dart +++ b/lib/features/devices/view/widgets/room_page_switch.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_app/features/devices/model/ac_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_interface.dart'; import 'package:syncrow_app/features/shared_widgets/custom_switch.dart'; @@ -38,7 +37,7 @@ class RoomPageSwitch extends StatelessWidget { Navigator.push( context, CustomPageRoute( - builder: (context) => AcInterface(acModel: device as ACModel), + builder: (context) => AcInterface(deviceModel: device), ), ); } diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index 8941e6c..0ed7b45 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -40,19 +40,18 @@ class RoomsSlider extends StatelessWidget { ), ), ), - ...SpacesCubit.get(context).selectedSpace.rooms.map( + if (SpacesCubit.selectedSpace != null) + if (SpacesCubit.selectedSpace!.rooms != null) + ...SpacesCubit.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)); + SpacesCubit.selectedSpace!.rooms!.indexOf(room)); }, child: TitleMedium( - text: room.name, + text: room.name!, style: context.titleMedium.copyWith( fontSize: 25, color: SpacesCubit.get(context).selectedRoom == room diff --git a/lib/features/devices/view/widgets/screens/screens_view.dart b/lib/features/devices/view/widgets/screens/screens_view.dart deleted file mode 100644 index fe19fc8..0000000 --- a/lib/features/devices/view/widgets/screens/screens_view.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -class ScreensView extends StatelessWidget { - const ScreensView({super.key}); - - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} diff --git a/lib/features/devices/view/widgets/wizard_switches.dart b/lib/features/devices/view/widgets/wizard_switches.dart index 6400625..8a200d2 100644 --- a/lib/features/devices/view/widgets/wizard_switches.dart +++ b/lib/features/devices/view/widgets/wizard_switches.dart @@ -18,68 +18,81 @@ class WizartSwitches extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - crossAxisSpacing: 10, - mainAxisSpacing: 10, - childAspectRatio: 1.5, - ), - padding: const EdgeInsets.only(top: 10), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: DevicesCubit.allCategories.length, - itemBuilder: (_, index) { - return InkWell( - onTap: () { - DevicesCubit.get(context).selectCategory(index); - //Navigate to the chosen category view without animation + return state is! DevicesLoading + ? DevicesCubit.allCategories != null + ? GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 10, + mainAxisSpacing: 10, + childAspectRatio: 1.5, + ), + padding: const EdgeInsets.only(top: 10), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: DevicesCubit.allCategories!.length, + itemBuilder: (_, index) { + return InkWell( + onTap: () { + DevicesCubit.get(context).selectCategory(index); + //Navigate to the chosen category view without animation - Navigator.push(context, CustomPageRoute(builder: (context) { - return DevicesCubit.get(context).chosenCategoryView!; - })); - }, - child: DefaultContainer( - child: Padding( - padding: const EdgeInsets.only(top: 10, right: 10, left: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - SvgPicture.asset( - DevicesCubit.allCategories[index].icon, - fit: BoxFit.contain, - ), - CustomSwitch( - category: DevicesCubit.allCategories[index], - ), - ], - ), - Expanded( - child: FittedBox( - fit: BoxFit.scaleDown, - child: BodyLarge( - text: DevicesCubit.allCategories[index].name, - style: context.bodyLarge.copyWith( - fontWeight: FontWeight.bold, - height: 0, - fontSize: 24, - color: Colors.grey, + Navigator.push(context, + CustomPageRoute(builder: (context) { + return DevicesCubit.get(context) + .chosenCategoryView!; + })); + }, + child: DefaultContainer( + child: Padding( + padding: const EdgeInsets.only( + top: 10, right: 10, left: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + SvgPicture.asset( + DevicesCubit.allCategories![index].icon!, + fit: BoxFit.contain, + ), + CustomSwitch( + category: + DevicesCubit.allCategories![index], + ), + ], + ), + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + child: BodyLarge( + text: DevicesCubit + .allCategories![index].name!, + style: context.bodyLarge.copyWith( + fontWeight: FontWeight.bold, + height: 0, + fontSize: 24, + color: Colors.grey, + ), + ), + ), + ), + ], ), ), ), - ), - ], - ), - ), - ), - ); - }, - ); + ); + }, + ) + : const SizedBox.shrink() + : const Center( + child: CircularProgressIndicator(), + ); }, ); } diff --git a/lib/features/shared_widgets/text_widgets/body_small.dart b/lib/features/shared_widgets/text_widgets/body_small.dart index f784dc7..5973781 100644 --- a/lib/features/shared_widgets/text_widgets/body_small.dart +++ b/lib/features/shared_widgets/text_widgets/body_small.dart @@ -10,6 +10,7 @@ class BodySmall extends StatelessWidget { this.fontColor, this.fontSize, this.fontWeight, + this.textAlign, }); final String text; @@ -20,6 +21,8 @@ class BodySmall extends StatelessWidget { final double? fontSize; final FontWeight? fontWeight; + final TextAlign? textAlign; + @override Widget build(BuildContext context) => CustomText( text, @@ -27,5 +30,6 @@ class BodySmall extends StatelessWidget { fontColor: fontColor, fontSize: fontSize, fontWeight: fontWeight, + textAlign: textAlign, ); } diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index d36745f..99a826e 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -1,59 +1,67 @@ import 'package:flutter/material.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +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/login_view.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; -import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/helpers/custom_page_route.dart'; class SplashView extends StatelessWidget { const SplashView({super.key}); @override Widget build(BuildContext context) { - //TODO remove this delay - Future.value().then((value) async { - var isLoggedIn = await const FlutterSecureStorage() - .read(key: Constants.tokenAccessKey) != - 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 BlocBuilder( + builder: (context, state) { + if (state is AuthTokenSuccess) { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacementNamed(context, Routes.homeRoute); + }); + } else { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacement( + context, + CustomPageRoute(builder: (context) => const LoginView()), + ); + }); + } + + 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, - ) - ], - ), + ); + }, ); } } diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index b3da647..48acd39 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -4,6 +4,7 @@ class Assets { static const String assetsIconsSettings = 'assets/icons/settings.svg'; static const String fontsAftikaRegular = 'assets/fonts/AftikaRegular.ttf'; + static const String icons3GangSwitch = 'assets/icons/3GangSwitch.svg'; static const String iconsAC = 'assets/icons/AC.svg'; static const String iconsActive = 'assets/icons/active.svg'; static const String iconsAutomatedClock = 'assets/icons/automated_clock.svg'; @@ -16,6 +17,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'; @@ -23,6 +30,7 @@ class Assets { static const String iconsFan3 = 'assets/icons/fan-3.svg'; static const String iconsFilter = 'assets/icons/filter.png'; static const String iconsFrequency = 'assets/icons/frequency.svg'; + static const String iconsGang = 'assets/icons/gang.svg'; static const String iconsGateway = 'assets/icons/Gateway.svg'; static const String iconsGoogle = 'assets/icons/Google.svg'; static const String iconsHome = 'assets/icons/home.svg'; @@ -31,6 +39,8 @@ class Assets { static const String iconsLayout = 'assets/icons/Layout.svg'; static const String iconsLayoutFill = 'assets/icons/Layout-fill.svg'; static const String iconsLight = 'assets/icons/Light.svg'; + static const String iconsLightSwitchOff = 'assets/icons/lightSwitchOff.svg'; + static const String iconsLightSwitchOn = 'assets/icons/lightSwitchOn.svg'; static const String iconsLock = 'assets/icons/lock.svg'; static const String iconsLogo = 'assets/icons/logo.png'; static const String iconsMenu = 'assets/icons/Menu.svg'; @@ -41,11 +51,14 @@ class Assets { static const String iconsRoutinesFill = 'assets/icons/Routines-fill.svg'; static const String iconsScan = 'assets/icons/Scan.svg'; static const String iconsScreen = 'assets/icons/Screen.svg'; + static const String iconsSensors = 'assets/icons/sensors.svg'; static const String iconsSettings = 'assets/icons/settings.png'; static const String iconsSummer = 'assets/icons/Summer.svg'; static const String iconsSummerMode = 'assets/icons/summer_mode.svg'; static const String iconsSunnyMode = 'assets/icons/sunnyMode.svg'; static const String iconsSustainability = 'assets/icons/sustainability.svg'; + static const String iconsVector = 'assets/icons/Vector.svg'; + static const String iconsVector1 = 'assets/icons/Vector-1.svg'; static const String iconsVoltMeter = 'assets/icons/volt-meter.svg'; static const String iconsWindyMode = 'assets/icons/windyMode.svg'; static const String iconsWinter = 'assets/icons/Winter.svg'; diff --git a/lib/main.dart b/lib/main.dart index 2fa8e01..189df41 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,11 +2,9 @@ import 'dart:async'; 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/services/locator.dart'; import 'package:syncrow_app/utils/bloc_observer.dart'; import 'package:syncrow_app/utils/helpers/localization_helpers.dart'; -import 'package:syncrow_app/utils/resource_manager/constants.dart'; import 'my_app.dart'; @@ -35,11 +33,6 @@ void main() { initialSetup(); //final SharedPreferences prefs = await SharedPreferences.getInstance(); - // var isLoggedIn = (prefs.getBool('isLoggedIn') == null) ? false : prefs.getBool('isLoggedIn'); - // - var storage = const FlutterSecureStorage(); - bool isLoggedIn = - await (storage.read(key: Constants.tokenAccessKey)) != null; //to save the locale in the shared preferences await LocalizationService.saveLocale(const Locale("en", "AE")); diff --git a/lib/my_app.dart b/lib/my_app.dart index b92aa33..99375cc 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -1,13 +1,10 @@ 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'; import 'package:syncrow_app/utils/resource_manager/theme_manager.dart'; - -import 'features/devices/bloc/devices_cubit.dart'; import 'navigation/router.dart' as router; import 'navigation/routing_constants.dart'; @@ -28,14 +25,9 @@ class MyApp extends StatelessWidget { create: (context) => AuthCubit(), ), BlocProvider( + lazy: false, create: (context) => NavCubit(), ), - BlocProvider( - create: (context) => SpacesCubit(), - ), - BlocProvider( - create: (context) => DevicesCubit(), - ), ], child: MaterialApp( debugShowCheckedModeBanner: false, diff --git a/lib/navigation/router.dart b/lib/navigation/router.dart index 2888538..49647e9 100644 --- a/lib/navigation/router.dart +++ b/lib/navigation/router.dart @@ -25,7 +25,7 @@ class Router { case Routes.devicesRoute: return MaterialPageRoute( - builder: (_) => const CategoriesView(), settings: settings); + builder: (_) => const DevicesView(), settings: settings); case Routes.profileRoute: return MaterialPageRoute( diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 592fffc..bfe22be 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -1,6 +1,7 @@ abstract class ApiEndpoints { - static const String baseUrl = 'http://100.107.182.63:4001'; + static const String baseUrl = 'https://syncrow.azurewebsites.net'; + // Authentication static const String signUp = '$baseUrl/authentication/user/signup'; static const String login = '$baseUrl/authentication/user/login'; static const String deleteUser = '$baseUrl/authentication/user/delete/{id}'; @@ -8,4 +9,14 @@ abstract class ApiEndpoints { static const String verifyOtp = '$baseUrl/authentication/user/verify-otp'; static const String forgetPassword = '$baseUrl/authentication/user/forget-password'; + +// Spaces + static const String spaces = '$baseUrl/home'; + static const String rooms = '$baseUrl/room'; + + // Devices + static const String control = '$baseUrl/device/control'; + + //groups + static const String groups = '$baseUrl/group'; } diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index 62d406c..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,24 +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 token = Token.fromJson(json['data']); - return token; - }); - 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/devices_api.dart b/lib/services/api/devices_api.dart new file mode 100644 index 0000000..cf885e8 --- /dev/null +++ b/lib/services/api/devices_api.dart @@ -0,0 +1,37 @@ +import 'package:syncrow_app/features/devices/model/device_category_model.dart'; +import 'package:syncrow_app/features/devices/model/device_control_model.dart'; +import 'package:syncrow_app/services/api/api_links_endpoints.dart'; +import 'package:syncrow_app/services/api/http_service.dart'; + +class DevicesAPI { + static final HTTPService _httpService = HTTPService(); + + static Future> controlDevice( + DeviceControlModel controlModel) async { + final response = await _httpService.post( + path: ApiEndpoints.control, + body: controlModel.toJson(), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } + + static Future> fetchGroups(int spaceId) async { + Map params = { + "homeId": spaceId, + "pageSize": 100, + "pageNo": 1 + }; + final response = await _httpService.get( + path: ApiEndpoints.groups, + queryParameters: params, + showServerMessage: false, + expectedResponseModel: (json) => + DevicesCategoryModel.fromJsonList(json['groups']), + ); + return response; + } +} diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index a247d35..3460bc4 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -1,4 +1,6 @@ import 'package:dio/dio.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:syncrow_app/features/auth/model/token.dart'; class HTTPInterceptor extends InterceptorsWrapper { // @override @@ -7,36 +9,38 @@ class HTTPInterceptor extends InterceptorsWrapper { // return handler.next(response); // } // - // @override - // void onRequest(RequestOptions options, - // RequestInterceptorHandler handler) async { - // // TODO: Implement logic for adding headers to requests. - // // This method is called before a request is sent. - // super.onRequest(options, handler); - // } - // - // @override - // void onError(DioException err, ErrorInterceptorHandler handler) async { - // // TODO: Implement error handling logic. - // // This method is called when an error occurs during a request. - // super.onError(err, handler); - // } - // - // /// Validates the response and returns true if it is successful (status code 2xx). - // Future validateResponse(Response response) async { - // if (response.statusCode != null) { - // 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; - // } - // } + @override + void onRequest( + RequestOptions options, RequestInterceptorHandler handler) async { + var storage = const FlutterSecureStorage(); + var token = await storage.read(key: Token.loginAccessTokenKey); + + options.headers['Authorization'] = 'Bearer $token'; + super.onRequest(options, handler); + } +// +// @override +// void onError(DioException err, ErrorInterceptorHandler handler) async { +// // TODO: Implement error handling logic. +// // This method is called when an error occurs during a request. +// super.onError(err, handler); +// } +// +// /// Validates the response and returns true if it is successful (status code 2xx). +// Future validateResponse(Response response) async { +// if (response.statusCode != null) { +// 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; +// } +// } } diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index ec76fa5..a5f24bd 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: 60), - receiveTimeout: const Duration(seconds: 60), + connectTimeout: const Duration(seconds: 30), + receiveTimeout: const Duration(seconds: 30), ), ); @@ -37,10 +37,14 @@ class HTTPService { path, queryParameters: queryParameters, ); + + // debugPrint("status code is ${response.statusCode}"); + // debugPrint("response data is ${response.data}"); return expectedResponseModel(response.data); } catch (error) { debugPrint("******* Error"); debugPrint(error.toString()); + debugPrint("******* Error End"); rethrow; } } @@ -59,8 +63,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/network_exception.dart b/lib/services/api/network_exception.dart index d117750..0cec78b 100644 --- a/lib/services/api/network_exception.dart +++ b/lib/services/api/network_exception.dart @@ -28,8 +28,8 @@ class ServerFailure extends Failure { return ServerFailure("Bad certificate!"); case DioExceptionType.badResponse: - return ServerFailure.fromResponse( - dioError.response!.statusCode!, dioError.response!.data); + return ServerFailure.fromResponse(dioError.response!.statusCode!, + dioError.response!.data!["message"]); case DioExceptionType.cancel: return ServerFailure("The request to ApiServer was canceled"); @@ -38,6 +38,9 @@ class ServerFailure extends Failure { case DioExceptionType.unknown: return ServerFailure("Unexpected Error, Please try again!"); + + default: + return ServerFailure("Unexpected Error, Please try again!"); } } diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart new file mode 100644 index 0000000..3f93385 --- /dev/null +++ b/lib/services/api/spaces_api.dart @@ -0,0 +1,41 @@ +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/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 final HTTPService _httpService = HTTPService(); + + static Future> getSpaces() async { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + 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> 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; + } +} diff --git a/lib/utils/helpers/custom_page_route.dart b/lib/utils/helpers/custom_page_route.dart index ee34a24..c39585a 100644 --- a/lib/utils/helpers/custom_page_route.dart +++ b/lib/utils/helpers/custom_page_route.dart @@ -1,11 +1,15 @@ import 'package:flutter/material.dart'; -class CustomPageRoute extends MaterialPageRoute { - CustomPageRoute({ - required super.builder, - super.settings, - }); +class CustomPageRoute extends PageRouteBuilder { + final WidgetBuilder builder; - @override - Duration get transitionDuration => const Duration(milliseconds: 0); + CustomPageRoute({required this.builder}) + : super( + pageBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation) { + return builder(context); + }, + transitionDuration: Duration.zero, + reverseTransitionDuration: Duration.zero, + ); } diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index 10022fd..6675670 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; abstract class ColorsManager { static const Color textPrimaryColor = Color(0xFF5D5D5D); + static const Color switchOffColor = Color(0x7F8D99AE); static const Color primaryColor = Color(0xFF0030CB); static Color primaryColorWithOpacity = const Color(0xFF023DFE).withOpacity(0.6); diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 6327b1b..1ec5ab8 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -11,14 +11,17 @@ abstract class Constants { static const double defaultPadding = 16; - static const String tokenAccessKey = 'access_token'; + static const String token = ''; } enum DeviceType { AC, Lights, - Door, + DoorLock, Curtain, - Screens, + ThreeGang, Gateway, + Sensors, + Gang, + Other, }