import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:onesignal_flutter/onesignal_flutter.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:share_plus/share_plus.dart'; import 'package:syncrow_app/features/app_layout/model/permission_model.dart'; import 'package:syncrow_app/features/app_layout/model/space_model.dart'; import 'package:syncrow_app/features/app_layout/view/widgets/app_bar_home_dropdown.dart'; import 'package:syncrow_app/features/auth/model/project_model.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart'; import 'package:syncrow_app/features/devices/model/subspace_model.dart'; import 'package:syncrow_app/features/devices/view/widgets/devices_view_body.dart'; import 'package:syncrow_app/features/menu/view/menu_view.dart'; import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_app/features/scene/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_app/features/scene/bloc/smart_scene/smart_scene_select_dart_bloc.dart'; import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart'; import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart'; import 'package:syncrow_app/features/scene/view/routines_view.dart'; import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; import 'package:syncrow_app/navigation/routing_constants.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; import 'package:syncrow_app/services/api/profile_api.dart'; import 'package:syncrow_app/services/api/spaces_api.dart'; import 'package:syncrow_app/utils/constants/temp_const.dart'; import 'package:syncrow_app/utils/helpers/snack_bar.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; part 'home_state.dart'; class HomeCubit extends Cubit { HomeCubit._() : super(HomeInitial()) { // checkIfNotificationPermissionGranted(); fetchUserInfo().then( (value) { if (selectedSpace == null) { fetchUnitsByUserId(); fetchPermissions(); // .then((value) { // if (selectedSpace != null) { // fetchRoomsByUnitId(selectedSpace!); // } // }); } }, ); } static UserModel? user; List? permissionModel = []; static HomeCubit? _instance; static HomeCubit getInstance() { // If an instance already exists, return it _instance ??= HomeCubit._(); return _instance!; } Future fetchUserInfo() async { try { emit(HomeLoading()); var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await ProfileApi().fetchUserInfo(uuid); project = user?.project; emit(HomeUserInfoLoaded(user!)); } catch (e) { return; } } static bool manageSupSpace = false; static bool manageScene = false; static bool manageDeviceLocation = false; static bool visitorPasswordManagement = false; String errorMsg = ''; Future fetchPermissions() async { try { emit(HomeLoading()); final response = await ProfileApi().fetchPermissions(user!.role!.uuid); permissionModel = PermissionModel.fromJsonList(response); hasViewPermission(); emit(PermissionsRoleLoaded(permissionModel!)); } catch (e) { emit(HomeError(e.toString())); } } void hasViewPermission() { emit(HomeLoading()); manageSupSpace = hasPermission( permissionModel!, 'SPACE_MANAGEMENT', 'MANAGE_SPACE', 'ASSIGN_USER_TO_SPACE', ); manageScene = hasPermission( permissionModel!, 'AUTOMATION_MANAGEMENT', 'MANAGE_SCENES', 'UPDATE', ); manageDeviceLocation = hasPermission( permissionModel!, 'DEVICE_MANAGEMENT', 'MANAGE_DEVICE', 'LOCATION_UPDATE', ); visitorPasswordManagement = hasPermission( permissionModel!, 'VISITOR_PASSWORD_MANAGEMENT', 'MANAGE_VISITOR_PASSWORD', 'VIEW', ); emit(HomePermissionUpdated()); } bool hasPermission(List permissions, String mainTitle, String subTitle, String finalTitle) { try { final mainOption = permissions.firstWhere( (perm) => perm.title == mainTitle, ); final subOption = mainOption.subOptions.firstWhere( (sub) => sub.title == subTitle, ); if (subOption.subOptions == null) { return false; } final finalOption = subOption.subOptions!.firstWhere( (finalSub) => finalSub.title == finalTitle, ); return finalOption.isChecked == true; } catch (e) { return false; } } void emitSafe(HomeState newState) { final cubit = this; if (!cubit.isClosed) { cubit.emit(newState); } } @override Future close() { _instance = null; selectedSpace = null; selectedRoom = null; pageIndex = 0; // OneSignal.User.pushSubscription // .removeObserver((stateChanges) => oneSignalSubscriptionObserver); // OneSignal.Notifications.removePermissionObserver( // (permission) => oneSignalPermissionObserver); // OneSignal.Notifications.removeClickListener( // (event) => oneSignalClickListenerObserver); return super.close(); } static HomeCubit get(context) => BlocProvider.of(context); List spaces = []; SpaceModel? selectedSpace; SubSpaceModel? selectedRoom; Project? project; PageController devicesPageController = PageController(); PageController roomsPageController = PageController(); var duration = const Duration(milliseconds: 300); // void oneSignalPermissionObserver; // void oneSignalSubscriptionObserver; // void oneSignalClickListenerObserver; // selectSpace(SpaceModel space) async { // selectedSpace = space; // emit(SpaceSelected(space)); // } checkIfNotificationPermissionGranted() async { try { OneSignal.initialize('762350c9-1e5d-4d95-a648-16d4dc8a25e1'); //Show native push notification dialog if (Platform.isIOS) { await OneSignal.Notifications.permissionNative(); } else { await OneSignal.Notifications.requestPermission(true); } if (await Permission.notification.isGranted == false) { return; } var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; if (userUuid.isNotEmpty) { await OneSignal.login(userUuid); } //Enable push notifications await OneSignal.User.pushSubscription.optIn(); // //this function will be called once a user is subscribed // oneSignalSubscriptionObserver = // OneSignal.User.pushSubscription.addObserver((state) async { // if (state.current.optedIn) { // await _sendSubscriptionId(); // } // }); // // Send the player id when a user allows notifications // oneSignalPermissionObserver = // OneSignal.Notifications.addPermissionObserver((state) async { // await _sendSubscriptionId(); // }); // //check if the player id is sent, if not send it again // await _sendSubscriptionId(); // oneSignalClickListenerObserver = // OneSignal.Notifications.addClickListener((event) async { // //Once the user clicks on the notification // }); } catch (err) { debugPrint("******* Error"); debugPrint(err.toString()); rethrow; } } _sendSubscriptionId() async { // String? subscriptionId = OneSignal.User.pushSubscription.id ?? ''; //TODO send the subscription id to BE } changeSelectedSpace(SpaceModel space) { selectedSpace = space; emitSafe(SpaceSelected(space)); fetchRoomsByUnitId(space); } roomSliderPageChanged(int index) { devicesPageController.animateToPage( index, duration: duration, curve: Curves.linear, ); if (index == 0) { unselectRoom(); } else if (index == 1) { unselectRoom1(); } else { selectedRoom = selectedSpace!.subspaces[index - 2]; emitSafe(RoomSelected(selectedRoom!)); } } devicesPageChanged(int index) { roomsPageController.animateToPage( index, duration: const Duration(milliseconds: 300), curve: Curves.linear, ); if (index <= 0) { unselectRoom(); } else if (index == 1) { unselectRoom1(); } else { selectedRoom = selectedSpace!.subspaces[index - 2]; emitSafe(RoomSelected(selectedRoom!)); } } unselectRoom() { // selectedRoom = null; devicesPageController.animateToPage( 0, duration: duration, curve: Curves.linear, ); roomsPageController.animateToPage( 0, duration: duration, curve: Curves.linear, ); emitSafe(RoomUnSelected()); } unselectRoom1() { // selectedRoom = null; devicesPageController.animateToPage( 1, duration: duration, curve: Curves.linear, ); roomsPageController.animateToPage( 1, duration: duration, curve: Curves.linear, ); emitSafe(RoomUnSelected()); } //////////////////////////////////////// API //////////////////////////////////////// generateInvitation(SpaceModel unit) async { try { final invitationCode = await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid, project?.uuid ?? TempConst.projectIdDev); if (invitationCode.isNotEmpty) { Share.share('The invitation code is $invitationCode'); CustomSnackBar.displaySnackBar( 'Invitation code generated successfully the code is: $invitationCode'); } else { CustomSnackBar.displaySnackBar('Please try again!'); } } catch (failure) { CustomSnackBar.displaySnackBar('Something went wrong'); return; } } Future joinAUnit(String code) async { try { var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? ''; Map body = {'inviteCode': code}; final success = await SpacesAPI.joinUnit(userUuid, body); if (success) { await fetchUnitsByUserId(); CustomSnackBar.displaySnackBar('Done successfully'); } return true; } catch (failure) { CustomSnackBar.displaySnackBar('Something went wrong'); return false; } } fetchUnitsByUserId() async { emitSafe(GetSpacesLoading()); try { spaces = await SpacesAPI.getSpacesByUserId(); emitSafe(GetSpacesSuccess(spaces)); } catch (failure) { emitSafe(GetSpacesError("No units found")); return; } if (spaces.isNotEmpty) { selectedSpace = spaces.first; await fetchRoomsByUnitId(selectedSpace!); emitSafe(GetSpacesSuccess(spaces)); } else { emitSafe(GetSpacesError("No spaces found")); } } fetchRoomsByUnitId(SpaceModel space) async { emitSafe(GetSpaceRoomsLoading()); try { space.subspaces = await SpacesAPI.getSubSpaceBySpaceId( space.community.uuid, space.id, project?.uuid ?? TempConst.projectIdDev); } catch (failure) { emitSafe(GetSpaceRoomsError(failure.toString())); return; } if (space.subspaces.isNotEmpty) { emitSafe(GetSpaceRoomsSuccess(space.subspaces)); } else { emitSafe(GetSpaceRoomsError("No rooms found")); } } activationCode(activationCode) async { try { emitSafe(GetSpaceRoomsLoading()); var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); var res = await SpacesAPI.activationCodeSpace( activationCode: activationCode, userUuid: uuid); if (res['success'] == true) { fetchUserInfo(); fetchUnitsByUserId(); } emitSafe(GetSpacesSuccess(spaces!)); return res['success']; } on DioException catch (e) { final errorMessage = e.response?.data['error']['message']; errorMsg = e.response?.data['error']['message']; emitSafe(ActivationError(errMessage: errorMsg)); return false; } } /////////////////////////////////////// Nav /////////////////////////////////////// static int pageIndex = 0; static Map> appBarActions = { // 'Dashboard': [ // // IconButton( // // icon: const Icon( // // Icons.add, // // size: 25, // // ), // // style: ButtonStyle( // // foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), // // ), // // onPressed: () { // // Navigator.push( // // NavigationService.navigatorKey.currentContext!, // // CustomPageRoute( // // builder: (context) => CurtainView( // // curtain: DeviceModel( // // name: "Curtain", // // status: [StatusModel(code: "awd", value: 1)], // // productType: DeviceType.Curtain, // // ), // // ), // // ), // // ); // // }, // // ), // ], 'Devices': [ //TODO: to be checked // IconButton( // icon: const Icon( // Icons.add, // size: 25, // ), // style: ButtonStyle( // foregroundColor: // MaterialStateProperty.all(ColorsManager.textPrimaryColor), // ), // onPressed: () {}, // ), // IconButton( // icon: const Icon( // Icons.more_vert, // size: 25, // ), // style: ButtonStyle( // foregroundColor: // MaterialStateProperty.all(ColorsManager.textPrimaryColor), // ), // onPressed: () {}, // ), ], 'Routine': [ // IconButton( // icon: Image.asset( // Assets.assetsIconsFilter, // height: 20, // width: 20, // ), // onPressed: () {}, // ), manageScene ? IconButton( icon: const Icon( Icons.add, size: 32, ), style: ButtonStyle( foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor), ), onPressed: () { Navigator.pushNamed( NavigationService.navigatorKey.currentContext!, Routes.sceneTasksRoute, arguments: SceneSettingsRouteArguments( sceneType: '', sceneId: '', sceneName: '', ), ); NavigationService.navigatorKey.currentContext! .read() .add(const ClearTaskListEvent()); NavigationService.navigatorKey.currentContext! .read() .add(const SceneTypeEvent(CreateSceneEnum.none)); NavigationService.navigatorKey.currentContext! .read() .add(const SmartSceneClearEvent()); BlocProvider.of( NavigationService.navigatorKey.currentState!.context) .add(ResetEffectivePeriod()); NavigationService.navigatorKey.currentContext! .read() .add(const ClearTabToRunSetting()); }, ) : const SizedBox(), // IconButton( // icon: const Icon( // Icons.more_vert, // size: 28, // ), // style: ButtonStyle( // foregroundColor: // WidgetStateProperty.all(ColorsManager.textPrimaryColor), // ), // onPressed: () {}, // ), ], 'Menu': [ // IconButton( // icon: SvgPicture.asset( // Assets.assetsIconsScan, // height: 20, // width: 20, // ), // onPressed: () {}, // ), ], }; static Map appBarLeading = { // 'Dashboard': const AppBarHomeDropdown(), 'Devices': const AppBarHomeDropdown(), 'Routine': const AppBarHomeDropdown(), 'Menu': Padding( padding: const EdgeInsets.only(left: 15), child: Image.asset( Assets.assetsImagesLogoHorizontal, height: 15, width: 100, fit: BoxFit.scaleDown, ), ), }; static var bottomNavItems = [ // defaultBottomNavBarItem(icon: Assets.assetsIconsDashboard, label: 'Dashboard'), // defaultBottomNavBarItem(icon: Assets.assetsIconslayout, label: 'Layout'), defaultBottomNavBarItem(icon: Assets.assetsIconsDevices, label: 'Devices'), defaultBottomNavBarItem(icon: Assets.assetsIconsRoutines, label: 'Routine'), defaultBottomNavBarItem(icon: Assets.assetsIconsMenu, label: 'Menu'), ]; final List pages = [ // const DashboardView(), // const LayoutPage(), BlocProvider( create: (context) => DevicesCubit.getInstance(), child: const DevicesViewBody(), ), const RoutinesView(), const MenuView(), ]; void updatePageIndex(int index) { pageIndex = index; emitSafe(NavChangePage()); } void updateDevice(String deviceId) async { try { final response = await DevicesAPI.firmwareDevice( deviceId: deviceId, firmwareVersion: '0'); if (response['success'] ?? false) { CustomSnackBar.displaySnackBar('No updates available'); } } catch (_) {} } } BottomNavigationBarItem defaultBottomNavBarItem( {required String icon, required String label}) { return BottomNavigationBarItem( icon: SvgPicture.asset(icon), activeIcon: SvgPicture.asset( icon.replaceAll('.svg', '-fill.svg'), colorFilter: const ColorFilter.mode( ColorsManager.primaryColor, BlendMode.srcIn, ), ), label: label, ); } // class PermissionUtils { // // Check if the "VIEW" permission exists in "MANAGE_SUBSPACE" // static bool hasViewPermission(List permissions) { // return _hasPermission(permissions, 'MANAGE_SUBSPACE', 'VIEW'); // } // // Generalized permission checker // static bool _hasPermission( // List permissions, String mainTitle, String subTitle) { // final mainOption = permissions.firstWhere( // (perm) => perm['title'] == mainTitle, // orElse: () => null, // ); // if (mainOption != null) { // final subOption = mainOption['subOptions'].firstWhere( // (sub) => sub['title'] == subTitle, // orElse: () => null, // ); // return subOption != null && subOption['isChecked'] == true; // } // return false; // } // }