From 0f3cc453ce28cff8c01015ab5e295c70985e422d Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 12 Mar 2024 11:14:31 +0300 Subject: [PATCH 01/15] Semi-implemented getting spaces feature --- assets/icons/DoorLockLinkage.svg | 13 ++ assets/icons/DoorLockLock.svg | 61 +++++ assets/icons/DoorLockMembers.svg | 14 ++ assets/icons/DoorLockPassword.svg | 16 ++ assets/icons/DoorLockRecords.svg | 16 ++ .../app_layout/bloc/spaces_cubit.dart | 220 ++---------------- .../app_layout/bloc/spaces_state.dart | 6 + .../app_layout/model/space_model.dart | 12 +- lib/features/app_layout/view/app_layout.dart | 20 +- .../view/widgets/app_bar_home_dropdown.dart | 2 +- .../app_layout/view/widgets/app_body.dart | 24 +- lib/features/auth/bloc/auth_cubit.dart | 10 +- lib/features/auth/model/token.dart | 18 +- lib/features/auth/model/user_model.dart | 26 +-- .../view/widgets/devices_view_body.dart | 5 +- .../devices/view/widgets/rooms_slider.dart | 6 +- lib/features/splash/view/splash_view.dart | 4 +- lib/main.dart | 7 - lib/services/api/api_links_endpoints.dart | 6 +- lib/services/api/authentication_api.dart | 5 +- lib/services/api/http_interceptor.dart | 67 +++--- lib/services/api/http_service.dart | 7 +- lib/services/api/network_exception.dart | 3 + lib/services/api/spaces_api.dart | 35 +++ lib/utils/resource_manager/constants.dart | 2 +- 25 files changed, 311 insertions(+), 294 deletions(-) create mode 100644 assets/icons/DoorLockLinkage.svg create mode 100644 assets/icons/DoorLockLock.svg create mode 100644 assets/icons/DoorLockMembers.svg create mode 100644 assets/icons/DoorLockPassword.svg create mode 100644 assets/icons/DoorLockRecords.svg create mode 100644 lib/services/api/spaces_api.dart 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/lib/features/app_layout/bloc/spaces_cubit.dart b/lib/features/app_layout/bloc/spaces_cubit.dart index c090e93..995a165 100644 --- a/lib/features/app_layout/bloc/spaces_cubit.dart +++ b/lib/features/app_layout/bloc/spaces_cubit.dart @@ -1,210 +1,23 @@ +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(); + } 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; + SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null; RoomModel? selectedRoom; @@ -229,7 +42,7 @@ class SpacesCubit extends Cubit { if (index == 0) { unselectRoom(); } else { - selectedRoom = selectedSpace.rooms[index - 1]; + selectedRoom = selectedSpace!.rooms![index - 1]; } emit(RoomSelected(selectedRoom!)); } @@ -244,7 +57,7 @@ class SpacesCubit extends Cubit { if (index == 0) { unselectRoom(); } else { - selectedRoom = selectedSpace.rooms[index - 1]; + selectedRoom = selectedSpace!.rooms![index - 1]; } emit(RoomSelected(selectedRoom!)); } @@ -266,5 +79,16 @@ 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(); + emit(SpacesLoaded(spaces)); + } on DioException catch (e) { + emit(SpacesError(e.message ?? "Something went wrong")); + throw ServerFailure.fromDioError(e); + } catch (e) { + emit(SpacesError(e.toString())); + } + } } diff --git a/lib/features/app_layout/bloc/spaces_state.dart b/lib/features/app_layout/bloc/spaces_state.dart index 9d9c527..f10d2b9 100644 --- a/lib/features/app_layout/bloc/spaces_state.dart +++ b/lib/features/app_layout/bloc/spaces_state.dart @@ -12,6 +12,12 @@ class SpacesLoaded extends SpacesState { SpacesLoaded(this.spaces); } +class SpacesError extends SpacesState { + final String errMessage; + + SpacesError(this.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..f94be8e 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; + final List? rooms; SpaceModel({ required this.id, @@ -21,9 +21,9 @@ 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: [], ); } } diff --git a/lib/features/app_layout/view/app_layout.dart b/lib/features/app_layout/view/app_layout.dart index ac465f4..788fe9a 100644 --- a/lib/features/app_layout/view/app_layout.dart +++ b/lib/features/app_layout/view/app_layout.dart @@ -2,6 +2,7 @@ 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'; @@ -20,13 +21,18 @@ class AppLayout extends StatelessWidget { statusBarIconBrightness: Brightness.light, ), child: SafeArea( - child: Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: DefaultAppBar(context), - body: const AppBody(), - bottomNavigationBar: const DefaultNavBar(), + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: state is SpacesLoaded ? DefaultAppBar(context) : null, + body: const AppBody(), + bottomNavigationBar: + state is SpacesLoaded ? const DefaultNavBar() : null, + ); + }, ), ), ); 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..0d07911 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 @@ -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, diff --git a/lib/features/app_layout/view/widgets/app_body.dart b/lib/features/app_layout/view/widgets/app_body.dart index e98fa87..da10b5f 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'; @@ -25,10 +26,25 @@ class AppBody extends StatelessWidget { opacity: 0.4, ), ), - child: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: NavCubit.of(context).pageController, - children: NavCubit.of(context).pages, + child: BlocConsumer( + listener: (context, state) { + if (state is SpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errMessage), + ), + ); + } + }, + builder: (context, state) { + return state is! SpacesLoading + ? PageView( + physics: const NeverScrollableScrollPhysics(), + controller: NavCubit.of(context).pageController, + children: NavCubit.of(context).pages, + ) + : const Center(child: CircularProgressIndicator()); + }, ) // NavCubit.of(context).currentPage, ); diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 95d6115..e78775b 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -8,7 +8,6 @@ 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'; @@ -35,9 +34,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,7 +53,7 @@ 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); user = UserModel.fromToken(token); emit(AuthSuccess()); @@ -62,6 +61,7 @@ class AuthCubit extends Cubit { emit(AuthError('Something went wrong')); } } on DioException catch (e) { + emit(AuthError(e.message ?? "Something went wrong")); throw ServerFailure.fromDioError(e); } } @@ -70,7 +70,7 @@ class AuthCubit extends Cubit { emit(AuthLoading()); try { FlutterSecureStorage storage = const FlutterSecureStorage(); - await storage.delete(key: Constants.tokenAccessKey); + await storage.delete(key: Token.loginAccessTokenKey); emit(AuthLoggedOut()); } on DioException catch (e) { throw ServerFailure.fromDioError(e); diff --git a/lib/features/auth/model/token.dart b/lib/features/auth/model/token.dart index c87bf82..5f3b22b 100644 --- a/lib/features/auth/model/token.dart +++ b/lib/features/auth/model/token.dart @@ -4,8 +4,8 @@ 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; @@ -16,6 +16,10 @@ class Token { bool get accessTokenIsNotEmpty => accessToken.isNotEmpty; + bool get refreshTokenIsNotEmpty => refreshToken.isNotEmpty; + + bool get isNotEmpty => accessToken.isNotEmpty && refreshToken.isNotEmpty; + Token( this.accessToken, this.refreshToken, @@ -26,13 +30,17 @@ class Token { 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] ?? ''); } - Map toJson() => {loginRefreshToken: refreshToken}; + Map toJson() => {loginRefreshTokenKey: refreshToken}; + + Map accessTokenToJson() => {loginAccessTokenKey: accessToken}; Map decodeToken() { final parts = accessToken.split('.'); diff --git a/lib/features/auth/model/user_model.dart b/lib/features/auth/model/user_model.dart index 4d003a5..beafad5 100644 --- a/lib/features/auth/model/user_model.dart +++ b/lib/features/auth/model/user_model.dart @@ -1,7 +1,7 @@ import 'package:syncrow_app/features/auth/model/token.dart'; class UserModel { - final String? id; + final String? uuid; final String? email; final String? name; final String? photoUrl; @@ -12,18 +12,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 +24,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 +34,18 @@ class UserModel { ); } + //uuid to json + + Map uuIdAsJson() => { + 'userUuid': uuid, + }; + //from token factory UserModel.fromToken(Token token) { Map tempJson = token.decodeToken(); return UserModel( - id: tempJson['uuid'].toString(), + uuid: tempJson['uuid'].toString(), email: tempJson['email'], name: null, photoUrl: null, @@ -61,7 +57,7 @@ class UserModel { Map toJson() { return { - 'id': id, + 'id': uuid, 'email': email, 'name': name, 'photoUrl': photoUrl, diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 35858b7..8b39984 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -46,7 +46,10 @@ class DevicesViewBody extends StatelessWidget { }, children: [ const WizardPage(), - ...SpacesCubit.get(context).selectedSpace.rooms.map( + ...SpacesCubit.get(context) + .selectedSpace! + .rooms! + .map( (room) { return RoomPage( room: room, diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index 8941e6c..b7de8bd 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -40,15 +40,15 @@ class RoomsSlider extends StatelessWidget { ), ), ), - ...SpacesCubit.get(context).selectedSpace.rooms.map( + ...SpacesCubit.get(context).selectedSpace!.rooms!.map( (room) => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: InkWell( onTap: () { SpacesCubit.get(context).roomSliderPageChanged( SpacesCubit.get(context) - .selectedSpace - .rooms + .selectedSpace! + .rooms! .indexOf(room)); }, child: TitleMedium( diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index d36745f..5186421 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_app/features/auth/model/token.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'; class SplashView extends StatelessWidget { const SplashView({super.key}); @@ -13,7 +13,7 @@ class SplashView extends StatelessWidget { //TODO remove this delay Future.value().then((value) async { var isLoggedIn = await const FlutterSecureStorage() - .read(key: Constants.tokenAccessKey) != + .read(key: Token.loginAccessTokenKey) != null; if (isLoggedIn) { Navigator.pushReplacementNamed(context, Routes.homeRoute); 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/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 592fffc..7259ebb 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,7 @@ 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'; } diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index 62d406c..06d062b 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -24,10 +24,7 @@ class AuthenticationAPI { path: ApiEndpoints.login, body: model.toJson(), showServerMessage: false, - expectedResponseModel: (json) { - Token token = Token.fromJson(json['data']); - return token; - }); + expectedResponseModel: (json) => Token.fromJson(json['data'])); debugPrint("response: $response"); return response; } diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index a247d35..cb018c7 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; class HTTPInterceptor extends InterceptorsWrapper { // @override @@ -7,36 +8,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 { + //pass the token from the flutter secure storage to the request header + + options.headers['Authorization'] = 'Bearer ${AuthCubit.token.accessToken}'; + + 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..ecaeae7 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: 5), + receiveTimeout: const Duration(seconds: 5), ), ); @@ -37,6 +37,9 @@ 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"); diff --git a/lib/services/api/network_exception.dart b/lib/services/api/network_exception.dart index d117750..2082e97 100644 --- a/lib/services/api/network_exception.dart +++ b/lib/services/api/network_exception.dart @@ -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..dad5df9 --- /dev/null +++ b/lib/services/api/spaces_api.dart @@ -0,0 +1,35 @@ +import 'package:syncrow_app/features/app_layout/model/space_model.dart'; +import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/services/api/api_links_endpoints.dart'; +import 'package:syncrow_app/services/api/http_service.dart'; + +class SpacesAPI { + // static Future loginWithEmail( + // {required LoginWithEmailModel model}) async { + // final response = await HTTPService().post( + // path: ApiEndpoints.login, + // body: model.toJson(), + // showServerMessage: false, + // expectedResponseModel: (json) { + // Token token = Token.fromJson(json['data']); + // return token; + // }); + // debugPrint("response: $response"); + // return response; + // } + + static Future> getSpaces() async { + final response = await HTTPService().get( + path: "${ApiEndpoints.spaces}/${AuthCubit.user!.uuid}", + showServerMessage: false, + expectedResponseModel: (json) { + List spaces = []; + for (var space in json) { + spaces.add(SpaceModel.fromJson(space)); + } + return spaces; + }, + ); + return response; + } +} diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 6327b1b..8a67e03 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -11,7 +11,7 @@ abstract class Constants { static const double defaultPadding = 16; - static const String tokenAccessKey = 'access_token'; + static const String token = ''; } enum DeviceType { From 024f15728bd211d155aea4504a026744727abecb Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Wed, 13 Mar 2024 13:52:22 +0300 Subject: [PATCH 02/15] getting spaces and rooms from api {null checks} --- lib/features/app_layout/bloc/nav_cubit.dart | 10 +- .../app_layout/bloc/spaces_cubit.dart | 26 ++++- .../app_layout/bloc/spaces_state.dart | 12 ++ .../app_layout/model/space_model.dart | 2 +- lib/features/app_layout/view/app_layout.dart | 70 ++++++++---- .../view/widgets/app_bar_home_dropdown.dart | 11 +- .../app_layout/view/widgets/app_body.dart | 61 +++++----- .../view/widgets/default_app_bar.dart | 4 +- lib/features/auth/bloc/auth_cubit.dart | 13 ++- lib/features/auth/model/token.dart | 37 +++++- lib/features/auth/model/user_model.dart | 7 +- .../auth/view/widgets/login/login_button.dart | 46 -------- .../auth/view/widgets/login/login_form.dart | 41 ++++++- .../auth/view/widgets/login/login_view.dart | 7 -- lib/features/devices/model/room_model.dart | 10 +- .../view/widgets/devices_view_body.dart | 49 +++++--- .../devices/view/widgets/room_page.dart | 2 +- .../devices/view/widgets/rooms_slider.dart | 47 ++++---- lib/features/splash/view/splash_view.dart | 107 +++++++++++------- lib/generated/assets.dart | 6 + lib/my_app.dart | 4 - lib/services/api/api_links_endpoints.dart | 1 + lib/services/api/authentication_api.dart | 24 ++-- lib/services/api/http_interceptor.dart | 9 +- lib/services/api/http_service.dart | 8 +- lib/services/api/spaces_api.dart | 42 ++++--- 26 files changed, 390 insertions(+), 266 deletions(-) delete mode 100644 lib/features/auth/view/widgets/login/login_button.dart diff --git a/lib/features/app_layout/bloc/nav_cubit.dart b/lib/features/app_layout/bloc/nav_cubit.dart index 87a1967..ebcf53a 100644 --- a/lib/features/app_layout/bloc/nav_cubit.dart +++ b/lib/features/app_layout/bloc/nav_cubit.dart @@ -16,6 +16,13 @@ class NavCubit extends Cubit { NavCubit() : super(NavInitial()); static NavCubit of(context) => BlocProvider.of(context); + + //functoin to do the important work when the user logs out + static clear() { + pageIndex = 0; + pageController.jumpToPage(0); + } + static int pageIndex = 0; static Map> appBarActions = { @@ -139,11 +146,10 @@ class NavCubit extends Cubit { const MenuView(), ]; - final PageController pageController = PageController(); + static final PageController pageController = PageController(); void updatePageIndex(int index) { pageIndex = index; - print('index: $index'); pageController.animateToPage(index, duration: const Duration(milliseconds: 150), curve: Curves.easeIn); emit(NavChangePage()); diff --git a/lib/features/app_layout/bloc/spaces_cubit.dart b/lib/features/app_layout/bloc/spaces_cubit.dart index 995a165..f9c7e50 100644 --- a/lib/features/app_layout/bloc/spaces_cubit.dart +++ b/lib/features/app_layout/bloc/spaces_cubit.dart @@ -10,7 +10,11 @@ part 'spaces_state.dart'; class SpacesCubit extends Cubit { SpacesCubit() : super(SpacesInitial()) { - fetchSpaces(); + fetchSpaces().then((value) { + if (selectedSpace != null) { + fetchRooms(selectedSpace!); + } + }); } static SpacesCubit get(context) => BlocProvider.of(context); @@ -83,12 +87,24 @@ class SpacesCubit extends Cubit { emit(SpacesLoading()); try { spaces = await SpacesAPI.getSpaces(); + spaces.isNotEmpty ? selectSpace(spaces.first) : null; emit(SpacesLoaded(spaces)); } on DioException catch (e) { - emit(SpacesError(e.message ?? "Something went wrong")); - throw ServerFailure.fromDioError(e); - } catch (e) { - emit(SpacesError(e.toString())); + emit(SpacesError(ServerFailure.fromDioError(e).errMessage)); + } + } + + fetchRooms(SpaceModel space) async { + emit(SpaceRoomsLoading()); + try { + space.rooms = await SpacesAPI.getRooms(space.id!); + if (space.rooms != null) { + emit(SpaceRoomsLoaded(space.rooms!)); + } else { + emit(SpaceRoomsError("No rooms found")); + } + } on DioException catch (e) { + emit(SpacesError(ServerFailure.fromDioError(e).errMessage)); } } } diff --git a/lib/features/app_layout/bloc/spaces_state.dart b/lib/features/app_layout/bloc/spaces_state.dart index f10d2b9..8fd63d4 100644 --- a/lib/features/app_layout/bloc/spaces_state.dart +++ b/lib/features/app_layout/bloc/spaces_state.dart @@ -18,6 +18,18 @@ class SpacesError extends SpacesState { SpacesError(this.errMessage); } +class SpaceRoomsLoading extends SpacesLoading {} + +class SpaceRoomsLoaded extends SpacesLoading { + final List rooms; + + SpaceRoomsLoaded(this.rooms); +} + +class SpaceRoomsError extends SpacesError { + SpaceRoomsError(super.errMessage); +} + class SpacesSelected extends SpacesState { final SpaceModel space; diff --git a/lib/features/app_layout/model/space_model.dart b/lib/features/app_layout/model/space_model.dart index f94be8e..7205ebf 100644 --- a/lib/features/app_layout/model/space_model.dart +++ b/lib/features/app_layout/model/space_model.dart @@ -3,7 +3,7 @@ import 'package:syncrow_app/features/devices/model/room_model.dart'; class SpaceModel { final int? id; final String? name; - final List? rooms; + late List? rooms; SpaceModel({ required this.id, diff --git a/lib/features/app_layout/view/app_layout.dart b/lib/features/app_layout/view/app_layout.dart index 788fe9a..bce053e 100644 --- a/lib/features/app_layout/view/app_layout.dart +++ b/lib/features/app_layout/view/app_layout.dart @@ -6,6 +6,7 @@ import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/app_body.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/default_app_bar.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/default_nav_bar.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; class AppLayout extends StatelessWidget { @@ -13,30 +14,51 @@ class AppLayout extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return AnnotatedRegion( - value: SystemUiOverlayStyle( - statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), - statusBarIconBrightness: Brightness.light, - ), - child: SafeArea( - child: BlocBuilder( - builder: (context, state) { - return Scaffold( - backgroundColor: ColorsManager.backgroundColor, - extendBodyBehindAppBar: true, - extendBody: true, - appBar: state is SpacesLoaded ? DefaultAppBar(context) : null, - body: const AppBody(), - bottomNavigationBar: - state is SpacesLoaded ? const DefaultNavBar() : null, - ); - }, - ), - ), - ); - }, + return BlocProvider( + create: (context) => SpacesCubit(), + child: BlocListener( + listener: (context, state) { + if (state is SpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errMessage), + ), + ); + Navigator.of(context) + .popUntil((route) => route.settings.name == Routes.authLogin); + } + }, + child: BlocBuilder( + builder: (context, state) { + return AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: ColorsManager.primaryColor.withOpacity(0.5), + statusBarIconBrightness: Brightness.light, + ), + child: SafeArea( + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: ColorsManager.backgroundColor, + extendBodyBehindAppBar: true, + extendBody: true, + appBar: + state is! SpacesLoading || state is! SpaceRoomsLoading + ? const DefaultAppBar() + : null, + body: const AppBody(), + bottomNavigationBar: + state is! SpacesLoading || state is! SpaceRoomsLoading + ? const DefaultNavBar() + : null, + ); + }, + ), + ), + ); + }, + ), + ), ); } } diff --git a/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart b/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart index 0d07911..b5b1ab3 100644 --- a/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart +++ b/lib/features/app_layout/view/widgets/app_bar_home_dropdown.dart @@ -28,10 +28,10 @@ class AppBarHomeDropdown extends StatelessWidget { underline: const SizedBox.shrink(), padding: const EdgeInsets.all(0), borderRadius: BorderRadius.circular(20), - value: SpacesCubit.get(context).selectedSpace, + value: SpacesCubit.get(context).selectedSpace!.id, items: SpacesCubit.spaces.map((space) { return DropdownMenuItem( - value: space, + value: space.id, child: SizedBox( width: 100, child: Row( @@ -50,7 +50,7 @@ class AppBarHomeDropdown extends StatelessWidget { const SizedBox(width: 5), Expanded( child: BodyMedium( - text: space.name ?? "", + text: space.name ?? "??", style: context.bodyMedium.copyWith( fontSize: 15, color: ColorsManager.textPrimaryColor, @@ -64,7 +64,10 @@ class AppBarHomeDropdown extends StatelessWidget { ); }).toList(), onChanged: (value) { - SpacesCubit.get(context).selectSpace(value!); + if (value != null) { + SpacesCubit.get(context).selectSpace(SpacesCubit.spaces + .firstWhere((element) => element.id == value)); + } }, ), ); diff --git a/lib/features/app_layout/view/widgets/app_body.dart b/lib/features/app_layout/view/widgets/app_body.dart index da10b5f..49cec88 100644 --- a/lib/features/app_layout/view/widgets/app_body.dart +++ b/lib/features/app_layout/view/widgets/app_body.dart @@ -15,39 +15,38 @@ class AppBody extends StatelessWidget { return BlocBuilder( builder: (context, state) { return Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, - ), - fit: BoxFit.cover, - opacity: 0.4, + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.imagesBackground, ), + fit: BoxFit.cover, + opacity: 0.4, ), - child: BlocConsumer( - listener: (context, state) { - if (state is SpacesError) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(state.errMessage), - ), - ); - } - }, - builder: (context, state) { - return state is! SpacesLoading - ? PageView( - physics: const NeverScrollableScrollPhysics(), - controller: NavCubit.of(context).pageController, - children: NavCubit.of(context).pages, - ) - : const Center(child: CircularProgressIndicator()); - }, - ) - // NavCubit.of(context).currentPage, - ); + ), + child: BlocConsumer( + listener: (context, state) { + if (state is SpacesError) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errMessage), + ), + ); + } + }, + builder: (context, state) { + return state is! SpacesLoading || state is! SpaceRoomsLoading + ? PageView( + physics: const NeverScrollableScrollPhysics(), + controller: NavCubit.pageController, + children: NavCubit.of(context).pages, + ) + : const Center(child: CircularProgressIndicator()); + }, + ), + ); }, ); } diff --git a/lib/features/app_layout/view/widgets/default_app_bar.dart b/lib/features/app_layout/view/widgets/default_app_bar.dart index e280bc1..1eb8beb 100644 --- a/lib/features/app_layout/view/widgets/default_app_bar.dart +++ b/lib/features/app_layout/view/widgets/default_app_bar.dart @@ -4,9 +4,7 @@ import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { - const DefaultAppBar(this.context, {super.key}); - - final BuildContext context; + const DefaultAppBar({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index e78775b..49a924d 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; import 'package:syncrow_app/features/auth/model/token.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart'; @@ -20,7 +21,7 @@ class AuthCubit extends Cubit { TextEditingController passwordController = TextEditingController(); bool isPasswordVisible = false; - GlobalKey formKey = GlobalKey(); + static GlobalKey formKey = GlobalKey(); void changePasswordVisibility() { isPasswordVisible = !isPasswordVisible; @@ -54,15 +55,16 @@ class AuthCubit extends Cubit { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.write( key: Token.loginAccessTokenKey, value: token.accessToken); - + const FlutterSecureStorage().write( + key: UserModel.userUuidKey, + value: Token.decodeToken(token.accessToken)['uuid'].toString()); user = UserModel.fromToken(token); emit(AuthSuccess()); } else { emit(AuthError('Something went wrong')); } } on DioException catch (e) { - emit(AuthError(e.message ?? "Something went wrong")); - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).errMessage)); } } @@ -71,9 +73,10 @@ class AuthCubit extends Cubit { try { FlutterSecureStorage storage = const FlutterSecureStorage(); await storage.delete(key: Token.loginAccessTokenKey); + NavCubit.clear(); emit(AuthLoggedOut()); } on DioException catch (e) { - throw ServerFailure.fromDioError(e); + emit(AuthError(ServerFailure.fromDioError(e).errMessage)); } } } diff --git a/lib/features/auth/model/token.dart b/lib/features/auth/model/token.dart index 5f3b22b..0c428a7 100644 --- a/lib/features/auth/model/token.dart +++ b/lib/features/auth/model/token.dart @@ -10,9 +10,26 @@ class Token { final String accessToken; final String refreshToken; + //{ + // "email": "test@test.com", + // "userId": 3, + // "uuid": "563e22d2-cb30-46d3-8c48-fa7d762342f0", + // "sessionId": "f76aa067-c915-4921-b04d-9fbc71c4965a", + // "iat": 1710137435, + // "exp": 1710137735 + // } + + final String sessionId; + + final int iat; + final int exp; + Token.emptyConstructor() : accessToken = '', - refreshToken = ''; + refreshToken = '', + sessionId = '', + iat = 0, + exp = 0; bool get accessTokenIsNotEmpty => accessToken.isNotEmpty; @@ -23,9 +40,16 @@ class Token { Token( this.accessToken, this.refreshToken, + this.sessionId, + this.iat, + this.exp, ); - Token.refreshToken(this.refreshToken) : accessToken = ''; + Token.refreshToken(this.refreshToken) + : accessToken = '', + sessionId = '', + iat = 0, + exp = 0; factory Token.fromJson(Map json) { //save token to secure storage @@ -34,15 +58,16 @@ class Token { key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? ''); //create token object ? - return Token( - json[loginAccessTokenKey] ?? '', json[loginRefreshTokenKey] ?? ''); + return Token(json[loginAccessTokenKey] ?? '', + json[loginRefreshTokenKey] ?? '', '', 0, 0); } - Map toJson() => {loginRefreshTokenKey: refreshToken}; + Map refreshTokenToJson() => + {loginRefreshTokenKey: refreshToken}; Map accessTokenToJson() => {loginAccessTokenKey: accessToken}; - Map decodeToken() { + static Map decodeToken(String accessToken) { final parts = accessToken.split('.'); if (parts.length != 3) { throw Exception('invalid access token'); diff --git a/lib/features/auth/model/user_model.dart b/lib/features/auth/model/user_model.dart index beafad5..5491d73 100644 --- a/lib/features/auth/model/user_model.dart +++ b/lib/features/auth/model/user_model.dart @@ -1,6 +1,7 @@ import 'package:syncrow_app/features/auth/model/token.dart'; class UserModel { + static String userUuidKey = 'userUuid'; final String? uuid; final String? email; final String? name; @@ -36,13 +37,9 @@ class UserModel { //uuid to json - Map uuIdAsJson() => { - 'userUuid': uuid, - }; - //from token factory UserModel.fromToken(Token token) { - Map tempJson = token.decodeToken(); + Map tempJson = Token.decodeToken(token.accessToken); return UserModel( uuid: tempJson['uuid'].toString(), diff --git a/lib/features/auth/view/widgets/login/login_button.dart b/lib/features/auth/view/widgets/login/login_button.dart deleted file mode 100644 index 3e2f7e5..0000000 --- a/lib/features/auth/view/widgets/login/login_button.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; -import 'package:syncrow_app/features/shared_widgets/default_button.dart'; - -class LoginButton extends StatelessWidget { - const LoginButton({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: DefaultButton( - isDone: state is AuthSuccess, - isLoading: state is AuthLoading, - customButtonStyle: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - Colors.black.withOpacity(.25), - ), - foregroundColor: MaterialStateProperty.all( - Colors.white, - ), - ), - child: const Text( - 'Login', - ), - onPressed: () { - if (AuthCubit.get(context).formKey.currentState!.validate()) { - AuthCubit.get(context).login(); - FocusScope.of(context).unfocus(); - } - }, - ), - ), - ], - ); - }, - ); - } -} diff --git a/lib/features/auth/view/widgets/login/login_form.dart b/lib/features/auth/view/widgets/login/login_form.dart index 29984bc..3447f3e 100644 --- a/lib/features/auth/view/widgets/login/login_form.dart +++ b/lib/features/auth/view/widgets/login/login_form.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/auth/view/widgets/login/forget_password.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/utils/resource_manager/styles_manager.dart'; @@ -11,10 +13,11 @@ class LoginForm extends StatelessWidget { @override Widget build(BuildContext context) { + var formKey = GlobalKey(); return BlocBuilder( builder: (context, state) { return Form( - key: AuthCubit.get(context).formKey, + key: formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -40,7 +43,7 @@ class LoginForm extends StatelessWidget { return null; }, onTapOutside: (event) { - AuthCubit.get(context).formKey.currentState!.validate(); + formKey.currentState!.validate(); }, decoration: defaultInputDecoration(context, hint: "Example@email.com"), @@ -65,12 +68,44 @@ class LoginForm extends StatelessWidget { return null; }, onTapOutside: (event) { - AuthCubit.get(context).formKey.currentState!.validate(); + formKey.currentState!.validate(); }, obscureText: !AuthCubit.get(context).isPasswordVisible, decoration: defaultInputDecoration(context, hint: "At least 8 characters"), ), + const SizedBox(height: 10), + // const LoginUserAgreement(), + const ForgetPassword(), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: DefaultButton( + isDone: state is AuthSuccess, + isLoading: state is AuthLoading, + customButtonStyle: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Colors.black.withOpacity(.25), + ), + foregroundColor: MaterialStateProperty.all( + Colors.white, + ), + ), + child: const Text( + 'Login', + ), + onPressed: () { + if (formKey.currentState!.validate()) { + AuthCubit.get(context).login(); + FocusScope.of(context).unfocus(); + } + }, + ), + ), + ], + ) ], ), ), diff --git a/lib/features/auth/view/widgets/login/login_view.dart b/lib/features/auth/view/widgets/login/login_view.dart index 3520e39..34c00ab 100644 --- a/lib/features/auth/view/widgets/login/login_view.dart +++ b/lib/features/auth/view/widgets/login/login_view.dart @@ -3,8 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/dont_have_an_account.dart'; -import 'package:syncrow_app/features/auth/view/widgets/login/forget_password.dart'; -import 'package:syncrow_app/features/auth/view/widgets/login/login_button.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_divider.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_form.dart'; import 'package:syncrow_app/features/auth/view/widgets/login/login_with_google_facebook.dart'; @@ -94,11 +92,6 @@ class LoginView extends StatelessWidget { height: 20, ), const LoginForm(), - const SizedBox(height: 10), - // const LoginUserAgreement(), - const ForgetPassword(), - const SizedBox(height: 10), - const LoginButton(), const LoginDivider(), const LoginWithGoogleFacebook(), const DontHaveAnAccount(), diff --git a/lib/features/devices/model/room_model.dart b/lib/features/devices/model/room_model.dart index 921eb36..3937f17 100644 --- a/lib/features/devices/model/room_model.dart +++ b/lib/features/devices/model/room_model.dart @@ -1,10 +1,10 @@ import 'package:syncrow_app/features/devices/model/device_category_model.dart'; class RoomModel { - final String id; - final String name; + final int? id; + final String? name; - final List categories; + final List? categories; RoomModel({ required this.id, @@ -22,8 +22,8 @@ class RoomModel { factory RoomModel.fromJson(Map json) { return RoomModel( - id: json['id'], - name: json['name'], + id: json['roomId'], + name: json['roomName'], categories: json['devices'], ); } diff --git a/lib/features/devices/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 8b39984..4db3d30 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -19,12 +19,17 @@ class DevicesViewBody extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => DevicesCubit(), - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { - //TODO : move to NavigationCubit - return state is DevicesLoading - ? const Center(child: CircularProgressIndicator()) - : Padding( + return BlocBuilder( + builder: (context, state) { + print( + "length : ${SpacesCubit.get(context).selectedSpace!.rooms!.length}"); + //TODO : move to NavigationCubit + if (state is DevicesLoading) { + return const Center(child: CircularProgressIndicator()); + } else { + return Padding( padding: EdgeInsets.only( top: Constants.appBarHeight, bottom: Constants.bottomNavBarHeight, @@ -46,16 +51,21 @@ class DevicesViewBody extends StatelessWidget { }, children: [ const WizardPage(), - ...SpacesCubit.get(context) - .selectedSpace! - .rooms! - .map( - (room) { - return RoomPage( - room: room, - ); - }, - ) + if (SpacesCubit.get(context).selectedSpace != null) + if (SpacesCubit.get(context) + .selectedSpace! + .rooms != + null) + ...SpacesCubit.get(context) + .selectedSpace! + .rooms! + .map( + (room) { + return RoomPage( + room: room, + ); + }, + ) ], ), ), @@ -66,7 +76,11 @@ class DevicesViewBody extends StatelessWidget { child: SmoothPageIndicator( controller: SpacesCubit.get(context).devicesPageController, - count: 3, + count: SpacesCubit.get(context) + .selectedSpace! + .rooms! + .length + + 1, effect: const WormEffect( paintStyle: PaintingStyle.stroke, dotHeight: 8, @@ -77,6 +91,9 @@ class DevicesViewBody extends StatelessWidget { ], ), ); + } + }, + ); }, ), ); diff --git a/lib/features/devices/view/widgets/room_page.dart b/lib/features/devices/view/widgets/room_page.dart index 4c0455a..7f97b81 100644 --- a/lib/features/devices/view/widgets/room_page.dart +++ b/lib/features/devices/view/widgets/room_page.dart @@ -14,7 +14,7 @@ class RoomPage extends StatelessWidget { @override Widget build(BuildContext context) { List devices = []; - for (var category in room.categories) { + for (var category in room.categories ?? []) { devices.addAll(category.devices); } return Padding( diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index b7de8bd..c046d78 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -40,30 +40,33 @@ class RoomsSlider extends StatelessWidget { ), ), ), - ...SpacesCubit.get(context).selectedSpace!.rooms!.map( - (room) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: InkWell( - onTap: () { - SpacesCubit.get(context).roomSliderPageChanged( - SpacesCubit.get(context) - .selectedSpace! - .rooms! - .indexOf(room)); - }, - child: TitleMedium( - text: room.name, - style: context.titleMedium.copyWith( - fontSize: 25, - color: SpacesCubit.get(context).selectedRoom == room - ? ColorsManager.textPrimaryColor - : ColorsManager.textPrimaryColor - .withOpacity(.2), + if (SpacesCubit.get(context).selectedSpace != null) + if (SpacesCubit.get(context).selectedSpace!.rooms != null) + ...SpacesCubit.get(context).selectedSpace!.rooms!.map( + (room) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: InkWell( + onTap: () { + SpacesCubit.get(context).roomSliderPageChanged( + SpacesCubit.get(context) + .selectedSpace! + .rooms! + .indexOf(room)); + }, + child: TitleMedium( + text: room.name!, + style: context.titleMedium.copyWith( + fontSize: 25, + color: SpacesCubit.get(context).selectedRoom == + room + ? ColorsManager.textPrimaryColor + : ColorsManager.textPrimaryColor + .withOpacity(.2), + ), + ), ), ), - ), - ), - ) + ) ], ), ); diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index 5186421..0c65933 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -10,50 +10,77 @@ class SplashView extends StatelessWidget { @override Widget build(BuildContext context) { - //TODO remove this delay - Future.value().then((value) async { - var isLoggedIn = await const FlutterSecureStorage() - .read(key: Token.loginAccessTokenKey) != - null; - if (isLoggedIn) { - Navigator.pushReplacementNamed(context, Routes.homeRoute); - } else { - Navigator.pushReplacementNamed(context, Routes.authLogin); - } - }); - return Scaffold( - body: Stack( - alignment: Alignment.center, - children: [ - Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage( - Assets.imagesBackground, + return FutureBuilder( + future: getTokenAndValidate(context), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data ?? false) { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacementNamed(context, Routes.homeRoute); + }); + } else { + Future.delayed(const Duration(seconds: 1), () { + Navigator.pushReplacementNamed(context, Routes.authLogin); + }); + } + } + return Scaffold( + body: Stack( + alignment: Alignment.center, + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.imagesBackground, + ), + fit: BoxFit.cover, + ), ), - fit: BoxFit.cover, ), - ), - ), - Container( - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height, - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage(Assets.imagesVector), - fit: BoxFit.cover, - opacity: 0.9, + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(Assets.imagesVector), + fit: BoxFit.cover, + opacity: 0.9, + ), + ), ), - ), + SvgPicture.asset( + Assets.imagesLogo, + width: 240, + ) + ], ), - SvgPicture.asset( - Assets.imagesLogo, - width: 240, - ) - ], - ), + ); + }, ); } + + Future getTokenAndValidate(BuildContext context) async { + return await const FlutterSecureStorage() + .read(key: Token.loginAccessTokenKey) + .then((value) { + if (value == null) { + return false; + } + print("Decoding token Started"); + var tokenData = Token.decodeToken(value ?? ""); + print("checking token data"); + + if (tokenData.containsKey('exp')) { + var exp = tokenData['exp'] ?? 0; + var currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; + print('time: $currentTime exp: $exp'); + return currentTime < exp; + } else { + return false; + } + }); + } } diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index b3da647..72246f3 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -16,6 +16,12 @@ class Assets { static const String iconsDevices = 'assets/icons/Devices.svg'; static const String iconsDevicesFill = 'assets/icons/Devices-fill.svg'; static const String iconsDoorLock = 'assets/icons/doorLock.svg'; + static const String iconsDoorLockLinkage = 'assets/icons/DoorLockLinkage.svg'; + static const String iconsDoorLockLock = 'assets/icons/DoorLockLock.svg'; + static const String iconsDoorLockMembers = 'assets/icons/DoorLockMembers.svg'; + static const String iconsDoorLockPassword = + 'assets/icons/DoorLockPassword.svg'; + static const String iconsDoorLockRecords = 'assets/icons/DoorLockRecords.svg'; static const String iconsFacebook = 'assets/icons/Facebook.svg'; static const String iconsFan0 = 'assets/icons/fan-0.svg'; static const String iconsFan1 = 'assets/icons/fan-1.svg'; diff --git a/lib/my_app.dart b/lib/my_app.dart index b92aa33..154b9e6 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/app_layout/bloc/nav_cubit.dart'; -import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.dart'; import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:syncrow_app/utils/resource_manager/constants.dart'; @@ -30,9 +29,6 @@ class MyApp extends StatelessWidget { BlocProvider( create: (context) => NavCubit(), ), - BlocProvider( - create: (context) => SpacesCubit(), - ), BlocProvider( create: (context) => DevicesCubit(), ), diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 7259ebb..4233712 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -12,4 +12,5 @@ abstract class ApiEndpoints { // Spaces static const String spaces = '$baseUrl/home'; + static const String rooms = '$baseUrl/room'; } diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index 06d062b..70f502c 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; import 'package:syncrow_app/features/auth/model/token.dart'; import 'package:syncrow_app/features/auth/model/verify_code.dart'; @@ -11,21 +10,22 @@ class AuthenticationAPI { path: ApiEndpoints.verifyOtp, body: data.toJson(), showServerMessage: false, - expectedResponseModel: (json) { - Token token = Token.fromJson(json); - return token; - }); + expectedResponseModel: (json) => Token.fromJson(json)); return response; } static Future loginWithEmail( {required LoginWithEmailModel model}) async { - final response = await HTTPService().post( - path: ApiEndpoints.login, - body: model.toJson(), - showServerMessage: false, - expectedResponseModel: (json) => Token.fromJson(json['data'])); - debugPrint("response: $response"); - return response; + try { + final response = await HTTPService().post( + path: ApiEndpoints.login, + body: model.toJson(), + showServerMessage: false, + expectedResponseModel: (json) => Token.fromJson(json['data'])); + // debugPrint("response: $response"); + return response; + } catch (e) { + rethrow; + } } } diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index cb018c7..fa22beb 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:syncrow_app/features/auth/model/token.dart'; class HTTPInterceptor extends InterceptorsWrapper { // @override @@ -11,9 +12,9 @@ class HTTPInterceptor extends InterceptorsWrapper { @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { - //pass the token from the flutter secure storage to the request header - - options.headers['Authorization'] = 'Bearer ${AuthCubit.token.accessToken}'; + var storage = FlutterSecureStorage(); + var token = await storage.read(key: Token.loginAccessTokenKey); + options.headers['Authorization'] = 'Bearer $token'; super.onRequest(options, handler); } diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index ecaeae7..531d4ba 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -17,8 +17,8 @@ class HTTPService { baseUrl: ApiEndpoints.baseUrl, receiveDataWhenStatusError: true, followRedirects: false, - connectTimeout: const Duration(seconds: 5), - receiveTimeout: const Duration(seconds: 5), + connectTimeout: const Duration(seconds: 30), + receiveTimeout: const Duration(seconds: 30), ), ); @@ -62,8 +62,8 @@ class HTTPService { queryParameters: queryParameters, options: options, ); - debugPrint("status code is ${response.statusCode}"); - debugPrint("response data is ${response.data}"); + // debugPrint("status code is ${response.statusCode}"); + // debugPrint("response data is ${response.data}"); return expectedResponseModel(response.data); } catch (error) { rethrow; diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index dad5df9..060ca78 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -1,26 +1,19 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; -import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/auth/model/user_model.dart'; +import 'package:syncrow_app/features/devices/model/room_model.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; class SpacesAPI { - // static Future loginWithEmail( - // {required LoginWithEmailModel model}) async { - // final response = await HTTPService().post( - // path: ApiEndpoints.login, - // body: model.toJson(), - // showServerMessage: false, - // expectedResponseModel: (json) { - // Token token = Token.fromJson(json['data']); - // return token; - // }); - // debugPrint("response: $response"); - // return response; - // } - static Future> getSpaces() async { + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); final response = await HTTPService().get( - path: "${ApiEndpoints.spaces}/${AuthCubit.user!.uuid}", + path: ApiEndpoints.spaces, + queryParameters: { + "userUuid": uuid, + }, showServerMessage: false, expectedResponseModel: (json) { List spaces = []; @@ -32,4 +25,21 @@ class SpacesAPI { ); return response; } + + //get rooms by space id + static Future> getRooms(int spaceId) async { + final response = await HTTPService().get( + path: ApiEndpoints.rooms, + queryParameters: {"homeId": spaceId}, + showServerMessage: false, + expectedResponseModel: (json) { + List rooms = []; + for (var room in json) { + rooms.add(RoomModel.fromJson(room)); + } + return rooms; + }, + ); + return response; + } } From 00d1ad50f1f095c5bdfd4e71afa281af86b9573f Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Thu, 14 Mar 2024 00:38:00 +0300 Subject: [PATCH 03/15] built 3 gang switch View --- lib/features/devices/bloc/devices_cubit.dart | 43 ++-- lib/features/devices/model/ac_model.dart | 9 + lib/features/devices/model/curtain_model.dart | 9 + .../devices/model/device_control_model.dart | 27 +++ lib/features/devices/model/device_model.dart | 9 +- .../devices/model/function_model.dart | 42 ++++ lib/features/devices/model/light_model.dart | 9 + lib/features/devices/view/devices_view.dart | 4 +- .../lights_switches/light_switche.dart | 54 +++++ .../lights_switches/light_switches.dart | 86 ++++++++ .../lights_switches/light_switches_body.dart | 202 ++++++++++++++++++ .../view/widgets/screens/screens_view.dart | 10 - lib/generated/assets.dart | 5 + lib/navigation/router.dart | 2 +- lib/utils/resource_manager/color_manager.dart | 1 + lib/utils/resource_manager/constants.dart | 2 +- 16 files changed, 481 insertions(+), 33 deletions(-) create mode 100644 lib/features/devices/model/device_control_model.dart create mode 100644 lib/features/devices/model/function_model.dart create mode 100644 lib/features/devices/view/widgets/lights_switches/light_switche.dart create mode 100644 lib/features/devices/view/widgets/lights_switches/light_switches.dart create mode 100644 lib/features/devices/view/widgets/lights_switches/light_switches_body.dart delete mode 100644 lib/features/devices/view/widgets/screens/screens_view.dart diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 8b706a6..47e22fa 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -9,7 +9,7 @@ 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/utils/resource_manager/constants.dart'; @@ -26,7 +26,8 @@ class DevicesCubit extends Cubit { devices: [ ACModel( name: "Living Room AC", - id: '0', + id: 0, + functions: [], status: false, temperature: 20, fanSpeed: 0, @@ -42,7 +43,8 @@ class DevicesCubit extends Cubit { ), ACModel( name: "Master Bedroom AC", - id: '1', + id: 1, + functions: [], status: false, temperature: 20, fanSpeed: 0, @@ -58,7 +60,8 @@ class DevicesCubit extends Cubit { ), ACModel( name: "Kitchen AC", - id: '2', + id: 2, + functions: [], status: false, temperature: 20, fanSpeed: 0, @@ -74,7 +77,8 @@ class DevicesCubit extends Cubit { ), ACModel( name: "Bathroom AC", - id: '3', + id: 3, + functions: [], status: false, temperature: 20, fanSpeed: 0, @@ -98,7 +102,8 @@ class DevicesCubit extends Cubit { devices: [ LightModel( name: "Living Room Light", - id: '0', + id: 0, + functions: [], status: false, color: 0, brightness: 20, @@ -115,7 +120,8 @@ class DevicesCubit extends Cubit { ), LightModel( name: "Master Bedroom Light", - id: '1', + id: 1, + functions: [], status: false, color: 2, brightness: 40, @@ -132,7 +138,8 @@ class DevicesCubit extends Cubit { ), LightModel( name: "Kitchen Light", - id: '2', + id: 2, + functions: [], status: false, color: 1, brightness: 60, @@ -149,7 +156,8 @@ class DevicesCubit extends Cubit { ), LightModel( name: "Bathroom Light", - id: '3', + id: 3, + functions: [], status: false, color: 3, brightness: 80, @@ -166,7 +174,8 @@ class DevicesCubit extends Cubit { ), LightModel( name: "Balcony Light", - id: '4', + id: 4, + functions: [], status: false, color: 4, brightness: 100, @@ -198,7 +207,8 @@ class DevicesCubit extends Cubit { devices: [ CurtainModel( openPercentage: 10, - id: "1", + id: 1, + functions: [], name: "Living Room Curtain", status: false, type: DeviceType.Curtain, @@ -207,7 +217,8 @@ class DevicesCubit extends Cubit { ), CurtainModel( openPercentage: 20, - id: "2", + id: 2, + functions: [], name: "Master Bedroom Curtain", status: false, type: DeviceType.Curtain, @@ -222,10 +233,10 @@ class DevicesCubit extends Cubit { ), DevicesCategoryModel( devices: [], - icon: Assets.iconsScreen, - name: 'Screens', - type: DeviceType.Screens, - page: const ScreensView(), + icon: Assets.icons3GangSwitch, + name: 'Bedroom Lights', + type: DeviceType.ThreeGang, + page: const LightSwitchesView(), ), DevicesCategoryModel( devices: [], diff --git a/lib/features/devices/model/ac_model.dart b/lib/features/devices/model/ac_model.dart index 737118d..3476d59 100644 --- a/lib/features/devices/model/ac_model.dart +++ b/lib/features/devices/model/ac_model.dart @@ -1,4 +1,5 @@ import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/function_model.dart'; class ACModel extends DeviceModel { late double temperature; @@ -23,6 +24,7 @@ class ACModel extends DeviceModel { required super.status, required super.image, required super.timer, + required super.functions, }); Map toJson() { @@ -40,6 +42,12 @@ class ACModel extends DeviceModel { } factory ACModel.fromJson(Map json) { + List func = []; + if (json['functions'] != null) { + json['functions'].forEach((v) { + func.add(FunctionModel.fromJson(v)); + }); + } return ACModel( id: json['id'], name: json['name'], @@ -52,6 +60,7 @@ class ACModel extends DeviceModel { timer: json['timer'], coolTo: json['coolTo'], bounds: Bounds.fromJson(json['bounds']), + functions: func, ); } } diff --git a/lib/features/devices/model/curtain_model.dart b/lib/features/devices/model/curtain_model.dart index 6abc311..e693871 100644 --- a/lib/features/devices/model/curtain_model.dart +++ b/lib/features/devices/model/curtain_model.dart @@ -1,4 +1,5 @@ import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/function_model.dart'; class CurtainModel extends DeviceModel { late int openPercentage; @@ -11,6 +12,7 @@ class CurtainModel extends DeviceModel { required super.status, required super.image, required super.timer, + required super.functions, }); Map toJson() { @@ -26,6 +28,12 @@ class CurtainModel extends DeviceModel { } factory CurtainModel.fromJson(Map json) { + List func = []; + if (json['functions'] != null) { + json['functions'].forEach((v) { + func.add(FunctionModel.fromJson(v)); + }); + } return CurtainModel( id: json['id'], name: json['name'], @@ -34,6 +42,7 @@ class CurtainModel extends DeviceModel { timer: json['timer'], type: json['type'], image: json['image'], + functions: func, ); } } 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..78fb291 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: 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 index ab2f258..7de5ffa 100644 --- a/lib/features/devices/model/light_model.dart +++ b/lib/features/devices/model/light_model.dart @@ -1,4 +1,5 @@ import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/function_model.dart'; class LightModel extends DeviceModel { late double brightness; @@ -19,6 +20,7 @@ class LightModel extends DeviceModel { required super.status, required super.image, required super.timer, + required super.functions, }); Map toJson() { @@ -37,6 +39,12 @@ class LightModel extends DeviceModel { } factory LightModel.fromJson(Map json) { + List func = []; + if (json['functions'] != null) { + json['functions'].forEach((v) { + func.add(FunctionModel.fromJson(v)); + }); + } return LightModel( id: json['id'], name: json['name'], @@ -48,6 +56,7 @@ class LightModel extends DeviceModel { type: json['type'], image: json['image'], recentColors: json['recentColors'], + functions: func, ); } } 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/lights_switches/light_switche.dart b/lib/features/devices/view/widgets/lights_switches/light_switche.dart new file mode 100644 index 0000000..cabb568 --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switche.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class LightSwitch extends StatefulWidget { + const LightSwitch({ + super.key, + }); + + @override + State createState() => _LightSwitchState(); +} + +class _LightSwitchState extends State { + bool isOn = false; + + @override + Widget build(BuildContext context) { + return InkWell( + overlayColor: MaterialStateProperty.all(Colors.transparent), + onTap: () { + setState(() { + isOn = !isOn; + }); + }, + child: Stack( + alignment: isOn ? Alignment.topCenter : Alignment.bottomCenter, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(100.0)), + color: isOn + ? ColorsManager.primaryColorWithOpacity + : ColorsManager.switchOffColor, + ), + width: 60, + height: 115, + ), + Padding( + padding: const EdgeInsets.all(5.0), + child: SizedBox.square( + dimension: 60, + child: SvgPicture.asset( + isOn ? 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..2c915b2 --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/view/widgets/lights_switches/light_switche.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()), + const Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + LightSwitch(), + LightSwitch(), + LightSwitch(), + ], + ), + 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/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/generated/assets.dart b/lib/generated/assets.dart index 72246f3..90d49a2 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'; @@ -37,6 +38,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'; @@ -52,6 +55,8 @@ class Assets { 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/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/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 8a67e03..4156ab1 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -19,6 +19,6 @@ enum DeviceType { Lights, Door, Curtain, - Screens, + ThreeGang, Gateway, } From 1c25f65028412d38d28c99cf315dc21e9f8eb531 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Thu, 14 Mar 2024 00:42:38 +0300 Subject: [PATCH 04/15] 3gang assets --- assets/icons/3GangSwitch.svg | 19 +++++++++++++++++ assets/icons/Vector-1.svg | 29 +++++++++++++++++++++++++ assets/icons/Vector.svg | 17 +++++++++++++++ assets/icons/lightSwitchOff.svg | 25 ++++++++++++++++++++++ assets/icons/lightSwitchOn.svg | 38 +++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+) create mode 100644 assets/icons/3GangSwitch.svg create mode 100644 assets/icons/Vector-1.svg create mode 100644 assets/icons/Vector.svg create mode 100644 assets/icons/lightSwitchOff.svg create mode 100644 assets/icons/lightSwitchOn.svg 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/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/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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c5f1b8d0902ae635cb23446e063f3c59c62cfa3c Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Thu, 14 Mar 2024 01:14:54 +0300 Subject: [PATCH 05/15] switch functionality initial implantation --- lib/features/devices/bloc/devices_cubit.dart | 27 +++++++ lib/features/devices/bloc/devices_state.dart | 10 +++ .../widgets/lights_switches/light_switch.dart | 70 +++++++++++++++++++ .../lights_switches/light_switche.dart | 54 -------------- .../lights_switches/light_switches_body.dart | 29 ++++++-- lib/services/api/api_links_endpoints.dart | 3 + lib/services/api/devices_api.dart | 18 +++++ 7 files changed, 152 insertions(+), 59 deletions(-) create mode 100644 lib/features/devices/view/widgets/lights_switches/light_switch.dart delete mode 100644 lib/features/devices/view/widgets/lights_switches/light_switche.dart create mode 100644 lib/services/api/devices_api.dart diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 47e22fa..5d1fb04 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,8 +1,10 @@ +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/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'; @@ -12,6 +14,8 @@ import 'package:syncrow_app/features/devices/view/widgets/lights/lights_view.dar 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'; @@ -395,4 +399,27 @@ class DevicesCubit extends Cubit { emit(DevicesCategoryChanged()); } + + //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)); + // } + // } + deviceControl(DeviceControlModel control) async { + emit(DeviceControlLoading()); + try { + var response = await DevicesAPI.controlDevice(control); + emit(DeviceControlSuccess()); + } on DioException catch (e) { + emit(DeviceControlError(ServerFailure.fromDioError(e).errMessage)); + } + } } diff --git a/lib/features/devices/bloc/devices_state.dart b/lib/features/devices/bloc/devices_state.dart index 4a6ba5d..d913d09 100644 --- a/lib/features/devices/bloc/devices_state.dart +++ b/lib/features/devices/bloc/devices_state.dart @@ -20,3 +20,13 @@ class CategorySwitchChanged extends DevicesState {} class DeviceSwitchChanged extends DevicesState {} class DeviceSelected extends DevicesState {} + +class DeviceControlLoading extends DevicesState {} + +class DeviceControlSuccess extends DevicesState {} + +class DeviceControlError extends DevicesState { + final String errorMsg; + + DeviceControlError(this.errorMsg); +} 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..8963f57 --- /dev/null +++ b/lib/features/devices/view/widgets/lights_switches/light_switch.dart @@ -0,0 +1,70 @@ +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) { + 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_switche.dart b/lib/features/devices/view/widgets/lights_switches/light_switche.dart deleted file mode 100644 index cabb568..0000000 --- a/lib/features/devices/view/widgets/lights_switches/light_switche.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_app/generated/assets.dart'; -import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; - -class LightSwitch extends StatefulWidget { - const LightSwitch({ - super.key, - }); - - @override - State createState() => _LightSwitchState(); -} - -class _LightSwitchState extends State { - bool isOn = false; - - @override - Widget build(BuildContext context) { - return InkWell( - overlayColor: MaterialStateProperty.all(Colors.transparent), - onTap: () { - setState(() { - isOn = !isOn; - }); - }, - child: Stack( - alignment: isOn ? Alignment.topCenter : Alignment.bottomCenter, - children: [ - Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(100.0)), - color: isOn - ? ColorsManager.primaryColorWithOpacity - : ColorsManager.switchOffColor, - ), - width: 60, - height: 115, - ), - Padding( - padding: const EdgeInsets.all(5.0), - child: SizedBox.square( - dimension: 60, - child: SvgPicture.asset( - isOn ? Assets.iconsLightSwitchOn : Assets.iconsLightSwitchOff, - fit: BoxFit.fill, - ), - ), - ), - ], - ), - ); - } -} 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 index 2c915b2..f5a20a3 100644 --- a/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart +++ b/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart @@ -1,6 +1,7 @@ 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_switche.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'; @@ -20,13 +21,31 @@ class LightSwitchesBody extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Expanded(child: SizedBox.shrink()), - const Row( + Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ - LightSwitch(), - LightSwitch(), - LightSwitch(), + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_1', + value: true, + ), + ), + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_2', + value: true, + ), + ), + LightSwitch( + control: DeviceControlModel( + deviceId: 'bfe10693d4fd263206ocq9', + code: 'switch_3', + value: true, + ), + ), ], ), Expanded( diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 4233712..6e21740 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -13,4 +13,7 @@ abstract class ApiEndpoints { // Spaces static const String spaces = '$baseUrl/home'; static const String rooms = '$baseUrl/room'; + + // Devices + static const String control = '$baseUrl/device/control'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart new file mode 100644 index 0000000..2e74df5 --- /dev/null +++ b/lib/services/api/devices_api.dart @@ -0,0 +1,18 @@ +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 Future> controlDevice( + DeviceControlModel controlModel) async { + final response = await HTTPService().post( + path: ApiEndpoints.control, + body: controlModel.toJson(), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } +} From 8ac164f0c5f782f7f2099fbd36daf80ccaf7696b Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Fri, 15 Mar 2024 22:23:34 +0300 Subject: [PATCH 06/15] fixed AppBar null check --- .../view/widgets/default_app_bar.dart | 33 +++++++++++-------- lib/my_app.dart | 1 + 2 files changed, 21 insertions(+), 13 deletions(-) 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 1eb8beb..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,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 'package:syncrow_app/utils/resource_manager/constants.dart'; class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -10,19 +11,25 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget { 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/my_app.dart b/lib/my_app.dart index 154b9e6..8e88cdb 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -27,6 +27,7 @@ class MyApp extends StatelessWidget { create: (context) => AuthCubit(), ), BlocProvider( + lazy: false, create: (context) => NavCubit(), ), BlocProvider( From 65cbf104851f22a9c62c9706fea47422d15bbbca Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Sun, 17 Mar 2024 09:38:26 +0300 Subject: [PATCH 07/15] temp --- lib/features/devices/bloc/ac/ac_cubit.dart | 56 -- lib/features/devices/bloc/ac/ac_state.dart | 30 - .../devices/bloc/curtains/curtains_cubit.dart | 13 - .../devices/bloc/curtains/curtains_state.dart | 5 - lib/features/devices/bloc/devices_cubit.dart | 683 +++++++++++------- lib/features/devices/bloc/devices_state.dart | 10 + .../devices/bloc/lights/lights_cubit.dart | 62 -- .../devices/bloc/lights/lights_state.dart | 39 - lib/features/devices/model/ac_model.dart | 90 --- lib/features/devices/model/curtain_model.dart | 48 -- .../devices/model/device_category_model.dart | 47 +- lib/features/devices/model/device_model.dart | 2 +- lib/features/devices/model/light_model.dart | 62 -- .../view/widgets/ACs/ac_interface.dart | 12 +- .../widgets/ACs/ac_interface_controls.dart | 8 +- .../widgets/ACs/ac_interface_temp_unit.dart | 40 +- .../widgets/ACs/ac_mode_control_unit.dart | 24 +- .../view/widgets/ACs/ac_temp_widget.dart | 22 +- .../devices/view/widgets/ACs/acs_list.dart | 33 +- .../devices/view/widgets/ACs/acs_view.dart | 18 +- .../widgets/ACs/category_view_app_bar.dart | 2 +- .../view/widgets/ACs/universal_ac_temp.dart | 15 +- .../view/widgets/curtains/curtain_list.dart | 26 +- .../view/widgets/curtains/curtain_view.dart | 67 +- .../view/widgets/lights/light_brightness.dart | 41 +- .../view/widgets/lights/light_interface.dart | 8 +- .../lights/light_interface_contols.dart | 4 +- .../widgets/lights/light_interface_modes.dart | 12 +- .../lights/light_interface_recent_color.dart | 18 +- .../lights/light_interface_slider.dart | 16 +- .../lights/light_interface_switch.dart | 7 +- .../view/widgets/lights/lights_list.dart | 4 +- .../view/widgets/lights/lights_view.dart | 31 +- .../view/widgets/lights/lights_view_list.dart | 4 +- .../view/widgets/room_page_switch.dart | 3 +- .../devices/view/widgets/wizard_switches.dart | 4 +- lib/services/api/devices_api.dart | 17 + lib/utils/resource_manager/constants.dart | 4 +- 38 files changed, 686 insertions(+), 901 deletions(-) delete mode 100644 lib/features/devices/bloc/ac/ac_cubit.dart delete mode 100644 lib/features/devices/bloc/ac/ac_state.dart delete mode 100644 lib/features/devices/bloc/curtains/curtains_cubit.dart delete mode 100644 lib/features/devices/bloc/curtains/curtains_state.dart delete mode 100644 lib/features/devices/bloc/lights/lights_cubit.dart delete mode 100644 lib/features/devices/bloc/lights/lights_state.dart delete mode 100644 lib/features/devices/model/ac_model.dart delete mode 100644 lib/features/devices/model/curtain_model.dart delete mode 100644 lib/features/devices/model/light_model.dart 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 5d1fb04..97be459 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,19 +1,16 @@ 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/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/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'; @@ -26,229 +23,229 @@ class DevicesCubit extends Cubit { static DevicesCubit get(context) => BlocProvider.of(context); static List allCategories = [ - DevicesCategoryModel( - devices: [ - ACModel( - name: "Living Room AC", - id: 0, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - 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, - functions: [], - name: "Living Room Curtain", - status: false, - type: DeviceType.Curtain, - image: '', - timer: null, - ), - CurtainModel( - openPercentage: 20, - id: 2, - functions: [], - 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.icons3GangSwitch, - name: 'Bedroom Lights', - type: DeviceType.ThreeGang, - page: const LightSwitchesView(), - ), - DevicesCategoryModel( - devices: [], - icon: Assets.iconsGateway, - name: 'Gateway', - type: DeviceType.Gateway, - page: const GateWayView(), - ), + // DevicesCategoryModel( + // devices: [ + // DeviceModel( + // name: "Living Room AC", + // id: 0, + // functions: [], + // status: false, + // temperature: 20, + // fanSpeed: 0, + // tempMode: 0, + // coolTo: 20, + // type: DeviceType.AC, + // image: '', + // timer: null, + // bounds: Bounds( + // min: 20, + // max: 30, + // ), + // ), + // DeviceModel( + // name: "Master Bedroom AC", + // id: 1, + // functions: [], + // status: false, + // temperature: 20, + // fanSpeed: 0, + // tempMode: 0, + // coolTo: 20, + // type: DeviceType.AC, + // image: '', + // timer: null, + // bounds: Bounds( + // min: 20, + // max: 30, + // ), + // ), + // DeviceModel( + // name: "Kitchen AC", + // id: 2, + // functions: [], + // status: false, + // temperature: 20, + // fanSpeed: 0, + // tempMode: 0, + // coolTo: 20, + // type: DeviceType.AC, + // image: '', + // timer: null, + // bounds: Bounds( + // min: 20, + // max: 30, + // ), + // ), + // DeviceModel( + // name: "Bathroom AC", + // id: 3, + // functions: [], + // 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: [ + // DeviceModel( + // name: "Living Room Light", + // id: 0, + // functions: [], + // status: false, + // color: 0, + // brightness: 20, + // lightingMode: 1, + // timer: null, + // type: DeviceType.Lights, + // image: '', + // recentColors: [ + // 0xFF83D9FF, + // 0xFFFC3E81, + // 0xFFC0FF66, + // 0xFFFDC242, + // ], + // ), + // DeviceModel( + // name: "Master Bedroom Light", + // id: 1, + // functions: [], + // status: false, + // color: 2, + // brightness: 40, + // lightingMode: 1, + // timer: null, + // type: DeviceType.Lights, + // image: '', + // recentColors: [ + // 0xFF83D9FF, + // 0xFFFC3E81, + // 0xFFC0FF66, + // 0xFFFDC242, + // ], + // ), + // DeviceModel( + // name: "Kitchen Light", + // id: 2, + // functions: [], + // status: false, + // color: 1, + // brightness: 60, + // lightingMode: 1, + // timer: null, + // type: DeviceType.Lights, + // image: '', + // recentColors: [ + // 0xFF83D9FF, + // 0xFFFC3E81, + // 0xFFC0FF66, + // 0xFFFDC242, + // ], + // ), + // DeviceModel( + // name: "Bathroom Light", + // id: 3, + // functions: [], + // status: false, + // color: 3, + // brightness: 80, + // lightingMode: 1, + // timer: null, + // type: DeviceType.Lights, + // image: '', + // recentColors: [ + // 0xFF83D9FF, + // 0xFFFC3E81, + // 0xFFC0FF66, + // 0xFFFDC242, + // ], + // ), + // DeviceModel( + // name: "Balcony Light", + // id: 4, + // functions: [], + // 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.DoorLock, + // page: const DoorView(), + // ), + // DevicesCategoryModel( + // devices: [ + // DeviceModel( + // openPercentage: 10, + // id: 1, + // functions: [], + // name: "Living Room Curtain", + // status: false, + // type: DeviceType.Curtain, + // image: '', + // timer: null, + // ), + // DeviceModel( + // openPercentage: 20, + // id: 2, + // functions: [], + // 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.icons3GangSwitch, + // name: 'Bedroom Lights', + // type: DeviceType.ThreeGang, + // page: const LightSwitchesView(), + // ), + // DevicesCategoryModel( + // devices: [], + // icon: Assets.iconsGateway, + // name: 'Gateway', + // type: DeviceType.Gateway, + // page: const GateWayView(), + // ), ]; selectCategory(int index) { @@ -272,7 +269,22 @@ class DevicesCubit extends Cubit { Widget? get chosenCategoryView { for (var category in allCategories) { if (category.isSelected) { - return category.page; + 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; @@ -289,11 +301,13 @@ 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; + if (category.devices != null) { + for (var device in category.devices!) { + if (device.isSelected) { + category.isSelected = false; + emit(DeviceSelected()); + return; + } } } } @@ -303,9 +317,11 @@ class DevicesCubit extends Cubit { DeviceModel? getSelectedDevice() { for (var category in allCategories) { - for (var device in category.devices) { - if (device.isSelected) { - return device; + if (category.devices != null) { + for (var device in category.devices!) { + if (device.isSelected) { + return device; + } } } } @@ -315,13 +331,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); @@ -331,59 +351,72 @@ 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; + } } } } @@ -391,8 +424,10 @@ class DevicesCubit extends Cubit { clearCategoriesSelection(BuildContext context) { 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); @@ -422,4 +457,108 @@ class DevicesCubit extends Cubit { emit(DeviceControlError(ServerFailure.fromDioError(e).errMessage)); } } + + fetchGroups( + SpaceModel space, + ) async { + emit(DevicesCategoriesLoading()); + try { + allCategories = await DevicesAPI.fetchGroups(space.id!); + emit(DevicesCategoriesSuccess()); + } on DioException catch (e) { + emit(DevicesCategoriesError(ServerFailure.fromDioError(e).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 d913d09..fb53e09 100644 --- a/lib/features/devices/bloc/devices_state.dart +++ b/lib/features/devices/bloc/devices_state.dart @@ -30,3 +30,13 @@ class DeviceControlError extends DevicesState { DeviceControlError(this.errorMsg); } + +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 3476d59..0000000 --- a/lib/features/devices/model/ac_model.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; -import 'package:syncrow_app/features/devices/model/function_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, - required super.functions, - }); - - 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) { - List func = []; - if (json['functions'] != null) { - json['functions'].forEach((v) { - func.add(FunctionModel.fromJson(v)); - }); - } - 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']), - functions: func, - ); - } -} - -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 e693871..0000000 --- a/lib/features/devices/model/curtain_model.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; -import 'package:syncrow_app/features/devices/model/function_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, - required super.functions, - }); - - Map toJson() { - return { - 'openPercentage': openPercentage, - 'timer': timer, - 'id': id, - 'name': name, - 'status': status, - 'type': type, - 'image': image, - }; - } - - factory CurtainModel.fromJson(Map json) { - List func = []; - if (json['functions'] != null) { - json['functions'].forEach((v) { - func.add(FunctionModel.fromJson(v)); - }); - } - return CurtainModel( - id: json['id'], - name: json['name'], - status: json['status'], - openPercentage: json['openPercentage'], - timer: json['timer'], - type: json['type'], - image: json['image'], - functions: func, - ); - } -} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index c9f3303..c1ad2b7 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -1,38 +1,55 @@ // 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/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'], + icon = json['icon'], + type = deviceTypeMap[json['groupName']] ?? DeviceType.Other, + devices = [], + isSelected = false; } + +Map deviceTypeMap = { + 'Ceiling Presence Sensors': DeviceType.Sensors, + 'Wall presence sensor': DeviceType.Sensors, + 'Door Locks': DeviceType.DoorLock, + 'Gateways': DeviceType.Gateway, + 'ACs': DeviceType.AC, +}; diff --git a/lib/features/devices/model/device_model.dart b/lib/features/devices/model/device_model.dart index 78fb291..e14493e 100644 --- a/lib/features/devices/model/device_model.dart +++ b/lib/features/devices/model/device_model.dart @@ -30,7 +30,7 @@ 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/light_model.dart b/lib/features/devices/model/light_model.dart deleted file mode 100644 index 7de5ffa..0000000 --- a/lib/features/devices/model/light_model.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:syncrow_app/features/devices/model/device_model.dart'; -import 'package:syncrow_app/features/devices/model/function_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, - required super.functions, - }); - - 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) { - List func = []; - if (json['functions'] != null) { - json['functions'].forEach((v) { - func.add(FunctionModel.fromJson(v)); - }); - } - 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'], - functions: func, - ); - } -} 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..c100ba8 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( 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/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..694131b 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..e86f888 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) { 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/wizard_switches.dart b/lib/features/devices/view/widgets/wizard_switches.dart index 6400625..dbede2f 100644 --- a/lib/features/devices/view/widgets/wizard_switches.dart +++ b/lib/features/devices/view/widgets/wizard_switches.dart @@ -51,7 +51,7 @@ class WizartSwitches extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SvgPicture.asset( - DevicesCubit.allCategories[index].icon, + DevicesCubit.allCategories[index].icon!, fit: BoxFit.contain, ), CustomSwitch( @@ -63,7 +63,7 @@ class WizartSwitches extends StatelessWidget { child: FittedBox( fit: BoxFit.scaleDown, child: BodyLarge( - text: DevicesCubit.allCategories[index].name, + text: DevicesCubit.allCategories[index].name!, style: context.bodyLarge.copyWith( fontWeight: FontWeight.bold, height: 0, diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 2e74df5..c1f6ae1 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -1,3 +1,4 @@ +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'; @@ -15,4 +16,20 @@ class DevicesAPI { ); return response; } + + static Future> fetchGroups(int spaceId) async { + final response = await HTTPService().get( + path: ApiEndpoints.control, + queryParameters: {'homeId': spaceId, 'pageSize': 100, 'page': 1}, + showServerMessage: false, + expectedResponseModel: (json) { + List categories = []; + for (var category in json['groups']) { + categories.add(DevicesCategoryModel.fromJson(category)); + } + }, + ); + print('fetchGroups response: $response'); + return response; + } } diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 4156ab1..701cd63 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -17,8 +17,10 @@ abstract class Constants { enum DeviceType { AC, Lights, - Door, + DoorLock, Curtain, ThreeGang, Gateway, + Sensors, + Other, } From 13e80fbad735aed7c26b4cd0793f6ac926bb1a33 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 18 Mar 2024 11:46:27 +0300 Subject: [PATCH 08/15] fetch groups --- .vscode/launch.json | 48 +++++++ .../app_layout/bloc/spaces_cubit.dart | 8 +- .../app_layout/model/space_model.dart | 4 + .../view/widgets/app_bar_home_dropdown.dart | 2 +- lib/features/devices/bloc/devices_cubit.dart | 34 +++-- lib/features/devices/bloc/devices_state.dart | 3 + .../devices/model/device_category_model.dart | 21 ++- .../view/widgets/devices_view_body.dart | 20 +-- .../widgets/lights_switches/light_switch.dart | 1 + .../lights_switches/light_switches_body.dart | 75 ++++++++--- .../devices/view/widgets/rooms_slider.dart | 46 +++---- .../devices/view/widgets/wizard_switches.dart | 120 +++++++++--------- .../text_widgets/body_small.dart | 4 + lib/generated/assets.dart | 2 + lib/services/api/api_links_endpoints.dart | 3 + lib/services/api/devices_api.dart | 26 ++-- lib/services/api/http_interceptor.dart | 4 +- lib/services/api/http_service.dart | 1 + lib/services/api/spaces_api.dart | 14 +- lib/utils/resource_manager/constants.dart | 1 + 20 files changed, 273 insertions(+), 164 deletions(-) create mode 100644 .vscode/launch.json 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/lib/features/app_layout/bloc/spaces_cubit.dart b/lib/features/app_layout/bloc/spaces_cubit.dart index f9c7e50..f2c5dbb 100644 --- a/lib/features/app_layout/bloc/spaces_cubit.dart +++ b/lib/features/app_layout/bloc/spaces_cubit.dart @@ -21,7 +21,7 @@ class SpacesCubit extends Cubit { static List spaces = []; - SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null; + static SpaceModel? selectedSpace = spaces.isNotEmpty ? spaces.first : null; RoomModel? selectedRoom; @@ -47,8 +47,8 @@ class SpacesCubit extends Cubit { unselectRoom(); } else { selectedRoom = selectedSpace!.rooms![index - 1]; + emit(RoomSelected(selectedRoom!)); } - emit(RoomSelected(selectedRoom!)); } devicesPageChanged(int index) { @@ -58,12 +58,12 @@ class SpacesCubit extends Cubit { curve: Curves.linear, ); - if (index == 0) { + if (index <= 0) { unselectRoom(); } else { selectedRoom = selectedSpace!.rooms![index - 1]; + emit(RoomSelected(selectedRoom!)); } - emit(RoomSelected(selectedRoom!)); } unselectRoom() { diff --git a/lib/features/app_layout/model/space_model.dart b/lib/features/app_layout/model/space_model.dart index 7205ebf..629a38f 100644 --- a/lib/features/app_layout/model/space_model.dart +++ b/lib/features/app_layout/model/space_model.dart @@ -26,4 +26,8 @@ class SpaceModel { rooms: [], ); } + + static List fromJsonList(List jsonList) { + return jsonList.map((item) => SpaceModel.fromJson(item)).toList(); + } } 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 b5b1ab3..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,7 +28,7 @@ class AppBarHomeDropdown extends StatelessWidget { underline: const SizedBox.shrink(), padding: const EdgeInsets.all(0), borderRadius: BorderRadius.circular(20), - value: SpacesCubit.get(context).selectedSpace!.id, + value: SpacesCubit.selectedSpace!.id, items: SpacesCubit.spaces.map((space) { return DropdownMenuItem( value: space.id, diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 97be459..4a40097 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.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'; @@ -18,7 +19,9 @@ 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!); + } static DevicesCubit get(context) => BlocProvider.of(context); @@ -435,23 +438,10 @@ class DevicesCubit extends Cubit { emit(DevicesCategoryChanged()); } - //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)); - // } - // } deviceControl(DeviceControlModel control) async { emit(DeviceControlLoading()); try { - var response = await DevicesAPI.controlDevice(control); + await DevicesAPI.controlDevice(control); emit(DeviceControlSuccess()); } on DioException catch (e) { emit(DeviceControlError(ServerFailure.fromDioError(e).errMessage)); @@ -461,12 +451,18 @@ class DevicesCubit extends Cubit { fetchGroups( SpaceModel space, ) async { - emit(DevicesCategoriesLoading()); try { - allCategories = await DevicesAPI.fetchGroups(space.id!); + if (state is! DevicesCategoriesLoading) { + emit(DevicesCategoriesLoading()); + allCategories = await DevicesAPI.fetchGroups(space.id!); + } else { + return; + } emit(DevicesCategoriesSuccess()); - } on DioException catch (e) { - emit(DevicesCategoriesError(ServerFailure.fromDioError(e).errMessage)); + } on DioException catch (error) { + emit( + DevicesCategoriesError(ServerFailure.fromDioError(error).errMessage), + ); } } diff --git a/lib/features/devices/bloc/devices_state.dart b/lib/features/devices/bloc/devices_state.dart index fb53e09..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 {} @@ -21,6 +22,7 @@ class DeviceSwitchChanged extends DevicesState {} class DeviceSelected extends DevicesState {} +// Device Control class DeviceControlLoading extends DevicesState {} class DeviceControlSuccess extends DevicesState {} @@ -31,6 +33,7 @@ class DeviceControlError extends DevicesState { DeviceControlError(this.errorMsg); } +// Categories class DevicesCategoriesLoading extends DevicesState {} class DevicesCategoriesSuccess extends DevicesState {} diff --git a/lib/features/devices/model/device_category_model.dart b/lib/features/devices/model/device_category_model.dart index c1ad2b7..1da69d8 100644 --- a/lib/features/devices/model/device_category_model.dart +++ b/lib/features/devices/model/device_category_model.dart @@ -1,6 +1,7 @@ // ignore_for_file: constant_identifier_names 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 { @@ -40,10 +41,16 @@ class DevicesCategoryModel { DevicesCategoryModel.fromJson(Map json) : name = json['groupName'], id = json['groupId'], - icon = json['icon'], 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 = { @@ -52,4 +59,16 @@ Map deviceTypeMap = { '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/view/widgets/devices_view_body.dart b/lib/features/devices/view/widgets/devices_view_body.dart index 4db3d30..c558633 100644 --- a/lib/features/devices/view/widgets/devices_view_body.dart +++ b/lib/features/devices/view/widgets/devices_view_body.dart @@ -23,8 +23,6 @@ class DevicesViewBody extends StatelessWidget { builder: (context, state) { return BlocBuilder( builder: (context, state) { - print( - "length : ${SpacesCubit.get(context).selectedSpace!.rooms!.length}"); //TODO : move to NavigationCubit if (state is DevicesLoading) { return const Center(child: CircularProgressIndicator()); @@ -51,15 +49,9 @@ class DevicesViewBody extends StatelessWidget { }, children: [ const WizardPage(), - if (SpacesCubit.get(context).selectedSpace != null) - if (SpacesCubit.get(context) - .selectedSpace! - .rooms != - null) - ...SpacesCubit.get(context) - .selectedSpace! - .rooms! - .map( + if (SpacesCubit.selectedSpace != null) + if (SpacesCubit.selectedSpace!.rooms != null) + ...SpacesCubit.selectedSpace!.rooms!.map( (room) { return RoomPage( room: room, @@ -76,11 +68,7 @@ class DevicesViewBody extends StatelessWidget { child: SmoothPageIndicator( controller: SpacesCubit.get(context).devicesPageController, - count: SpacesCubit.get(context) - .selectedSpace! - .rooms! - .length + - 1, + count: SpacesCubit.selectedSpace!.rooms!.length + 1, effect: const WormEffect( paintStyle: PaintingStyle.stroke, dotHeight: 8, diff --git a/lib/features/devices/view/widgets/lights_switches/light_switch.dart b/lib/features/devices/view/widgets/lights_switches/light_switch.dart index 8963f57..75ab87f 100644 --- a/lib/features/devices/view/widgets/lights_switches/light_switch.dart +++ b/lib/features/devices/view/widgets/lights_switches/light_switch.dart @@ -26,6 +26,7 @@ class LightSwitch extends StatelessWidget { DevicesCubit.get(context) .deviceControl(control) .then((value) { + print('Device control response: $value'); if (control.value ?? true) { control.value = false; } else { 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 index f5a20a3..6815fd0 100644 --- a/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart +++ b/lib/features/devices/view/widgets/lights_switches/light_switches_body.dart @@ -25,26 +25,65 @@ class LightSwitchesBody extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ - LightSwitch( - control: DeviceControlModel( - deviceId: 'bfe10693d4fd263206ocq9', - code: 'switch_1', - value: true, - ), + 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, + ), + ), + ], ), - LightSwitch( - control: DeviceControlModel( - deviceId: 'bfe10693d4fd263206ocq9', - code: 'switch_2', - value: true, - ), + 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, + ), + ), + ], ), - LightSwitch( - control: DeviceControlModel( - deviceId: 'bfe10693d4fd263206ocq9', - code: 'switch_3', - value: true, - ), + 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, + ), + ), + ], ), ], ), diff --git a/lib/features/devices/view/widgets/rooms_slider.dart b/lib/features/devices/view/widgets/rooms_slider.dart index c046d78..0ed7b45 100644 --- a/lib/features/devices/view/widgets/rooms_slider.dart +++ b/lib/features/devices/view/widgets/rooms_slider.dart @@ -40,33 +40,29 @@ class RoomsSlider extends StatelessWidget { ), ), ), - if (SpacesCubit.get(context).selectedSpace != null) - if (SpacesCubit.get(context).selectedSpace!.rooms != null) - ...SpacesCubit.get(context).selectedSpace!.rooms!.map( - (room) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: InkWell( - onTap: () { - SpacesCubit.get(context).roomSliderPageChanged( - SpacesCubit.get(context) - .selectedSpace! - .rooms! - .indexOf(room)); - }, - child: TitleMedium( - text: room.name!, - style: context.titleMedium.copyWith( - fontSize: 25, - color: SpacesCubit.get(context).selectedRoom == - room - ? ColorsManager.textPrimaryColor - : ColorsManager.textPrimaryColor - .withOpacity(.2), - ), - ), + 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.selectedSpace!.rooms!.indexOf(room)); + }, + child: TitleMedium( + text: room.name!, + style: context.titleMedium.copyWith( + fontSize: 25, + color: SpacesCubit.get(context).selectedRoom == room + ? ColorsManager.textPrimaryColor + : ColorsManager.textPrimaryColor + .withOpacity(.2), ), ), - ) + ), + ), + ) ], ), ); diff --git a/lib/features/devices/view/widgets/wizard_switches.dart b/lib/features/devices/view/widgets/wizard_switches.dart index dbede2f..006567f 100644 --- a/lib/features/devices/view/widgets/wizard_switches.dart +++ b/lib/features/devices/view/widgets/wizard_switches.dart @@ -18,68 +18,72 @@ 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 + ? const Center(child: CircularProgressIndicator()) + : 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, + ), + ), + ), + ), + ], ), ), - ], - ), - ), - ), - ); - }, - ); + ), + ); + }, + ); }, ); } 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/generated/assets.dart b/lib/generated/assets.dart index 90d49a2..48acd39 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -30,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'; @@ -50,6 +51,7 @@ 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'; diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 6e21740..bfe22be 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -16,4 +16,7 @@ abstract class ApiEndpoints { // Devices static const String control = '$baseUrl/device/control'; + + //groups + static const String groups = '$baseUrl/group'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index c1f6ae1..a89a361 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -4,9 +4,11 @@ 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( + final response = await _httpService.post( path: ApiEndpoints.control, body: controlModel.toJson(), showServerMessage: false, @@ -18,18 +20,20 @@ class DevicesAPI { } static Future> fetchGroups(int spaceId) async { - final response = await HTTPService().get( - path: ApiEndpoints.control, - queryParameters: {'homeId': spaceId, 'pageSize': 100, 'page': 1}, + Map params = { + "homeId": spaceId, + "pageSize": 100, + "page": 1 + }; + final response = await _httpService.get( + path: + "https://syncrow.azurewebsites.net/group?homeId=$spaceId&pageSize=100&pageNo=1", + // path: ApiEndpoints.groups, + // queryParameters: params, showServerMessage: false, - expectedResponseModel: (json) { - List categories = []; - for (var category in json['groups']) { - categories.add(DevicesCategoryModel.fromJson(category)); - } - }, + expectedResponseModel: (json) => + DevicesCategoryModel.fromJsonList(json['groups']), ); - print('fetchGroups response: $response'); return response; } } diff --git a/lib/services/api/http_interceptor.dart b/lib/services/api/http_interceptor.dart index fa22beb..3460bc4 100644 --- a/lib/services/api/http_interceptor.dart +++ b/lib/services/api/http_interceptor.dart @@ -12,10 +12,10 @@ class HTTPInterceptor extends InterceptorsWrapper { @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { - var storage = FlutterSecureStorage(); + var storage = const FlutterSecureStorage(); var token = await storage.read(key: Token.loginAccessTokenKey); - options.headers['Authorization'] = 'Bearer $token'; + options.headers['Authorization'] = 'Bearer $token'; super.onRequest(options, handler); } // diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index 531d4ba..c1dd03f 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -44,6 +44,7 @@ class HTTPService { } catch (error) { debugPrint("******* Error"); debugPrint(error.toString()); + debugPrint("******* Error End"); rethrow; } } diff --git a/lib/services/api/spaces_api.dart b/lib/services/api/spaces_api.dart index 060ca78..3f93385 100644 --- a/lib/services/api/spaces_api.dart +++ b/lib/services/api/spaces_api.dart @@ -6,29 +6,25 @@ 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( + final response = await _httpService.get( path: ApiEndpoints.spaces, queryParameters: { "userUuid": uuid, }, showServerMessage: false, - expectedResponseModel: (json) { - List spaces = []; - for (var space in json) { - spaces.add(SpaceModel.fromJson(space)); - } - return spaces; - }, + expectedResponseModel: (json) => SpaceModel.fromJsonList(json), ); return response; } //get rooms by space id static Future> getRooms(int spaceId) async { - final response = await HTTPService().get( + final response = await _httpService.get( path: ApiEndpoints.rooms, queryParameters: {"homeId": spaceId}, showServerMessage: false, diff --git a/lib/utils/resource_manager/constants.dart b/lib/utils/resource_manager/constants.dart index 701cd63..1ec5ab8 100644 --- a/lib/utils/resource_manager/constants.dart +++ b/lib/utils/resource_manager/constants.dart @@ -22,5 +22,6 @@ enum DeviceType { ThreeGang, Gateway, Sensors, + Gang, Other, } From 5cc2db7645facdd26c9a8208a444bd41549e53f9 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Mon, 18 Mar 2024 11:46:35 +0300 Subject: [PATCH 09/15] fetch groups --- assets/icons/gang.svg | 9 +++++++++ assets/icons/sensors.svg | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 assets/icons/gang.svg create mode 100644 assets/icons/sensors.svg 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/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 @@ + + + + + + + + + + + + + + + + + + + From 79279180df3dd3a47ff0de8b2e23e0c78ccf46bc Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 11:15:45 +0300 Subject: [PATCH 10/15] Refactor code for better readability and maintainability - Add method to get token and validate in AuthCubit - Update AuthState with loading, success, and error states for token - Use BlocBuilder in SplashView for token validation and navigation This commit refactors the code in AuthCubit to include a method to get the token and validate it. It also updates the AuthState with loading, success, and error states for token handling. In SplashView, BlocBuilder is now used to handle token validation and navigation based on the token status. --- lib/features/auth/bloc/auth_cubit.dart | 30 ++++++++++- lib/features/auth/bloc/auth_state.dart | 10 ++++ lib/features/devices/bloc/devices_cubit.dart | 2 + .../widgets/ACs/ac_mode_control_unit.dart | 2 +- lib/features/splash/view/splash_view.dart | 50 +++++-------------- lib/services/api/devices_api.dart | 8 ++- 6 files changed, 58 insertions(+), 44 deletions(-) diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 49a924d..408c80d 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -13,7 +13,9 @@ import 'package:syncrow_app/services/api/network_exception.dart'; part 'auth_state.dart'; class AuthCubit extends Cubit { - AuthCubit() : super(AuthInitial()); + AuthCubit() : super(AuthInitial()) { + getTokenAndValidate(); + } static AuthCubit get(context) => BlocProvider.of(context); @@ -79,4 +81,30 @@ class AuthCubit extends Cubit { 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/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 4a40097..8105505 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,3 +1,5 @@ +// ignore_for_file: constant_identifier_names + import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; 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 c100ba8..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 @@ -43,7 +43,7 @@ class _ACModeControlUnitState extends State { // 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]), diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index 0c65933..05d11d0 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -1,7 +1,7 @@ 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/model/token.dart'; +import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; @@ -10,20 +10,18 @@ class SplashView extends StatelessWidget { @override Widget build(BuildContext context) { - return FutureBuilder( - future: getTokenAndValidate(context), - builder: (context, snapshot) { - if (snapshot.hasData) { - if (snapshot.data ?? false) { - Future.delayed(const Duration(seconds: 1), () { - Navigator.pushReplacementNamed(context, Routes.homeRoute); - }); - } else { - Future.delayed(const Duration(seconds: 1), () { - Navigator.pushReplacementNamed(context, Routes.authLogin); - }); - } + return 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.pushReplacementNamed(context, Routes.authLogin); + }); } + return Scaffold( body: Stack( alignment: Alignment.center, @@ -61,26 +59,4 @@ class SplashView extends StatelessWidget { }, ); } - - Future getTokenAndValidate(BuildContext context) async { - return await const FlutterSecureStorage() - .read(key: Token.loginAccessTokenKey) - .then((value) { - if (value == null) { - return false; - } - print("Decoding token Started"); - var tokenData = Token.decodeToken(value ?? ""); - print("checking token data"); - - if (tokenData.containsKey('exp')) { - var exp = tokenData['exp'] ?? 0; - var currentTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; - print('time: $currentTime exp: $exp'); - return currentTime < exp; - } else { - return false; - } - }); - } } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index a89a361..cf885e8 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -23,13 +23,11 @@ class DevicesAPI { Map params = { "homeId": spaceId, "pageSize": 100, - "page": 1 + "pageNo": 1 }; final response = await _httpService.get( - path: - "https://syncrow.azurewebsites.net/group?homeId=$spaceId&pageSize=100&pageNo=1", - // path: ApiEndpoints.groups, - // queryParameters: params, + path: ApiEndpoints.groups, + queryParameters: params, showServerMessage: false, expectedResponseModel: (json) => DevicesCategoryModel.fromJsonList(json['groups']), From 42051bb977db60cad0f1e46ca8aa22c29b3bbf40 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 13:29:12 +0300 Subject: [PATCH 11/15] Fixed the overflow in the login screen, the page is scrollable. - Fixed Scrolling wasent working in login screen - Changed Home page to indexed based view instead of PageView --- lib/features/app_layout/bloc/nav_cubit.dart | 7 +- lib/features/app_layout/view/app_layout.dart | 12 +- .../app_layout/view/widgets/app_body.dart | 6 +- .../auth/view/widgets/login/login_view.dart | 64 ++-- lib/features/devices/bloc/devices_cubit.dart | 296 ++---------------- .../view/widgets/lights/lights_view.dart | 4 +- .../view/widgets/lights/lights_view_list.dart | 2 +- .../devices/view/widgets/wizard_switches.dart | 121 +++---- lib/my_app.dart | 5 - lib/services/api/http_service.dart | 4 +- 10 files changed, 148 insertions(+), 373 deletions(-) diff --git a/lib/features/app_layout/bloc/nav_cubit.dart b/lib/features/app_layout/bloc/nav_cubit.dart index ebcf53a..b485ad8 100644 --- a/lib/features/app_layout/bloc/nav_cubit.dart +++ b/lib/features/app_layout/bloc/nav_cubit.dart @@ -17,10 +17,8 @@ class NavCubit extends Cubit { static NavCubit of(context) => BlocProvider.of(context); - //functoin to do the important work when the user logs out static clear() { pageIndex = 0; - pageController.jumpToPage(0); } static int pageIndex = 0; @@ -146,12 +144,9 @@ class NavCubit extends Cubit { const MenuView(), ]; - static final PageController pageController = PageController(); - void updatePageIndex(int index) { pageIndex = index; - pageController.animateToPage(index, - duration: const Duration(milliseconds: 150), curve: Curves.easeIn); + emit(NavChangePage()); } } diff --git a/lib/features/app_layout/view/app_layout.dart b/lib/features/app_layout/view/app_layout.dart index bce053e..dbb164c 100644 --- a/lib/features/app_layout/view/app_layout.dart +++ b/lib/features/app_layout/view/app_layout.dart @@ -6,6 +6,7 @@ import 'package:syncrow_app/features/app_layout/bloc/spaces_cubit.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/app_body.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/default_app_bar.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/default_nav_bar.dart'; +import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; @@ -14,8 +15,15 @@ class AppLayout extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => SpacesCubit(), + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => SpacesCubit(), + ), + BlocProvider( + create: (context) => DevicesCubit(), + ), + ], child: BlocListener( listener: (context, state) { if (state is SpacesError) { diff --git a/lib/features/app_layout/view/widgets/app_body.dart b/lib/features/app_layout/view/widgets/app_body.dart index 49cec88..0ba7833 100644 --- a/lib/features/app_layout/view/widgets/app_body.dart +++ b/lib/features/app_layout/view/widgets/app_body.dart @@ -38,11 +38,7 @@ class AppBody extends StatelessWidget { }, builder: (context, state) { return state is! SpacesLoading || state is! SpaceRoomsLoading - ? PageView( - physics: const NeverScrollableScrollPhysics(), - controller: NavCubit.pageController, - children: NavCubit.of(context).pages, - ) + ? NavCubit.of(context).pages[NavCubit.pageIndex] : const Center(child: CircularProgressIndicator()); }, ), diff --git a/lib/features/auth/view/widgets/login/login_view.dart b/lib/features/auth/view/widgets/login/login_view.dart index 34c00ab..06713c2 100644 --- a/lib/features/auth/view/widgets/login/login_view.dart +++ b/lib/features/auth/view/widgets/login/login_view.dart @@ -33,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, @@ -99,9 +99,9 @@ class LoginView extends StatelessWidget { ), ), ), - ), - ) - ], + ) + ], + ), ), ); }, diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 8105505..2f3c7e1 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -1,4 +1,4 @@ -// ignore_for_file: constant_identifier_names +// ignore_for_file: constant_identifier_names, unused_import import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -22,273 +22,51 @@ part 'devices_state.dart'; class DevicesCubit extends Cubit { DevicesCubit() : super(DevicesInitial()) { - fetchGroups(SpacesCubit.selectedSpace!); + fetchGroups(SpacesCubit.selectedSpace!.id!); } static DevicesCubit get(context) => BlocProvider.of(context); - static List allCategories = [ - // DevicesCategoryModel( - // devices: [ - // DeviceModel( - // name: "Living Room AC", - // id: 0, - // functions: [], - // status: false, - // temperature: 20, - // fanSpeed: 0, - // tempMode: 0, - // coolTo: 20, - // type: DeviceType.AC, - // image: '', - // timer: null, - // bounds: Bounds( - // min: 20, - // max: 30, - // ), - // ), - // DeviceModel( - // name: "Master Bedroom AC", - // id: 1, - // functions: [], - // status: false, - // temperature: 20, - // fanSpeed: 0, - // tempMode: 0, - // coolTo: 20, - // type: DeviceType.AC, - // image: '', - // timer: null, - // bounds: Bounds( - // min: 20, - // max: 30, - // ), - // ), - // DeviceModel( - // name: "Kitchen AC", - // id: 2, - // functions: [], - // status: false, - // temperature: 20, - // fanSpeed: 0, - // tempMode: 0, - // coolTo: 20, - // type: DeviceType.AC, - // image: '', - // timer: null, - // bounds: Bounds( - // min: 20, - // max: 30, - // ), - // ), - // DeviceModel( - // name: "Bathroom AC", - // id: 3, - // functions: [], - // 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: [ - // DeviceModel( - // name: "Living Room Light", - // id: 0, - // functions: [], - // status: false, - // color: 0, - // brightness: 20, - // lightingMode: 1, - // timer: null, - // type: DeviceType.Lights, - // image: '', - // recentColors: [ - // 0xFF83D9FF, - // 0xFFFC3E81, - // 0xFFC0FF66, - // 0xFFFDC242, - // ], - // ), - // DeviceModel( - // name: "Master Bedroom Light", - // id: 1, - // functions: [], - // status: false, - // color: 2, - // brightness: 40, - // lightingMode: 1, - // timer: null, - // type: DeviceType.Lights, - // image: '', - // recentColors: [ - // 0xFF83D9FF, - // 0xFFFC3E81, - // 0xFFC0FF66, - // 0xFFFDC242, - // ], - // ), - // DeviceModel( - // name: "Kitchen Light", - // id: 2, - // functions: [], - // status: false, - // color: 1, - // brightness: 60, - // lightingMode: 1, - // timer: null, - // type: DeviceType.Lights, - // image: '', - // recentColors: [ - // 0xFF83D9FF, - // 0xFFFC3E81, - // 0xFFC0FF66, - // 0xFFFDC242, - // ], - // ), - // DeviceModel( - // name: "Bathroom Light", - // id: 3, - // functions: [], - // status: false, - // color: 3, - // brightness: 80, - // lightingMode: 1, - // timer: null, - // type: DeviceType.Lights, - // image: '', - // recentColors: [ - // 0xFF83D9FF, - // 0xFFFC3E81, - // 0xFFC0FF66, - // 0xFFFDC242, - // ], - // ), - // DeviceModel( - // name: "Balcony Light", - // id: 4, - // functions: [], - // 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.DoorLock, - // page: const DoorView(), - // ), - // DevicesCategoryModel( - // devices: [ - // DeviceModel( - // openPercentage: 10, - // id: 1, - // functions: [], - // name: "Living Room Curtain", - // status: false, - // type: DeviceType.Curtain, - // image: '', - // timer: null, - // ), - // DeviceModel( - // openPercentage: 20, - // id: 2, - // functions: [], - // 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.icons3GangSwitch, - // name: 'Bedroom Lights', - // type: DeviceType.ThreeGang, - // page: const LightSwitchesView(), - // ), - // 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) { - 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; + 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; + } } } } @@ -296,7 +74,7 @@ class DevicesCubit extends Cubit { } DevicesCategoryModel? get chosenCategory { - for (var category in allCategories) { + for (var category in allCategories!) { if (category.isSelected) { return category; } @@ -305,7 +83,7 @@ class DevicesCubit extends Cubit { } selectDevice(DeviceModel device) { - for (var category in allCategories) { + for (var category in allCategories!) { if (category.devices != null) { for (var device in category.devices!) { if (device.isSelected) { @@ -321,7 +99,7 @@ class DevicesCubit extends Cubit { } DeviceModel? getSelectedDevice() { - for (var category in allCategories) { + for (var category in allCategories!) { if (category.devices != null) { for (var device in category.devices!) { if (device.isSelected) { @@ -356,7 +134,7 @@ class DevicesCubit extends Cubit { turnOnOffDevice(DeviceModel device) { device.status = !device.status!; - DevicesCategoryModel category = allCategories.firstWhere((category) { + DevicesCategoryModel category = allCategories!.firstWhere((category) { if (category.devices != null) { return category.devices!.contains(device); } else { @@ -427,7 +205,7 @@ class DevicesCubit extends Cubit { } clearCategoriesSelection(BuildContext context) { - for (var category in allCategories) { + for (var category in allCategories!) { category.isSelected = false; if (category.devices != null) { for (var device in category.devices!) { @@ -450,16 +228,10 @@ class DevicesCubit extends Cubit { } } - fetchGroups( - SpaceModel space, - ) async { + fetchGroups(int spaceId) async { try { - if (state is! DevicesCategoriesLoading) { - emit(DevicesCategoriesLoading()); - allCategories = await DevicesAPI.fetchGroups(space.id!); - } else { - return; - } + emit(DevicesCategoriesLoading()); + allCategories = await DevicesAPI.fetchGroups(spaceId); emit(DevicesCategoriesSuccess()); } on DioException catch (error) { emit( diff --git a/lib/features/devices/view/widgets/lights/lights_view.dart b/lib/features/devices/view/widgets/lights/lights_view.dart index 694131b..80d3a23 100644 --- a/lib/features/devices/view/widgets/lights/lights_view.dart +++ b/lib/features/devices/view/widgets/lights/lights_view.dart @@ -27,8 +27,8 @@ class LightsView extends StatelessWidget { as DeviceModel; } List lights = []; - if (DevicesCubit.allCategories[1].devices != null) { - for (var device in DevicesCubit.allCategories[1].devices!) { + 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 e86f888..2348dd1 100644 --- a/lib/features/devices/view/widgets/lights/lights_view_list.dart +++ b/lib/features/devices/view/widgets/lights/lights_view_list.dart @@ -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/wizard_switches.dart b/lib/features/devices/view/widgets/wizard_switches.dart index 006567f..8a200d2 100644 --- a/lib/features/devices/view/widgets/wizard_switches.dart +++ b/lib/features/devices/view/widgets/wizard_switches.dart @@ -18,71 +18,80 @@ class WizartSwitches extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return state is DevicesLoading - ? const Center(child: CircularProgressIndicator()) - : 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( + 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: [ - SvgPicture.asset( - DevicesCubit.allCategories[index].icon!, - fit: BoxFit.contain, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + SvgPicture.asset( + DevicesCubit.allCategories![index].icon!, + fit: BoxFit.contain, + ), + CustomSwitch( + category: + DevicesCubit.allCategories![index], + ), + ], ), - 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, + ), + ), + ), ), ], ), - 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/my_app.dart b/lib/my_app.dart index 8e88cdb..99375cc 100644 --- a/lib/my_app.dart +++ b/lib/my_app.dart @@ -5,8 +5,6 @@ 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'; @@ -30,9 +28,6 @@ class MyApp extends StatelessWidget { lazy: false, create: (context) => NavCubit(), ), - BlocProvider( - create: (context) => DevicesCubit(), - ), ], child: MaterialApp( debugShowCheckedModeBanner: false, diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index c1dd03f..a5f24bd 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -38,8 +38,8 @@ class HTTPService { queryParameters: queryParameters, ); - 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) { debugPrint("******* Error"); From 3cbc3d7fa9d8b70416baccea8c563108656186b1 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 14:58:33 +0300 Subject: [PATCH 12/15] Add autovalidate mode to LoginForm Set autovalidate mode to onUserInteraction in the LoginForm widget to improve user experience. --- lib/features/auth/view/widgets/login/login_form.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/features/auth/view/widgets/login/login_form.dart b/lib/features/auth/view/widgets/login/login_form.dart index 3447f3e..fd3d7ab 100644 --- a/lib/features/auth/view/widgets/login/login_form.dart +++ b/lib/features/auth/view/widgets/login/login_form.dart @@ -18,6 +18,7 @@ class LoginForm extends StatelessWidget { builder: (context, state) { return Form( key: formKey, + autovalidateMode: AutovalidateMode.onUserInteraction, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, From cb913cd784e0790cd7f99917dcdc59cd8622b973 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 15:06:10 +0300 Subject: [PATCH 13/15] Refactor navigation in SplashView and add CustomPageRoute Introduce CustomPageRoute for smoother navigation transitions in SplashView. Update navigation in SplashView to use CustomPageRoute for login redirection. --- lib/features/splash/view/splash_view.dart | 7 ++++++- lib/utils/helpers/custom_page_route.dart | 18 +++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/features/splash/view/splash_view.dart b/lib/features/splash/view/splash_view.dart index 05d11d0..99a826e 100644 --- a/lib/features/splash/view/splash_view.dart +++ b/lib/features/splash/view/splash_view.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.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/helpers/custom_page_route.dart'; class SplashView extends StatelessWidget { const SplashView({super.key}); @@ -18,7 +20,10 @@ class SplashView extends StatelessWidget { }); } else { Future.delayed(const Duration(seconds: 1), () { - Navigator.pushReplacementNamed(context, Routes.authLogin); + Navigator.pushReplacement( + context, + CustomPageRoute(builder: (context) => const LoginView()), + ); }); } 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, + ); } From d4a9cfb7aba90faff7e94c4cdd3cbd943af57105 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 15:13:00 +0300 Subject: [PATCH 14/15] Refactor error handling in AuthCubit and ServerFailure Update error handling in AuthCubit to use toString() method and mdified handling of badResponse in ServerFailure class. --- lib/features/auth/bloc/auth_cubit.dart | 2 +- lib/services/api/network_exception.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 408c80d..cf98d3d 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -66,7 +66,7 @@ class AuthCubit extends Cubit { emit(AuthError('Something went wrong')); } } on DioException catch (e) { - emit(AuthError(ServerFailure.fromDioError(e).errMessage)); + emit(AuthError(ServerFailure.fromDioError(e).toString())); } } diff --git a/lib/services/api/network_exception.dart b/lib/services/api/network_exception.dart index 2082e97..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"); From a52f2c0b5aa2309236d2ec8ddc2b9e3b38c515d3 Mon Sep 17 00:00:00 2001 From: Mohammad Salameh Date: Tue, 19 Mar 2024 15:17:40 +0300 Subject: [PATCH 15/15] Add close method override in DevicesCubit Add override for close method in DevicesCubit to set _isClosed flag and call super.close(). This prevents further operations after closing. --- lib/features/devices/bloc/devices_cubit.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/features/devices/bloc/devices_cubit.dart b/lib/features/devices/bloc/devices_cubit.dart index 2f3c7e1..cebe17e 100644 --- a/lib/features/devices/bloc/devices_cubit.dart +++ b/lib/features/devices/bloc/devices_cubit.dart @@ -24,6 +24,13 @@ class DevicesCubit extends Cubit { 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); @@ -229,6 +236,8 @@ class DevicesCubit extends Cubit { } fetchGroups(int spaceId) async { + if (_isClosed) return; + try { emit(DevicesCategoriesLoading()); allCategories = await DevicesAPI.fetchGroups(spaceId);