mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2026-03-11 01:41:43 +00:00
Compare commits
11 Commits
Implement_
...
role_permi
| Author | SHA1 | Date | |
|---|---|---|---|
| 1189b52c57 | |||
| c578d63134 | |||
| d72253e3de | |||
| 790479effb | |||
| 827585815b | |||
| de024994c9 | |||
| bcc4ba98ff | |||
| df0f1c6c94 | |||
| 1f62cadcec | |||
| 90812f78e8 | |||
| b1368bf4d7 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,9 +5,11 @@
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.build/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
.swiftpm/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
|
||||
18
assets/icons/offToggleSwitchSmall.svg
Normal file
18
assets/icons/offToggleSwitchSmall.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg width="47" height="37" viewBox="0 0 47 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="6" y="1" width="41" height="25" rx="12.5" fill="#E9E9E9"/>
|
||||
<g filter="url(#filter0_d_6419_4507)">
|
||||
<circle cx="18.5" cy="13.5" r="10.5" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_6419_4507" x="0" y="0" width="37" height="37" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="5"/>
|
||||
<feGaussianBlur stdDeviation="4"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6419_4507"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6419_4507" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 965 B |
18
assets/icons/toggleSwitchSmall.svg
Normal file
18
assets/icons/toggleSwitchSmall.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg width="47" height="37" viewBox="0 0 47 37" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="1" width="41" height="25" rx="12.5" fill="#023DFE" fill-opacity="0.7"/>
|
||||
<g filter="url(#filter0_d_6689_981)">
|
||||
<circle cx="28.5" cy="13.5" r="10.5" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_6689_981" x="10" y="0" width="37" height="37" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="5"/>
|
||||
<feGaussianBlur stdDeviation="4"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6689_981"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6689_981" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 975 B |
@ -329,4 +329,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 4243bd7f9184f79552dd731a7c9d5cad03bd2706
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
COCOAPODS: 1.16.2
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
|
||||
@UIApplicationMain
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
|
||||
@ -7,10 +7,10 @@ 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/user_model.dart';
|
||||
import 'package:syncrow_app/features/dashboard/view/dashboard_view.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';
|
||||
@ -30,23 +30,30 @@ import 'package:syncrow_app/services/api/profile_api.dart';
|
||||
import 'package:syncrow_app/services/api/spaces_api.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<HomeState> {
|
||||
HomeCubit._() : super(HomeInitial()) {
|
||||
// checkIfNotificationPermissionGranted();
|
||||
fetchUserInfo();
|
||||
if (selectedSpace == null) {
|
||||
fetchUnitsByUserId();
|
||||
// .then((value) {
|
||||
// if (selectedSpace != null) {
|
||||
// fetchRoomsByUnitId(selectedSpace!);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
fetchUserInfo().then(
|
||||
(value) {
|
||||
if (selectedSpace == null) {
|
||||
fetchUnitsByUserId();
|
||||
fetchPermissions();
|
||||
|
||||
// .then((value) {
|
||||
// if (selectedSpace != null) {
|
||||
// fetchRoomsByUnitId(selectedSpace!);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
static UserModel? user;
|
||||
|
||||
List<PermissionModel>? permissionModel = [];
|
||||
|
||||
static HomeCubit? _instance;
|
||||
static HomeCubit getInstance() {
|
||||
// If an instance already exists, return it
|
||||
@ -56,15 +63,88 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
Future fetchUserInfo() async {
|
||||
try {
|
||||
emit(HomeLoading());
|
||||
var uuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
user = await ProfileApi().fetchUserInfo(uuid);
|
||||
emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info
|
||||
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<void> 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<PermissionModel> 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) {
|
||||
@ -89,7 +169,7 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
static HomeCubit get(context) => BlocProvider.of(context);
|
||||
|
||||
List<SpaceModel>? spaces;
|
||||
List<SpaceModel> spaces = [];
|
||||
|
||||
SpaceModel? selectedSpace;
|
||||
|
||||
@ -182,8 +262,10 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
if (index == 0) {
|
||||
unselectRoom();
|
||||
} else if (index == 1) {
|
||||
unselectRoom1();
|
||||
} else {
|
||||
selectedRoom = selectedSpace!.subspaces[index - 1];
|
||||
selectedRoom = selectedSpace!.subspaces[index - 2];
|
||||
emitSafe(RoomSelected(selectedRoom!));
|
||||
}
|
||||
}
|
||||
@ -197,8 +279,10 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
if (index <= 0) {
|
||||
unselectRoom();
|
||||
} else if (index == 1) {
|
||||
unselectRoom1();
|
||||
} else {
|
||||
selectedRoom = selectedSpace!.subspaces[index - 1];
|
||||
selectedRoom = selectedSpace!.subspaces[index - 2];
|
||||
emitSafe(RoomSelected(selectedRoom!));
|
||||
}
|
||||
}
|
||||
@ -220,6 +304,23 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
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 {
|
||||
@ -261,15 +362,16 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
emitSafe(GetSpacesLoading());
|
||||
try {
|
||||
spaces = await SpacesAPI.getSpacesByUserId();
|
||||
emitSafe(GetSpacesSuccess(spaces));
|
||||
} catch (failure) {
|
||||
emitSafe(GetSpacesError("No units found"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (spaces != null && spaces!.isNotEmpty) {
|
||||
selectedSpace = spaces!.first;
|
||||
if (spaces.isNotEmpty) {
|
||||
selectedSpace = spaces.first;
|
||||
await fetchRoomsByUnitId(selectedSpace!);
|
||||
emitSafe(GetSpacesSuccess(spaces!));
|
||||
emitSafe(GetSpacesSuccess(spaces));
|
||||
} else {
|
||||
emitSafe(GetSpacesError("No spaces found"));
|
||||
}
|
||||
@ -298,20 +400,21 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
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'];
|
||||
emitSafe(ActivationError(errMessage: errorMessage));
|
||||
return false;
|
||||
} catch (e) {
|
||||
emitSafe(ActivationError(errMessage: e.toString()));
|
||||
errorMsg = e.response?.data['error']['message'];
|
||||
emitSafe(ActivationError(errMessage: errorMsg));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////// Nav ///////////////////////////////////////
|
||||
@ -378,42 +481,44 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
// ),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
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<CreateSceneBloc>()
|
||||
.add(const ClearTaskListEvent());
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<CreateSceneBloc>()
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.none));
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(const SmartSceneClearEvent());
|
||||
BlocProvider.of<EffectPeriodBloc>(
|
||||
NavigationService.navigatorKey.currentState!.context)
|
||||
.add(ResetEffectivePeriod());
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<CreateSceneBloc>()
|
||||
.add(const ClearTabToRunSetting());
|
||||
},
|
||||
),
|
||||
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<CreateSceneBloc>()
|
||||
.add(const ClearTaskListEvent());
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<CreateSceneBloc>()
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.none));
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(const SmartSceneClearEvent());
|
||||
BlocProvider.of<EffectPeriodBloc>(
|
||||
NavigationService.navigatorKey.currentState!.context)
|
||||
.add(ResetEffectivePeriod());
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<CreateSceneBloc>()
|
||||
.add(const ClearTabToRunSetting());
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
// IconButton(
|
||||
// icon: const Icon(
|
||||
// Icons.more_vert,
|
||||
@ -503,3 +608,28 @@ BottomNavigationBarItem defaultBottomNavBarItem(
|
||||
label: label,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// class PermissionUtils {
|
||||
// // Check if the "VIEW" permission exists in "MANAGE_SUBSPACE"
|
||||
// static bool hasViewPermission(List<dynamic> permissions) {
|
||||
// return _hasPermission(permissions, 'MANAGE_SUBSPACE', 'VIEW');
|
||||
// }
|
||||
|
||||
// // Generalized permission checker
|
||||
// static bool _hasPermission(
|
||||
// List<dynamic> 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;
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -15,8 +15,8 @@ class HomeError extends HomeState {
|
||||
|
||||
class HomeSuccess extends HomeState {}
|
||||
|
||||
///specific states
|
||||
//get spaces
|
||||
class ActivationSuccess extends HomeState {}
|
||||
|
||||
class GetSpacesLoading extends HomeLoading {}
|
||||
|
||||
class GetSpacesSuccess extends HomeSuccess {
|
||||
@ -65,9 +65,15 @@ class RoomUnSelected extends HomeState {}
|
||||
|
||||
class NavChangePage extends HomeState {}
|
||||
|
||||
// Define new state classes
|
||||
class HomePermissionUpdated extends HomeState {}
|
||||
|
||||
class HomeUserInfoLoaded extends HomeState {
|
||||
final UserModel user;
|
||||
|
||||
HomeUserInfoLoaded(this.user);
|
||||
}
|
||||
|
||||
class PermissionsRoleLoaded extends HomeState {
|
||||
final List<PermissionModel> permissionModel;
|
||||
PermissionsRoleLoaded(this.permissionModel);
|
||||
}
|
||||
|
||||
39
lib/features/app_layout/model/permission_model.dart
Normal file
39
lib/features/app_layout/model/permission_model.dart
Normal file
@ -0,0 +1,39 @@
|
||||
class PermissionModel {
|
||||
final String title;
|
||||
final List<PermissionAttributes> subOptions;
|
||||
|
||||
PermissionModel({required this.title, required this.subOptions});
|
||||
|
||||
factory PermissionModel.fromJson(Map<String, dynamic> json) {
|
||||
return PermissionModel(
|
||||
title: json['title'],
|
||||
subOptions: (json['subOptions'] as List)
|
||||
.map((e) => PermissionAttributes.fromJson(e))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
static List<PermissionModel> fromJsonList(List<dynamic> jsonList) {
|
||||
return jsonList.map((json) => PermissionModel.fromJson(json)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionAttributes {
|
||||
final String title;
|
||||
final List<PermissionAttributes>? subOptions;
|
||||
final bool? isChecked;
|
||||
|
||||
PermissionAttributes({required this.title, this.subOptions, this.isChecked});
|
||||
|
||||
factory PermissionAttributes.fromJson(Map<String, dynamic> json) {
|
||||
return PermissionAttributes(
|
||||
title: json['title'],
|
||||
isChecked: json['isChecked'],
|
||||
subOptions: json['subOptions'] != null
|
||||
? (json['subOptions'] as List)
|
||||
.map((e) => PermissionAttributes.fromJson(e))
|
||||
.toList()
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,10 @@ class AppLayout extends StatelessWidget {
|
||||
child: BlocBuilder<HomeCubit, HomeState>(
|
||||
builder: (context, state) {
|
||||
return DefaultScaffold(
|
||||
appBar: HomeCubit.getInstance().spaces != null ? const DefaultAppBar() : null,
|
||||
appBar: HomeCubit.getInstance().spaces != null &&
|
||||
HomeCubit.getInstance().spaces!.isNotEmpty
|
||||
? const DefaultAppBar()
|
||||
: null,
|
||||
bottomNavBar: const DefaultNavBar(),
|
||||
child: const AppBody(),
|
||||
);
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/app_layout/model/space_model.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 DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
@ -19,10 +22,23 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
backgroundColor: Colors.transparent,
|
||||
leadingWidth: 200,
|
||||
toolbarHeight: Constants.appBarHeight,
|
||||
leading: HomeCubit.getInstance().spaces!.isNotEmpty
|
||||
? HomeCubit.appBarLeading[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]!
|
||||
leading: InkWell(
|
||||
onTap: () {
|
||||
final spaces = HomeCubit.getInstance().spaces!;
|
||||
showSpaceBottomSheet(context, spaces);
|
||||
},
|
||||
child: HomeCubit.getInstance().spaces!.isNotEmpty
|
||||
? AbsorbPointer(
|
||||
absorbing: true,
|
||||
child: HomeCubit.appBarLeading[HomeCubit
|
||||
.bottomNavItems[HomeCubit.pageIndex].label]!,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
actions: HomeCubit.manageScene
|
||||
? HomeCubit.appBarActions[
|
||||
HomeCubit.bottomNavItems[HomeCubit.pageIndex].label]
|
||||
: null,
|
||||
actions: HomeCubit.appBarActions[HomeCubit.bottomNavItems[HomeCubit.pageIndex].label],
|
||||
));
|
||||
},
|
||||
);
|
||||
@ -31,3 +47,83 @@ class DefaultAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
@override
|
||||
Size get preferredSize => Size.fromHeight(Constants.appBarHeight);
|
||||
}
|
||||
|
||||
void showSpaceBottomSheet(BuildContext context, List<SpaceModel> spaces) {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setModalState) {
|
||||
String? selectedSpaceId = HomeCubit.getInstance().selectedSpace?.id;
|
||||
final bool shouldLimitHeight = spaces.length > 5;
|
||||
return Container(
|
||||
constraints: shouldLimitHeight
|
||||
? BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.5,
|
||||
)
|
||||
: const BoxConstraints(),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: const BoxDecoration(color: Colors.black12),
|
||||
height: 5,
|
||||
width: 50,
|
||||
),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: spaces.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final space = spaces[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 30, right: 30),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(space.name),
|
||||
],
|
||||
),
|
||||
Radio<String>(
|
||||
value: space.id,
|
||||
groupValue: selectedSpaceId,
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue != null) {
|
||||
setModalState(() {
|
||||
selectedSpaceId = newValue;
|
||||
});
|
||||
HomeCubit.getInstance().changeSelectedSpace(
|
||||
spaces.firstWhere((s) => s.id == newValue),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
child: const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 0.5,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -221,6 +221,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
List<String> userFullName = fullName.split(' ');
|
||||
response = await AuthenticationAPI.signUp(
|
||||
model: SignUpModel(
|
||||
hasAcceptedAppAgreement: true,
|
||||
email: email.toLowerCase(),
|
||||
password: signUpPassword,
|
||||
firstName: userFullName[0],
|
||||
|
||||
@ -3,10 +3,12 @@ class SignUpModel {
|
||||
final String password;
|
||||
final String firstName;
|
||||
final String lastName;
|
||||
final bool hasAcceptedAppAgreement;
|
||||
|
||||
SignUpModel(
|
||||
{required this.email,
|
||||
required this.password,
|
||||
required this.hasAcceptedAppAgreement,
|
||||
required this.firstName,
|
||||
required this.lastName});
|
||||
|
||||
@ -15,7 +17,8 @@ class SignUpModel {
|
||||
email: json['email'],
|
||||
password: json['password'],
|
||||
firstName: json['firstName'],
|
||||
lastName: json['lastName']);
|
||||
lastName: json['lastName'],
|
||||
hasAcceptedAppAgreement: true);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -24,6 +27,7 @@ class SignUpModel {
|
||||
'password': password,
|
||||
'firstName': firstName,
|
||||
'lastName': lastName,
|
||||
"hasAcceptedAppAgreement": hasAcceptedAppAgreement
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,11 @@ class UserModel {
|
||||
final String? timeZone;
|
||||
final String? regionUuid;
|
||||
final bool? isAgreementAccepted;
|
||||
final bool? hasAcceptedWebAgreement;
|
||||
final DateTime? webAgreementAcceptedAt;
|
||||
final bool? hasAcceptedAppAgreement;
|
||||
final DateTime? appAgreementAcceptedAt;
|
||||
final Role? role;
|
||||
|
||||
UserModel({
|
||||
required this.uuid,
|
||||
@ -28,7 +33,11 @@ class UserModel {
|
||||
required this.isAgreementAccepted,
|
||||
required this.regionName,
|
||||
required this.timeZone,
|
||||
// required this.role,
|
||||
required this.hasAcceptedWebAgreement,
|
||||
required this.webAgreementAcceptedAt,
|
||||
required this.hasAcceptedAppAgreement,
|
||||
required this.appAgreementAcceptedAt,
|
||||
required this.role,
|
||||
});
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -44,6 +53,15 @@ class UserModel {
|
||||
regionName: json['region']?['regionName'],
|
||||
timeZone: json['timeZone']?['timeZoneOffset'],
|
||||
regionUuid: json['region']?['uuid'],
|
||||
hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
|
||||
webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(json['webAgreementAcceptedAt'])
|
||||
: null,
|
||||
hasAcceptedAppAgreement: json['hasAcceptedAppAgreement'],
|
||||
appAgreementAcceptedAt: json['appAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(json['appAgreementAcceptedAt'])
|
||||
: null,
|
||||
role: json['role'] != null ? Role.fromJson(json['role']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,6 +79,15 @@ class UserModel {
|
||||
regionUuid: null,
|
||||
regionName: tempJson['region']?['regionName'],
|
||||
timeZone: tempJson['timezone']?['timeZoneOffset'],
|
||||
hasAcceptedWebAgreement: tempJson['hasAcceptedWebAgreement'],
|
||||
webAgreementAcceptedAt: tempJson['webAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['webAgreementAcceptedAt'])
|
||||
: null,
|
||||
hasAcceptedAppAgreement: tempJson['hasAcceptedAppAgreement'],
|
||||
appAgreementAcceptedAt: tempJson['appAgreementAcceptedAt'] != null
|
||||
? DateTime.parse(tempJson['appAgreementAcceptedAt'])
|
||||
: null,
|
||||
role: tempJson['role'] != null ? Role.fromJson(tempJson['role']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@ -85,6 +112,43 @@ class UserModel {
|
||||
'isAgreementAccepted': isAgreementAccepted,
|
||||
'regionName': regionName,
|
||||
'timeZone': timeZone,
|
||||
'hasAcceptedWebAgreement': hasAcceptedWebAgreement,
|
||||
'webAgreementAcceptedAt': webAgreementAcceptedAt?.toIso8601String(),
|
||||
'hasAcceptedAppAgreement': hasAcceptedAppAgreement,
|
||||
'appAgreementAcceptedAt': appAgreementAcceptedAt?.toIso8601String(),
|
||||
'role': role?.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Role {
|
||||
final String? uuid;
|
||||
final DateTime? createdAt;
|
||||
final DateTime? updatedAt;
|
||||
final String? type;
|
||||
|
||||
Role({
|
||||
required this.uuid,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
factory Role.fromJson(Map<String, dynamic> json) {
|
||||
return Role(
|
||||
uuid: json['uuid'],
|
||||
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt']) : null,
|
||||
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt']) : null,
|
||||
type: json['type'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'createdAt': createdAt?.toIso8601String(),
|
||||
'updatedAt': updatedAt?.toIso8601String(),
|
||||
'type': type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,12 +3,15 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/user_agreement.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/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
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/constants.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/styles_manager.dart';
|
||||
@ -202,6 +205,93 @@ class SignUpView extends StatelessWidget {
|
||||
hint: "At least 8 characters"),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text:
|
||||
'By signing up you agree to our ',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
fontSize: 16,
|
||||
color: ColorsManager
|
||||
.onPrimaryColor,
|
||||
),
|
||||
children: [
|
||||
WidgetSpan(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const UserAgreement(),
|
||||
));
|
||||
},
|
||||
child: BodyMedium(
|
||||
text: 'Terms & Conditions',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
decoration:
|
||||
TextDecoration
|
||||
.underline,
|
||||
decorationColor:
|
||||
ColorsManager
|
||||
.onPrimaryColor,
|
||||
color: ColorsManager
|
||||
.onPrimaryColor,
|
||||
fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' and ',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
fontSize: 16,
|
||||
color: ColorsManager
|
||||
.onPrimaryColor,
|
||||
)),
|
||||
WidgetSpan(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const PrivacyPolicy(),
|
||||
));
|
||||
},
|
||||
child: BodyMedium(
|
||||
text: 'Privacy Policy',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
decoration:
|
||||
TextDecoration
|
||||
.underline,
|
||||
decorationColor:
|
||||
Colors.white,
|
||||
color: ColorsManager
|
||||
.onPrimaryColor,
|
||||
fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.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_control_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
@ -135,6 +136,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
ac.acSwitch = acSwitchValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_setAllAcsTempsAndSwitches();
|
||||
_emitAcsStatus(emit);
|
||||
} else {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -112,7 +114,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
code: 'presence_state',
|
||||
);
|
||||
recordGroups = response;
|
||||
// print('---${recordGroups.data!.first.eventTime}');
|
||||
// print('---${jsonEncode(recordGroups.data.first.)}');
|
||||
emit(FitchData());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
|
||||
@ -31,6 +31,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
// Fetch groups based on the selected space ID
|
||||
await fetchGroups(selectedSpace.id);
|
||||
await fetchAllDevices(selectedSpace);
|
||||
}
|
||||
|
||||
DevicesCubit._() : super(DevicesInitial()) {
|
||||
@ -47,6 +48,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
return _instance ??= DevicesCubit._();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_instance = null;
|
||||
@ -109,17 +111,6 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
}
|
||||
|
||||
// Getter to retrieve all devices from HomeCubit
|
||||
List<DeviceModel> get allDevices {
|
||||
List<DeviceModel> devices = [];
|
||||
if (HomeCubit.getInstance().selectedSpace != null) {
|
||||
for (var room in HomeCubit.getInstance().selectedSpace!.subspaces) {
|
||||
if (room.devices != null) {
|
||||
devices.addAll(room.devices!);
|
||||
}
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
// DevicesCategoryModel? get chosenCategory {
|
||||
// for (var category in allCategories!) {
|
||||
@ -267,36 +258,36 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
}
|
||||
|
||||
///////////////////////// API CALLS //////////////////////////
|
||||
deviceControl(DeviceControlModel control, String deviceId) async {
|
||||
emitSafe(DeviceControlLoading(
|
||||
code: control.code,
|
||||
));
|
||||
try {
|
||||
var response = await DevicesAPI.controlDevice(control, deviceId);
|
||||
// deviceControl(DeviceControlModel control, String deviceId) async {
|
||||
// emitSafe(DeviceControlLoading(
|
||||
// code: control.code,
|
||||
// ));
|
||||
// try {
|
||||
// var response = await DevicesAPI.controlDevice(control, deviceId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
emitSafe(DeviceControlSuccess(code: control.code));
|
||||
//this delay is to give tuya server time to update the status
|
||||
// Future.delayed(const Duration(milliseconds: 400), () {
|
||||
fetchDevicesStatues(
|
||||
deviceId,
|
||||
HomeCubit.getInstance()
|
||||
.selectedSpace!
|
||||
.subspaces
|
||||
.indexOf(HomeCubit.getInstance().selectedRoom!),
|
||||
code: control.code);
|
||||
// });
|
||||
} else {
|
||||
emitSafe(DeviceControlError('Failed to control the device'));
|
||||
}
|
||||
} catch (failure) {
|
||||
emitSafe(DeviceControlError(failure.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// if (response['success'] ?? false) {
|
||||
// emitSafe(DeviceControlSuccess(code: control.code));
|
||||
// //this delay is to give tuya server time to update the status
|
||||
// // Future.delayed(const Duration(milliseconds: 400), () {
|
||||
// fetchDevicesStatues(
|
||||
// deviceId,
|
||||
// HomeCubit.getInstance()
|
||||
// .selectedSpace!
|
||||
// .subspaces
|
||||
// .indexOf(HomeCubit.getInstance().selectedRoom!),
|
||||
// code: control.code);
|
||||
// // });
|
||||
// } else {
|
||||
// emitSafe(DeviceControlError('Failed to control the device'));
|
||||
// }
|
||||
// } catch (failure) {
|
||||
// emitSafe(DeviceControlError(failure.toString()));
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
fetchGroups(String spaceId) async {
|
||||
emitSafe(DevicesCategoriesLoading());
|
||||
emitSafe(GetDevicesLoading());
|
||||
try {
|
||||
allCategories = await DevicesAPI.fetchGroups(spaceId);
|
||||
} catch (e) {
|
||||
@ -321,12 +312,15 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
try {
|
||||
HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices =
|
||||
await DevicesAPI.getDevicesByRoomId(
|
||||
communityUuid: unit!.community.uuid, spaceUuid: unit.id, roomId: roomId);
|
||||
communityUuid: unit!.community.uuid,
|
||||
spaceUuid: unit.id,
|
||||
roomId: roomId);
|
||||
} catch (e) {
|
||||
emitSafe(GetDevicesError(e.toString()));
|
||||
return;
|
||||
}
|
||||
final devices = HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices;
|
||||
final devices =
|
||||
HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices;
|
||||
emitSafe(GetDevicesSuccess(devices));
|
||||
|
||||
//get status for each device
|
||||
@ -356,8 +350,11 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
emitSafe(GetDeviceStatusError(e.toString()));
|
||||
return;
|
||||
}
|
||||
HomeCubit.getInstance().selectedSpace!.subspaces[roomIndex].devices![deviceIndex].status =
|
||||
statuses;
|
||||
HomeCubit.getInstance()
|
||||
.selectedSpace!
|
||||
.subspaces[roomIndex]
|
||||
.devices![deviceIndex]
|
||||
.status = statuses;
|
||||
emitSafe(GetDeviceStatusSuccess(code: code));
|
||||
}
|
||||
|
||||
@ -406,6 +403,345 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
// emitSafe(LightBrightnessChanged(value));
|
||||
// }
|
||||
// }
|
||||
// List<DeviceModel> _fetchedDevices = [];
|
||||
|
||||
// List<DeviceModel> get allDevices => _fetchedDevices;
|
||||
List<DeviceModel> allDevices = [];
|
||||
|
||||
Future<void> fetchAllDevices(SpaceModel? unit) async {
|
||||
emitSafe(GetDevicesLoading());
|
||||
try {
|
||||
final devices = await DevicesAPI.getAllDevices(
|
||||
communityUuid: unit!.community.uuid,
|
||||
spaceUuid: unit.id,
|
||||
);
|
||||
allDevices = devices;
|
||||
emitSafe(GetDevicesSuccess(allDevices));
|
||||
} catch (e) {
|
||||
emitSafe(GetDevicesError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
bool isDeviceOn(DeviceModel device) {
|
||||
final switchStatuses = device.status.where(
|
||||
(s) => (s.code?.startsWith('switch') ?? false),
|
||||
);
|
||||
final anySwitchFalse = switchStatuses.any((s) => s.value == false);
|
||||
return !anySwitchFalse;
|
||||
}
|
||||
|
||||
void updateDeviceStatus(String deviceId, bool newToggleStatus) {
|
||||
emitSafe(GetDevicesLoading());
|
||||
|
||||
// Create a fresh copy of the device list.
|
||||
final updatedDevices = List<DeviceModel>.from(allDevices);
|
||||
|
||||
for (int i = 0; i < updatedDevices.length; i++) {
|
||||
final device = updatedDevices[i];
|
||||
if (device.uuid == deviceId) {
|
||||
updatedDevices[i] = DeviceModel(
|
||||
activeTime: device.activeTime,
|
||||
localKey: device.localKey,
|
||||
model: device.model,
|
||||
name: device.name,
|
||||
icon: device.icon,
|
||||
categoryName: device.categoryName,
|
||||
type: device.type,
|
||||
isOnline: device.isOnline,
|
||||
status: device.status, // Make sure to keep the same status list
|
||||
productName: device.productName,
|
||||
timeZone: device.timeZone,
|
||||
updateTime: device.updateTime,
|
||||
uuid: device.uuid,
|
||||
productUuid: device.productUuid,
|
||||
productType: device.productType,
|
||||
subspace: device.subspace,
|
||||
toggleStatus: newToggleStatus,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit(GetDevicesSuccess(updatedDevices));
|
||||
}
|
||||
|
||||
Future<void> threeGangToggle(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
final switches = ['switch_1', 'switch_2', 'switch_3'];
|
||||
for (final switchCode in switches) {
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == switchCode);
|
||||
if (statusIndex != -1) {
|
||||
final currentValue = device.status[statusIndex].value ?? false;
|
||||
final toggledValue = !currentValue;
|
||||
final controlRequest = DeviceControlModel(
|
||||
code: switchCode, value: toggledValue, deviceId: deviceUuid);
|
||||
final response =
|
||||
await DevicesAPI.controlDevice(controlRequest, deviceUuid);
|
||||
if (response['success'] != true) {
|
||||
throw Exception('Failed to toggle $switchCode');
|
||||
}
|
||||
device.status[statusIndex].value = toggledValue;
|
||||
}
|
||||
}
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> towGangToggle(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
final switches = [
|
||||
'switch_1',
|
||||
'switch_2',
|
||||
];
|
||||
for (final switchCode in switches) {
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == switchCode);
|
||||
if (statusIndex != -1) {
|
||||
final currentValue = device.status[statusIndex].value ?? false;
|
||||
final toggledValue = !currentValue;
|
||||
final controlRequest = DeviceControlModel(
|
||||
code: switchCode, value: toggledValue, deviceId: deviceUuid);
|
||||
final response =
|
||||
await DevicesAPI.controlDevice(controlRequest, deviceUuid);
|
||||
|
||||
device.status[statusIndex].value = response['result'];
|
||||
}
|
||||
}
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> towGTGangToggle(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
final switches = [
|
||||
'switch_1',
|
||||
'switch_2',
|
||||
];
|
||||
for (final switchCode in switches) {
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == switchCode);
|
||||
if (statusIndex != -1) {
|
||||
final currentValue = device.status[statusIndex].value ?? false;
|
||||
final toggledValue = !currentValue;
|
||||
final controlRequest = DeviceControlModel(
|
||||
code: switchCode, value: toggledValue, deviceId: deviceUuid);
|
||||
final response =
|
||||
await DevicesAPI.controlDevice(controlRequest, deviceUuid);
|
||||
|
||||
device.status[statusIndex].value = response['result'];
|
||||
}
|
||||
}
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> oneGangToggle(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
|
||||
final statusIndex = device.status.indexWhere((s) => s.code == 'switch_1');
|
||||
if (statusIndex != -1) {
|
||||
final currentValue = device.status[statusIndex].value ?? false;
|
||||
final toggledValue = !currentValue;
|
||||
final controlRequest = DeviceControlModel(
|
||||
code: 'switch_1', value: toggledValue, deviceId: deviceUuid);
|
||||
final response =
|
||||
await DevicesAPI.controlDevice(controlRequest, deviceUuid);
|
||||
print(response);
|
||||
if (response['result'] != true) {
|
||||
throw Exception('Failed to toggle switch_1');
|
||||
}
|
||||
device.status[statusIndex].value = response['result'];
|
||||
}
|
||||
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> oneGTGangToggle(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
|
||||
final statusIndex = device.status.indexWhere((s) => s.code == 'switch_1');
|
||||
if (statusIndex != -1) {
|
||||
final currentValue = device.status[statusIndex].value ?? false;
|
||||
final toggledValue = !currentValue;
|
||||
final controlRequest = DeviceControlModel(
|
||||
code: 'switch_1', value: toggledValue, deviceId: deviceUuid);
|
||||
final response =
|
||||
await DevicesAPI.controlDevice(controlRequest, deviceUuid);
|
||||
if (response['result'] != true) {
|
||||
throw Exception('Failed to toggle switch_1');
|
||||
}
|
||||
device.status[statusIndex].value = response['result'];
|
||||
}
|
||||
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) => (s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deviceControl(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final response = await DevicesAPI.controlDevice(control, deviceUuid);
|
||||
if (response['success'] == true) {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex != -1) {
|
||||
final device = allDevices[deviceIndex];
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == control.code);
|
||||
if (statusIndex != -1) {
|
||||
device.status[statusIndex].value = control.value;
|
||||
}
|
||||
final anySwitchOff = device.status.any(
|
||||
(s) =>
|
||||
(s.code?.startsWith('switch_') ?? false) && (s.value == false),
|
||||
);
|
||||
device.toggleStatus = !anySwitchOff;
|
||||
allDevices[deviceIndex] = device;
|
||||
}
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} else {
|
||||
emit(DeviceControlError('Failed to control the device'));
|
||||
}
|
||||
} catch (failure) {
|
||||
emit(DeviceControlError(failure.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeCurtainSwitch(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
final isOpen = control.value == "open";
|
||||
final newValue = isOpen ? 0 : 100;
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
code: 'percent_control',
|
||||
devicesUuid: [deviceUuid],
|
||||
value: newValue,
|
||||
);
|
||||
if (response['success'] == true) {
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == 'percent_control');
|
||||
if (statusIndex != -1) {
|
||||
device.status[statusIndex].value = newValue;
|
||||
}
|
||||
allDevices[deviceIndex] = device;
|
||||
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} else {
|
||||
emit(DeviceControlError('Failed to toggle curtain.'));
|
||||
}
|
||||
} catch (error) {
|
||||
emit(DeviceControlError(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changeGarageSwitch(
|
||||
DeviceControlModel control, String deviceUuid) async {
|
||||
emit(SwitchControlLoading(code: control.code));
|
||||
try {
|
||||
final deviceIndex = allDevices.indexWhere((d) => d.uuid == deviceUuid);
|
||||
if (deviceIndex == -1) {
|
||||
throw Exception('Device not found');
|
||||
}
|
||||
final device = allDevices[deviceIndex];
|
||||
final isOpen = control.value == "open";
|
||||
final newValue = isOpen ? 0 : 100;
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
code: 'percent_control',
|
||||
devicesUuid: [deviceUuid],
|
||||
value: newValue,
|
||||
);
|
||||
if (response['success'] == true) {
|
||||
final statusIndex =
|
||||
device.status.indexWhere((s) => s.code == 'percent_control');
|
||||
if (statusIndex != -1) {
|
||||
device.status[statusIndex].value = newValue;
|
||||
}
|
||||
allDevices[deviceIndex] = device;
|
||||
emit(DeviceControlSuccess(code: control.code));
|
||||
} else {
|
||||
emit(DeviceControlError('Failed to toggle curtain.'));
|
||||
}
|
||||
} catch (error) {
|
||||
emit(DeviceControlError(error.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum LightMode {
|
||||
|
||||
@ -34,6 +34,7 @@ class GetDeviceStatusError extends DevicesState {
|
||||
}
|
||||
|
||||
class GetDevicesLoading extends DevicesState {}
|
||||
class RoomLoading extends DevicesState {}
|
||||
|
||||
class GetDevicesSuccess extends DevicesState {
|
||||
GetDevicesSuccess(this.devices);
|
||||
@ -55,10 +56,16 @@ class DeviceSwitchChanged extends DevicesState {}
|
||||
class DeviceSelected extends DevicesState {}
|
||||
|
||||
// Device Control
|
||||
class DeviceControlLoading extends DevicesState {
|
||||
final String? code;
|
||||
// class DeviceControlLoading extends DevicesState {
|
||||
// final String? code;
|
||||
|
||||
DeviceControlLoading({this.code});
|
||||
// DeviceControlLoading({this.code});
|
||||
// }
|
||||
class SwitchControlLoading extends DevicesState {
|
||||
final String? code;
|
||||
final String? deviceId;
|
||||
|
||||
SwitchControlLoading({this.deviceId, this.code});
|
||||
}
|
||||
|
||||
class DeviceControlSuccess extends DevicesState {
|
||||
|
||||
@ -40,7 +40,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
bool createSchedule = false;
|
||||
List<ScheduleModel> listSchedule = [];
|
||||
|
||||
TwoTouchBloc({required this.twoTouchId, required this.switchCode}) : super(InitialState()) {
|
||||
TwoTouchBloc({required this.twoTouchId, required this.switchCode})
|
||||
: super(InitialState()) {
|
||||
on<InitialEvent>(_fetchTwoTouchStatus);
|
||||
on<TwoTouchUpdated>(_twoTouchUpdated);
|
||||
on<ChangeFirstSwitchStatusEvent>(_changeFirstSwitch);
|
||||
@ -71,13 +72,15 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
|
||||
int selectedTabIndex = 0;
|
||||
|
||||
void toggleSelectedIndex(ToggleSelectedEvent event, Emitter<TwoTouchState> emit) {
|
||||
void toggleSelectedIndex(
|
||||
ToggleSelectedEvent event, Emitter<TwoTouchState> emit) {
|
||||
emit(LoadingInitialState());
|
||||
selectedTabIndex = event.index;
|
||||
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
|
||||
}
|
||||
|
||||
void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter<TwoTouchState> emit) {
|
||||
void toggleCreateSchedule(
|
||||
ToggleCreateScheduleEvent event, Emitter<TwoTouchState> emit) {
|
||||
emit(LoadingInitialState());
|
||||
createSchedule = !createSchedule;
|
||||
selectedDays.clear();
|
||||
@ -85,7 +88,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
emit(UpdateCreateScheduleState(createSchedule));
|
||||
}
|
||||
|
||||
void _fetchTwoTouchStatus(InitialEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _fetchTwoTouchStatus(
|
||||
InitialEvent event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(twoTouchId);
|
||||
@ -104,18 +108,21 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$twoTouchId');
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$twoTouchId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
|
||||
stream.listen((DatabaseEvent event) async {
|
||||
if (_timer != null) {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<StatusModel> statusList = [];
|
||||
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList.add(StatusModel(code: element['code'], value: element['value']));
|
||||
statusList
|
||||
.add(StatusModel(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = TwoTouchModel.fromJson(statusList);
|
||||
@ -130,7 +137,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
emit(UpdateState(twoTouchModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeFirstSwitch(ChangeFirstSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _changeFirstSwitch(
|
||||
ChangeFirstSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingNewSate(twoTouchModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.firstSwitch = !event.value;
|
||||
@ -141,7 +149,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
|
||||
_timer = Timer(const Duration(milliseconds: 100), () async {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: twoTouchId, code: 'switch_1', value: !event.value),
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_1', value: !event.value),
|
||||
twoTouchId);
|
||||
|
||||
if (!response['success']) {
|
||||
@ -153,7 +162,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSecondSwitch(ChangeSecondSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _changeSecondSwitch(
|
||||
ChangeSecondSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingNewSate(twoTouchModel: deviceStatus));
|
||||
try {
|
||||
deviceStatus.secondSwitch = !event.value;
|
||||
@ -163,7 +173,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
_timer = Timer(const Duration(milliseconds: 100), () async {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: twoTouchId, code: 'switch_2', value: !event.value),
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_2', value: !event.value),
|
||||
twoTouchId);
|
||||
|
||||
if (!response['success']) {
|
||||
@ -186,11 +197,15 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_1', value: deviceStatus.firstSwitch),
|
||||
deviceId: twoTouchId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoTouchId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_2', value: deviceStatus.secondSwitch),
|
||||
deviceId: twoTouchId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoTouchId),
|
||||
]);
|
||||
|
||||
@ -213,11 +228,15 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
final response = await Future.wait([
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_1', value: deviceStatus.firstSwitch),
|
||||
deviceId: twoTouchId,
|
||||
code: 'switch_1',
|
||||
value: deviceStatus.firstSwitch),
|
||||
twoTouchId),
|
||||
DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: 'switch_2', value: deviceStatus.secondSwitch),
|
||||
deviceId: twoTouchId,
|
||||
code: 'switch_2',
|
||||
value: deviceStatus.secondSwitch),
|
||||
twoTouchId),
|
||||
]);
|
||||
if (response.every((element) => !element['success'])) {
|
||||
@ -237,8 +256,10 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
groupTwoTouchList[i].firstSwitch = true;
|
||||
groupTwoTouchList[i].secondSwitch = true;
|
||||
}
|
||||
emit(UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: true));
|
||||
List<String> allDeviceIds = groupTwoTouchList.map((device) => device.deviceId).toList();
|
||||
emit(
|
||||
UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: true));
|
||||
List<String> allDeviceIds =
|
||||
groupTwoTouchList.map((device) => device.deviceId).toList();
|
||||
|
||||
final response1 = await DevicesAPI.deviceBatchController(
|
||||
code: 'switch_1',
|
||||
@ -271,9 +292,11 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
groupTwoTouchList[i].secondSwitch = false;
|
||||
}
|
||||
|
||||
emit(UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: false));
|
||||
emit(UpdateGroupState(
|
||||
twoTouchList: groupTwoTouchList, allSwitches: false));
|
||||
|
||||
List<String> allDeviceIds = groupTwoTouchList.map((device) => device.deviceId).toList();
|
||||
List<String> allDeviceIds =
|
||||
groupTwoTouchList.map((device) => device.deviceId).toList();
|
||||
|
||||
final response1 = await DevicesAPI.deviceBatchController(
|
||||
code: 'switch_1',
|
||||
@ -298,17 +321,20 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSliding(ChangeSlidingSegment event, Emitter<TwoTouchState> emit) async {
|
||||
void _changeSliding(
|
||||
ChangeSlidingSegment event, Emitter<TwoTouchState> emit) async {
|
||||
emit(ChangeSlidingSegmentState(value: event.value));
|
||||
}
|
||||
|
||||
void _setCounterValue(SetCounterValue event, Emitter<TwoTouchState> emit) async {
|
||||
void _setCounterValue(
|
||||
SetCounterValue event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingNewSate(twoTouchModel: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: twoTouchId, code: event.deviceCode, value: seconds),
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId, code: event.deviceCode, value: seconds),
|
||||
twoTouchId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
@ -333,7 +359,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(GetCounterEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _getCounterValue(
|
||||
GetCounterEvent event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
add(GetScheduleEvent());
|
||||
@ -441,7 +468,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
deviceId: twoTouchId,
|
||||
);
|
||||
List<dynamic> jsonData = response;
|
||||
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
listSchedule =
|
||||
jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
emit(InitialState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
@ -452,12 +480,13 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
|
||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
DateTime dateTimeWithoutSeconds =
|
||||
DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute);
|
||||
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
|
||||
dateTime.day, dateTime.hour, dateTime.minute);
|
||||
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
Future toggleRepeat(ToggleScheduleEvent event, Emitter<TwoTouchState> emit) async {
|
||||
Future toggleRepeat(
|
||||
ToggleScheduleEvent event, Emitter<TwoTouchState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.changeSchedule(
|
||||
@ -476,7 +505,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteSchedule(DeleteScheduleEvent event, Emitter<TwoTouchState> emit) async {
|
||||
Future deleteSchedule(
|
||||
DeleteScheduleEvent event, Emitter<TwoTouchState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final response = await DevicesAPI.deleteSchedule(
|
||||
@ -496,7 +526,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _fetchTwoTouchWizardStatus(InitialWizardEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _fetchTwoTouchWizardStatus(
|
||||
InitialWizardEvent event, Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
devicesList = [];
|
||||
@ -506,7 +537,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
HomeCubit.getInstance().selectedSpace?.id ?? '', '2GT');
|
||||
|
||||
for (int i = 0; i < devicesList.length; i++) {
|
||||
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
var response =
|
||||
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
@ -529,15 +561,16 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
emit(UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: allSwitchesOn));
|
||||
emit(UpdateGroupState(
|
||||
twoTouchList: groupTwoTouchList, allSwitches: allSwitchesOn));
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _changeFirstWizardSwitch(
|
||||
ChangeFirstWizardSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event,
|
||||
Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingNewSate(twoTouchModel: deviceStatus));
|
||||
try {
|
||||
bool allSwitchesValue = true;
|
||||
@ -550,7 +583,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
});
|
||||
|
||||
emit(UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue));
|
||||
emit(UpdateGroupState(
|
||||
twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue));
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
code: 'switch_1',
|
||||
devicesUuid: [event.deviceId],
|
||||
@ -565,8 +599,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _changeSecondWizardSwitch(
|
||||
ChangeSecondWizardSwitchStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
void _changeSecondWizardSwitch(ChangeSecondWizardSwitchStatusEvent event,
|
||||
Emitter<TwoTouchState> emit) async {
|
||||
emit(LoadingNewSate(twoTouchModel: deviceStatus));
|
||||
try {
|
||||
bool allSwitchesValue = true;
|
||||
@ -579,7 +613,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
}
|
||||
});
|
||||
|
||||
emit(UpdateGroupState(twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue));
|
||||
emit(UpdateGroupState(
|
||||
twoTouchList: groupTwoTouchList, allSwitches: allSwitchesValue));
|
||||
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
code: 'switch_2',
|
||||
@ -598,7 +633,8 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
String statusSelected = '';
|
||||
String optionSelected = '';
|
||||
|
||||
Future<void> _changeStatus(ChangeStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
Future<void> _changeStatus(
|
||||
ChangeStatusEvent event, Emitter<TwoTouchState> emit) async {
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
final Map<String, Map<String, String>> controlMap = {
|
||||
@ -627,11 +663,15 @@ class TwoTouchBloc extends Bloc<TwoTouchEvent, TwoTouchState> {
|
||||
final selectedControl = controlMap[optionSelected]?[statusSelected];
|
||||
if (selectedControl != null) {
|
||||
await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: twoTouchId, code: optionSelected, value: selectedControl),
|
||||
DeviceControlModel(
|
||||
deviceId: twoTouchId,
|
||||
code: optionSelected,
|
||||
value: selectedControl),
|
||||
twoTouchId,
|
||||
);
|
||||
} else {
|
||||
emit(const FailedState(error: 'Invalid statusSelected or optionSelected'));
|
||||
emit(const FailedState(
|
||||
error: 'Invalid statusSelected or optionSelected'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
|
||||
@ -18,6 +18,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
on<ChangeIndicatorEvent>(_changeIndicator);
|
||||
on<ChangeValueEvent>(_changeValue);
|
||||
on<WallSensorUpdatedEvent>(_wallSensorUpdated);
|
||||
on<GetDeviceReportsEvent>(_getDeviceReports);
|
||||
}
|
||||
|
||||
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<WallSensorState> emit) async {
|
||||
@ -91,4 +92,17 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
} catch (_) {}
|
||||
emit(UpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _getDeviceReports(GetDeviceReportsEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
|
||||
try {
|
||||
await DevicesAPI.getDeviceReports(deviceId, event.code).then((value) {
|
||||
emit(DeviceReportsState(deviceReport: value, code: event.code));
|
||||
});
|
||||
} catch (e) {
|
||||
emit(FailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,3 +29,15 @@ class ChangeValueEvent extends WallSensorEvent {
|
||||
@override
|
||||
List<Object> get props => [value, code];
|
||||
}
|
||||
|
||||
class GetDeviceReportsEvent extends WallSensorEvent {
|
||||
final String deviceUuid;
|
||||
final String code;
|
||||
const GetDeviceReportsEvent({
|
||||
required this.deviceUuid,
|
||||
required this.code,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceUuid, code];
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/wall_sensor_model.dart';
|
||||
|
||||
class WallSensorState extends Equatable {
|
||||
@ -36,3 +37,9 @@ class FailedState extends WallSensorState {
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class DeviceReportsState extends WallSensorState {
|
||||
final DeviceReport deviceReport;
|
||||
final String code;
|
||||
const DeviceReportsState({required this.deviceReport, required this.code});
|
||||
}
|
||||
|
||||
@ -10,6 +10,9 @@ class DeviceModel {
|
||||
String? model;
|
||||
String? name;
|
||||
String? icon;
|
||||
String? categoryName;
|
||||
bool? toggleStatus = false;
|
||||
|
||||
String? type;
|
||||
bool? isOnline;
|
||||
List<StatusModel> status = [];
|
||||
@ -21,6 +24,8 @@ class DeviceModel {
|
||||
DeviceType? productType;
|
||||
bool isSelected = false;
|
||||
late List<FunctionModel> functions;
|
||||
DeviceSubspace? subspace;
|
||||
|
||||
DeviceModel(
|
||||
{this.activeTime,
|
||||
this.productUuid,
|
||||
@ -34,6 +39,9 @@ class DeviceModel {
|
||||
this.updateTime,
|
||||
this.uuid,
|
||||
this.productType,
|
||||
this.categoryName,
|
||||
this.subspace,
|
||||
this.toggleStatus,
|
||||
this.icon,
|
||||
this.type}) {
|
||||
functions = getFunctions(productType!);
|
||||
@ -87,23 +95,74 @@ class DeviceModel {
|
||||
} else {
|
||||
tempIcon = Assets.assetsIconsLogo;
|
||||
}
|
||||
// Step 1: Parse `status` as before
|
||||
final statusList = (json['status'] as List<dynamic>?)
|
||||
?.map((st) => StatusModel.fromJson(st as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
// Step 2: Check whether ANY status means "off"
|
||||
final anyOff = statusList?.any((s) {
|
||||
final code = s.code;
|
||||
if (code == null) return false;
|
||||
|
||||
// 1) Handle "switch" or "switch_x"
|
||||
if (code == 'switch' || code.startsWith('switch_')) {
|
||||
// If it's false, we consider that "off"
|
||||
return s.value == false;
|
||||
}
|
||||
|
||||
// 2) If code == "control" and value == "stop", consider "off"
|
||||
if (s.value == 'open') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3) If code == "percent_control" and value == 0, maybe "off"
|
||||
if (code == 'percent_control' && s.value == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add more conditions for other codes as needed
|
||||
|
||||
// Default: if none of the above apply, it's not "off"
|
||||
return false;
|
||||
});
|
||||
|
||||
// Step 3: Decide final toggleStatus (true = fully "on", false = partially/fully "off")
|
||||
bool computedToggleStatus = !(anyOff ?? false);
|
||||
|
||||
return DeviceModel(
|
||||
icon: tempIcon,
|
||||
activeTime: json['activeTime'],
|
||||
// id: json['id'],
|
||||
localKey: json['localKey'],
|
||||
model: json['model'],
|
||||
name: json['name'],
|
||||
isOnline: json['online'],
|
||||
productName: json['productName'],
|
||||
timeZone: json['timeZone'],
|
||||
updateTime: json['updateTime'],
|
||||
uuid: json['uuid'],
|
||||
productType: type,
|
||||
type: json['productType'],
|
||||
status: [],
|
||||
productUuid: json['productUuid']);
|
||||
icon: tempIcon,
|
||||
activeTime: json['activeTime'],
|
||||
categoryName: json['categoryName'],
|
||||
localKey: json['localKey'],
|
||||
model: json['model'],
|
||||
name: json['name'],
|
||||
isOnline: json['online'],
|
||||
productName: json['productName'],
|
||||
timeZone: json['timeZone'],
|
||||
updateTime: json['updateTime'],
|
||||
uuid: json['uuid'],
|
||||
productType: type,
|
||||
type: json['productType'],
|
||||
|
||||
// Use the newly computed toggleStatus:
|
||||
toggleStatus: computedToggleStatus,
|
||||
|
||||
// Or if you prefer to use the value returned by the backend when available:
|
||||
// toggleStatus: json['toggleStatus'] ?? computedToggleStatus,
|
||||
|
||||
status: statusList ?? [],
|
||||
subspace: json['subspace'] != null
|
||||
? DeviceSubspace.fromJson(json['subspace'])
|
||||
: DeviceSubspace(
|
||||
createdAt: null,
|
||||
disabled: false,
|
||||
subspaceName: '',
|
||||
updatedAt: null,
|
||||
uuid: '',
|
||||
),
|
||||
productUuid: json['productUuid'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -118,9 +177,50 @@ class DeviceModel {
|
||||
'updateTime': updateTime,
|
||||
'uuid': uuid,
|
||||
'productType': productType,
|
||||
'subspace': subspace?.toJson(), // serialize subspace
|
||||
};
|
||||
}
|
||||
|
||||
List<FunctionModel> getFunctions(DeviceType type) =>
|
||||
devicesFunctionsMap[productType] ?? [];
|
||||
}
|
||||
|
||||
class DeviceSubspace {
|
||||
String? uuid;
|
||||
DateTime? createdAt;
|
||||
DateTime? updatedAt;
|
||||
String? subspaceName;
|
||||
bool? disabled;
|
||||
|
||||
DeviceSubspace({
|
||||
this.uuid,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.subspaceName,
|
||||
this.disabled,
|
||||
});
|
||||
|
||||
factory DeviceSubspace.fromJson(Map<String, dynamic> json) {
|
||||
return DeviceSubspace(
|
||||
uuid: json['uuid'],
|
||||
createdAt: json['createdAt'] != null
|
||||
? DateTime.tryParse(json['createdAt'])
|
||||
: null,
|
||||
updatedAt: json['updatedAt'] != null
|
||||
? DateTime.tryParse(json['updatedAt'])
|
||||
: null,
|
||||
subspaceName: json['subspaceName'],
|
||||
disabled: json['disabled'],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'createdAt': createdAt?.toIso8601String(),
|
||||
'updatedAt': updatedAt?.toIso8601String(),
|
||||
'subspaceName': subspaceName,
|
||||
'disabled': disabled,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,17 +9,18 @@ class WallSensorModel {
|
||||
int currentDistance;
|
||||
int illuminance;
|
||||
bool indicator;
|
||||
int noOneTime;
|
||||
|
||||
WallSensorModel({
|
||||
required this.presenceState,
|
||||
required this.farDetection,
|
||||
required this.presenceTime,
|
||||
required this.motionSensitivity,
|
||||
required this.motionlessSensitivity,
|
||||
required this.currentDistance,
|
||||
required this.illuminance,
|
||||
required this.indicator,
|
||||
});
|
||||
WallSensorModel(
|
||||
{required this.presenceState,
|
||||
required this.farDetection,
|
||||
required this.presenceTime,
|
||||
required this.motionSensitivity,
|
||||
required this.motionlessSensitivity,
|
||||
required this.currentDistance,
|
||||
required this.illuminance,
|
||||
required this.indicator,
|
||||
required this.noOneTime});
|
||||
|
||||
factory WallSensorModel.fromJson(List<StatusModel> jsonList) {
|
||||
late String _presenceState;
|
||||
@ -30,6 +31,7 @@ class WallSensorModel {
|
||||
late int _currentDistance;
|
||||
late int _illuminance;
|
||||
late bool _indicator;
|
||||
late int _noOneTime;
|
||||
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'presence_state') {
|
||||
@ -48,6 +50,8 @@ class WallSensorModel {
|
||||
_illuminance = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'indicator') {
|
||||
_indicator = jsonList[i].value ?? false;
|
||||
} else if (jsonList[i].code == 'no_one_time') {
|
||||
_noOneTime = jsonList[i].value ?? 0;
|
||||
}
|
||||
}
|
||||
return WallSensorModel(
|
||||
@ -58,6 +62,7 @@ class WallSensorModel {
|
||||
motionlessSensitivity: _motionlessSensitivity,
|
||||
currentDistance: _currentDistance,
|
||||
illuminance: _illuminance,
|
||||
indicator: _indicator);
|
||||
indicator: _indicator,
|
||||
noOneTime: _noOneTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,8 +38,10 @@ class SettingProfilePage extends StatelessWidget {
|
||||
final _bloc = BlocProvider.of<DeviceSettingBloc>(context);
|
||||
return state is DeviceSettingLoadingState
|
||||
? const Center(
|
||||
child:
|
||||
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
@ -55,15 +57,18 @@ class SettingProfilePage extends StatelessWidget {
|
||||
child: SvgPicture.asset(
|
||||
Assets.sosHomeIcon,
|
||||
fit: BoxFit.fitHeight,
|
||||
height: MediaQuery.of(context).size.height * 0.13,
|
||||
height:
|
||||
MediaQuery.of(context).size.height * 0.13,
|
||||
))
|
||||
: CircleAvatar(
|
||||
radius: 55,
|
||||
backgroundColor: ColorsManager.graysColor,
|
||||
child: ClipOval(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
@ -71,7 +76,10 @@ class SettingProfilePage extends StatelessWidget {
|
||||
? Assets.fourSceneIcon
|
||||
: Assets.sixSceneIcon,
|
||||
fit: BoxFit.contain,
|
||||
height: MediaQuery.of(context).size.height * 0.08,
|
||||
height: MediaQuery.of(context)
|
||||
.size
|
||||
.height *
|
||||
0.08,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -89,7 +97,8 @@ class SettingProfilePage extends StatelessWidget {
|
||||
children: [
|
||||
IntrinsicWidth(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 200),
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 200),
|
||||
child: TextFormField(
|
||||
maxLength: 30,
|
||||
style: const TextStyle(
|
||||
@ -122,7 +131,8 @@ class SettingProfilePage extends StatelessWidget {
|
||||
Assets.sosEditProfile,
|
||||
color: Colors.grey,
|
||||
fit: BoxFit.contain,
|
||||
height: MediaQuery.of(context).size.height * 0.02,
|
||||
height: MediaQuery.of(context).size.height *
|
||||
0.02,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -141,15 +151,17 @@ class SettingProfilePage extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
bool? val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationSettingPage(
|
||||
space: spaces!.first,
|
||||
deviceId: device?.uuid ?? '',
|
||||
)),
|
||||
);
|
||||
if (val != null && val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
if (HomeCubit.visitorPasswordManagement) {
|
||||
bool? val = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocationSettingPage(
|
||||
space: spaces!.first,
|
||||
deviceId: device?.uuid ?? '',
|
||||
)),
|
||||
);
|
||||
if (val != null && val == true) {
|
||||
_bloc.add(const DeviceSettingInitialInfo());
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
@ -162,7 +174,8 @@ class SettingProfilePage extends StatelessWidget {
|
||||
children: [
|
||||
SizedBox(
|
||||
child: BodyMedium(
|
||||
text: _bloc.deviceInfo.subspace.subspaceName,
|
||||
text: _bloc
|
||||
.deviceInfo.subspace.subspaceName,
|
||||
fontColor: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.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_controls.dart';
|
||||
@ -71,6 +73,7 @@ class AcInterface extends StatelessWidget {
|
||||
onTap: () {
|
||||
BlocProvider.of<ACsBloc>(context)
|
||||
.add(AcSwitch(acSwitch: statusModel.acSwitch));
|
||||
|
||||
},
|
||||
child: SvgPicture.asset(Assets.acSwitchIcon))
|
||||
],
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.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';
|
||||
@ -40,6 +41,8 @@ class ACsView extends StatelessWidget {
|
||||
extendBody: true,
|
||||
appBar: deviceModel != null
|
||||
? DeviceAppbar(
|
||||
//BlocProvider.of<ACsBloc>(context).deviceStatus.acSwitch.toString()
|
||||
value: true,
|
||||
deviceName: deviceModel!.name!,
|
||||
deviceUuid: deviceModel!.uuid!,
|
||||
)
|
||||
|
||||
110
lib/features/devices/view/widgets/all_devices.dart
Normal file
110
lib/features/devices/view/widgets/all_devices.dart
Normal file
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/room_page_switch.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class AllDevices extends StatefulWidget {
|
||||
const AllDevices({super.key, required this.allDevices});
|
||||
|
||||
final List<DeviceModel> allDevices;
|
||||
|
||||
@override
|
||||
_AllDevicesState createState() => _AllDevicesState();
|
||||
}
|
||||
|
||||
class _AllDevicesState extends State<AllDevices> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
List<DeviceModel> _filteredDevices = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_filteredDevices = widget.allDevices ?? [];
|
||||
_searchController.addListener(_filterDevices);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.removeListener(_filterDevices);
|
||||
_searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _filterDevices() {
|
||||
final query = _searchController.text.toLowerCase();
|
||||
setState(() {
|
||||
_filteredDevices = widget.allDevices!
|
||||
.where((device) => device.name!.toLowerCase().contains(query))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
if (widget.allDevices.isNotEmpty)
|
||||
TextFormField(
|
||||
controller: _searchController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search',
|
||||
hintStyle: const TextStyle(
|
||||
color: ColorsManager.textGray,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400),
|
||||
prefixIcon: Container(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.searchIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
_filteredDevices.isNotEmpty
|
||||
? Expanded(
|
||||
child: GridView.builder(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
childAspectRatio: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
itemCount: _filteredDevices.length,
|
||||
itemBuilder: (context, index) {
|
||||
return RoomPageSwitch(
|
||||
allDevices: _filteredDevices,
|
||||
isAllDevices: true,
|
||||
device: _filteredDevices[index]);
|
||||
},
|
||||
),
|
||||
)
|
||||
: widget.allDevices.isNotEmpty
|
||||
? const Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'No Results Found',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
)),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -250,7 +250,9 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
),
|
||||
] else if (button['title'] == 'Space Type') ...[
|
||||
Text(
|
||||
model.spaceType.name.toString(),
|
||||
model.spaceType.name.toString() == "none"
|
||||
? "Office"
|
||||
: model.spaceType.name.toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
] else ...[
|
||||
@ -299,7 +301,13 @@ class CeilingSensorInterface extends StatelessWidget {
|
||||
);
|
||||
if (result != null) {
|
||||
bloc.add(ChangeValueEvent(
|
||||
type: title.toString(), value: result, code: 'nobody_time'));
|
||||
type: title.toString(),
|
||||
value: result
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replaceAll('sec', 's')
|
||||
.replaceAll('1hr', '1hour'), // Replacing "sec" with "s"
|
||||
code: 'nobody_time'));
|
||||
}
|
||||
} else if (title == 'Maximum Distance') {
|
||||
final result = await _showDialog(
|
||||
|
||||
@ -90,9 +90,7 @@ class PresenceRecord extends StatelessWidget {
|
||||
: Colors.grey,
|
||||
),
|
||||
title: Text(
|
||||
record.value == 'true'
|
||||
? "Opened"
|
||||
: "Closed",
|
||||
record.value.toString(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
|
||||
@ -106,12 +106,11 @@ class _PresenceSpaceTypeDialogState extends State<PresenceSpaceTypeDialog> {
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: SvgPicture.asset(
|
||||
icon,
|
||||
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
title == "None" ? "Office" : title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_event.dar
|
||||
import 'package:syncrow_app/features/devices/bloc/curtain_bloc/curtain_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/curtains/curtain_buttons.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
@ -33,11 +34,17 @@ class CurtainView extends StatelessWidget {
|
||||
// blindHeight = state.blindHeight;
|
||||
}
|
||||
return DefaultScaffold(
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: curtain!.name!,
|
||||
deviceUuid: curtain!.uuid!,
|
||||
),
|
||||
title: curtain.name,
|
||||
child: state is CurtainLoadingState
|
||||
? const Center(
|
||||
child:
|
||||
DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
child: DefaultContainer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: CircularProgressIndicator()),
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
@ -62,37 +69,56 @@ class CurtainView extends StatelessWidget {
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.assetsIconsCurtainsIconCurtainHolder,
|
||||
width: MediaQuery.sizeOf(context).width * 0.75,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width * 0.75,
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.75,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width * 0.75,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.025,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width *
|
||||
0.025,
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration:
|
||||
const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
height: MediaQuery.sizeOf(context).height * 0.35,
|
||||
width: MediaQuery.sizeOf(context).width * 0.35,
|
||||
height: MediaQuery.sizeOf(context)
|
||||
.height *
|
||||
0.35,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width *
|
||||
0.35,
|
||||
child: Stack(
|
||||
children: List.generate(
|
||||
4,
|
||||
(index) {
|
||||
double spacing = curtainWidth / 7.5;
|
||||
double leftMostPosition = index * spacing;
|
||||
double spacing =
|
||||
curtainWidth / 7.5;
|
||||
double leftMostPosition =
|
||||
index * spacing;
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration: const Duration(
|
||||
milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
left: leftMostPosition,
|
||||
child: SizedBox(
|
||||
height:
|
||||
MediaQuery.sizeOf(context).height * 0.35,
|
||||
width: MediaQuery.sizeOf(context).width * 0.08,
|
||||
height: MediaQuery.sizeOf(
|
||||
context)
|
||||
.height *
|
||||
0.35,
|
||||
width: MediaQuery.sizeOf(
|
||||
context)
|
||||
.width *
|
||||
0.08,
|
||||
child: SvgPicture.asset(
|
||||
Assets.assetsIconsCurtainsIconVerticalBlade,
|
||||
Assets
|
||||
.assetsIconsCurtainsIconVerticalBlade,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
@ -102,23 +128,37 @@ class CurtainView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration:
|
||||
const Duration(milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
height: MediaQuery.sizeOf(context).height * 0.35,
|
||||
width: MediaQuery.sizeOf(context).width * 0.35,
|
||||
height: MediaQuery.sizeOf(context)
|
||||
.height *
|
||||
0.35,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width *
|
||||
0.35,
|
||||
child: Stack(
|
||||
children: List.generate(
|
||||
4,
|
||||
(index) {
|
||||
double spacing = curtainWidth / 7.5;
|
||||
double rightMostPosition = index * spacing;
|
||||
double spacing =
|
||||
curtainWidth / 7.5;
|
||||
double rightMostPosition =
|
||||
index * spacing;
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
duration: const Duration(
|
||||
milliseconds: 200),
|
||||
curve: Curves.linear,
|
||||
right: rightMostPosition,
|
||||
child: SizedBox(
|
||||
height: MediaQuery.sizeOf(context).height * 0.35,
|
||||
width: MediaQuery.sizeOf(context).width * 0.08,
|
||||
height: MediaQuery.sizeOf(
|
||||
context)
|
||||
.height *
|
||||
0.35,
|
||||
width: MediaQuery.sizeOf(
|
||||
context)
|
||||
.width *
|
||||
0.08,
|
||||
child: SvgPicture.asset(
|
||||
Assets.rightVerticalBlade,
|
||||
fit: BoxFit.fill,
|
||||
@ -130,7 +170,9 @@ class CurtainView extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width * 0.025,
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width *
|
||||
0.025,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
@ -6,14 +7,27 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
class DeviceAppbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final String deviceName;
|
||||
final String deviceUuid;
|
||||
final bool? value;
|
||||
|
||||
final double appBarHeight = 56.0;
|
||||
final void Function()? onPressed;
|
||||
const DeviceAppbar(
|
||||
{super.key, required this.deviceName, required this.deviceUuid, this.onPressed});
|
||||
{super.key,
|
||||
required this.deviceName,
|
||||
this.value,
|
||||
required this.deviceUuid,
|
||||
this.onPressed});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
leading: IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(value ?? true);
|
||||
},
|
||||
icon: Icon(
|
||||
Platform.isIOS ? Icons.arrow_back_ios : Icons.arrow_back,
|
||||
)),
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: BodyLarge(
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/devices_cubit.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/all_devices.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/room_page.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/rooms_slider.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/wizard_page.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/view/scene_view.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/create_unit.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
@ -14,103 +14,130 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.da
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/strings_manager.dart';
|
||||
|
||||
class DevicesViewPage extends StatelessWidget {
|
||||
const DevicesViewPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SceneBloc(), // Initialize your SceneBloc here
|
||||
child: DevicesViewBody(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DevicesViewBody extends StatelessWidget {
|
||||
const DevicesViewBody({
|
||||
super.key,
|
||||
});
|
||||
|
||||
const DevicesViewBody({Key? key}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, state) {
|
||||
if (state is DevicesLoading ||
|
||||
state is GetDevicesLoading ||
|
||||
state is DevicesCategoriesLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else {
|
||||
return HomeCubit.getInstance().spaces?.isEmpty ?? true
|
||||
? const CreateUnitWidget()
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: StringsManager.devices,
|
||||
style: context.titleMedium.copyWith(
|
||||
fontSize: 25,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.1,
|
||||
child: const SceneView(
|
||||
pageType: true,
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
const RoomsSlider(),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: HomeCubit.getInstance().devicesPageController,
|
||||
onPageChanged: (index) {
|
||||
HomeCubit.getInstance().devicesPageChanged(index);
|
||||
},
|
||||
children: [
|
||||
WizardPage(
|
||||
groupsList: DevicesCubit.getInstance().allCategories ?? [],
|
||||
),
|
||||
if (HomeCubit.getInstance().selectedSpace != null)
|
||||
...HomeCubit.getInstance().selectedSpace!.subspaces.map(
|
||||
(room) {
|
||||
return RoomPage(
|
||||
room: room,
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
HomeCubit.getInstance().selectedSpace != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 7,
|
||||
),
|
||||
child: SmoothPageIndicator(
|
||||
controller: HomeCubit.getInstance().devicesPageController,
|
||||
count: HomeCubit.getInstance().selectedSpace!.subspaces.length + 1,
|
||||
effect: const WormEffect(
|
||||
paintStyle: PaintingStyle.stroke,
|
||||
dotHeight: 8,
|
||||
dotWidth: 8,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Center(
|
||||
child: BodyLarge(text: 'No Home Found'),
|
||||
),
|
||||
],
|
||||
);
|
||||
return BlocBuilder<HomeCubit, HomeState>(
|
||||
builder: (context, homeState) {
|
||||
final homeCubit = HomeCubit.getInstance();
|
||||
|
||||
// Handle state priority: Errors first
|
||||
if (homeState is ActivationError) {
|
||||
return const CreateUnitWidget();
|
||||
}
|
||||
|
||||
// Handle loading states
|
||||
if (homeState is GetSpacesLoading || homeState is HomeLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// Handle error states
|
||||
if (homeState is GetSpacesError) {
|
||||
return const CreateUnitWidget();
|
||||
}
|
||||
|
||||
// Handle success states
|
||||
if (homeState is GetSpacesSuccess ||
|
||||
homeState is RoomUnSelected ||
|
||||
homeState is RoomSelected ||
|
||||
homeState is NavChangePage) {
|
||||
// Show empty state if no spaces
|
||||
if (homeCubit.spaces.isEmpty) {
|
||||
return const CreateUnitWidget();
|
||||
}
|
||||
|
||||
return BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, devicesState) {
|
||||
// Devices loading states
|
||||
if (devicesState is DevicesLoading ||
|
||||
devicesState is DevicesCategoriesLoading ||
|
||||
devicesState is GetDevicesLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// Devices error state
|
||||
if (devicesState is GetDevicesError) {
|
||||
return Center(child: BodyLarge(text: devicesState.errorMsg));
|
||||
}
|
||||
// Main content for both GetSpacesSuccess and RoomUnSelected
|
||||
return _buildMainContent(context, homeCubit);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback for unknown states
|
||||
return const Center(child: BodyLarge(text: ''));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(BuildContext context, HomeCubit homeCubit) {
|
||||
final devicesCubit = context.read<DevicesCubit>();
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: StringsManager.devices,
|
||||
style: context.titleMedium.copyWith(fontSize: 25),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.1,
|
||||
child: const SceneView(pageType: true),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const RoomsSlider(),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: homeCubit.devicesPageController,
|
||||
onPageChanged: (index) {
|
||||
homeCubit.devicesPageChanged(index);
|
||||
if (index == 0) {
|
||||
devicesCubit.fetchAllDevices(homeCubit.selectedSpace);
|
||||
}
|
||||
},
|
||||
children: [
|
||||
AllDevices(
|
||||
allDevices: devicesCubit.allDevices,
|
||||
),
|
||||
WizardPage(
|
||||
groupsList: devicesCubit.allCategories ?? [],
|
||||
),
|
||||
if (homeCubit.selectedSpace != null)
|
||||
...homeCubit.selectedSpace!.subspaces.map(
|
||||
(room) => RoomPage(room: room),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildPageIndicator(homeCubit),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPageIndicator(HomeCubit homeCubit) {
|
||||
return homeCubit.selectedSpace != null
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 7),
|
||||
child: SmoothPageIndicator(
|
||||
controller: homeCubit.devicesPageController,
|
||||
count: homeCubit.selectedSpace!.subspaces.length + 2,
|
||||
effect: const WormEffect(
|
||||
paintStyle: PaintingStyle.stroke,
|
||||
dotHeight: 8,
|
||||
dotWidth: 8,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Center(
|
||||
child: BodyLarge(text: 'No Home Found'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_b
|
||||
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/garage_door_bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_preferences_settings.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_records_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/garage_door/schedule_garage_screen.dart';
|
||||
@ -22,6 +23,10 @@ class GarageDoorScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: device!.name!,
|
||||
deviceUuid: device!.uuid!,
|
||||
),
|
||||
title: 'Garage Door Opener',
|
||||
child: BlocProvider(
|
||||
create: (context) => GarageDoorBloc(GDId: device?.uuid ?? '')
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_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/6_scene_switch/six_scene_screen.dart';
|
||||
@ -28,23 +29,31 @@ import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_interf
|
||||
import 'package:syncrow_app/features/devices/view/widgets/three_gang/three_gang_interface.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/water_heater/water_heater_page.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/water_leak/water_leak_screen.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/custom_switch.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/helpers/custom_page_route.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
|
||||
class RoomPageSwitch extends StatelessWidget {
|
||||
const RoomPageSwitch({
|
||||
super.key,
|
||||
required this.device,
|
||||
});
|
||||
const RoomPageSwitch(
|
||||
{super.key,
|
||||
required this.device,
|
||||
this.isAllDevices = false,
|
||||
this.allDevices});
|
||||
|
||||
final DeviceModel device;
|
||||
final List<DeviceModel>? allDevices;
|
||||
final bool isAllDevices;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
showDeviceInterface(device, context);
|
||||
showDeviceInterface(
|
||||
device: device,
|
||||
context: context,
|
||||
isAllDevices: isAllDevices,
|
||||
allDevices: allDevices);
|
||||
},
|
||||
child: DefaultContainer(
|
||||
padding: const EdgeInsets.all(15),
|
||||
@ -60,22 +69,38 @@ class RoomPageSwitch extends StatelessWidget {
|
||||
device.icon!,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
// CustomSwitch(
|
||||
// device: device,
|
||||
// ),
|
||||
isAllDevices
|
||||
? CustomSwitch(
|
||||
device: device,
|
||||
)
|
||||
: const SizedBox(),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
device.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
device.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
device.subspace!.subspaceName ?? '',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.bodySmall.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -89,14 +114,23 @@ class RoomPageSwitch extends StatelessWidget {
|
||||
/// Shows the device interface based on the product type of the device.
|
||||
///
|
||||
/// The [device] parameter represents the device model.
|
||||
void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
Future<void> showDeviceInterface(
|
||||
{required DeviceModel device,
|
||||
required BuildContext context,
|
||||
required isAllDevices,
|
||||
List<DeviceModel>? allDevices}) async {
|
||||
final devicesCubit = context.read<DevicesCubit>();
|
||||
|
||||
switch (device.productType) {
|
||||
case DeviceType.AC:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
ACsView(deviceModel: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
// navigateToInterface(ACsView(deviceModel: device), context);
|
||||
break;
|
||||
case DeviceType.WallSensor:
|
||||
@ -116,12 +150,15 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
// navigateToInterface(CeilingSensorInterface(ceilingSensor: device), context);
|
||||
break;
|
||||
case DeviceType.Curtain:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) => CurtainView(
|
||||
curtain: device,
|
||||
)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
break;
|
||||
case DeviceType.Blind:
|
||||
break;
|
||||
@ -143,30 +180,43 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
case DeviceType.LightBulb:
|
||||
navigateToInterface(LightInterface(light: device), context);
|
||||
case DeviceType.OneGang:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
OneGangInterface(gangSwitch: device)));
|
||||
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
case DeviceType.TwoGang:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
TwoGangInterface(gangSwitch: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
case DeviceType.ThreeGang:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
ThreeGangInterface(gangSwitch: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.WH:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
WaterHeaterPage(device: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
case DeviceType.DS:
|
||||
Navigator.push(
|
||||
context,
|
||||
@ -182,31 +232,43 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
PowerClampPage(device: device)));
|
||||
|
||||
case DeviceType.OneTouch:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
OneTouchScreen(device: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.TowTouch:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
TwoTouchInterface(touchSwitch: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
case DeviceType.ThreeTouch:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
ThreeTouchInterface(touchSwitch: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.GarageDoor:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
GarageDoorScreen(device: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.WaterLeak:
|
||||
Navigator.push(
|
||||
@ -216,18 +278,24 @@ void showDeviceInterface(DeviceModel device, BuildContext context) {
|
||||
WaterLeakScreen(device: device)));
|
||||
|
||||
case DeviceType.SixScene:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
SixSceneScreen(device: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.FourScene:
|
||||
Navigator.push(
|
||||
var value = await Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
FourSceneScreen(device: device)));
|
||||
if (value && isAllDevices) {
|
||||
devicesCubit.fetchAllDevices(HomeCubit.getInstance().selectedSpace);
|
||||
}
|
||||
|
||||
case DeviceType.SOS:
|
||||
Navigator.push(
|
||||
|
||||
@ -23,6 +23,21 @@ class RoomsSlider extends StatelessWidget {
|
||||
HomeCubit.getInstance().roomSliderPageChanged(index);
|
||||
},
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
HomeCubit.getInstance().unselectRoom();
|
||||
},
|
||||
child: TitleMedium(
|
||||
text: 'All Devices',
|
||||
style: context.titleMedium.copyWith(
|
||||
fontSize: 25,
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: InkWell(
|
||||
@ -43,7 +58,10 @@ class RoomsSlider extends StatelessWidget {
|
||||
(room) => InkWell(
|
||||
onTap: () {
|
||||
HomeCubit.getInstance().roomSliderPageChanged(
|
||||
HomeCubit.getInstance().selectedSpace!.subspaces.indexOf(room));
|
||||
HomeCubit.getInstance()
|
||||
.selectedSpace!
|
||||
.subspaces
|
||||
.indexOf(room));
|
||||
},
|
||||
child: TitleMedium(
|
||||
text: room.name!,
|
||||
@ -51,7 +69,8 @@ class RoomsSlider extends StatelessWidget {
|
||||
fontSize: 25,
|
||||
color: HomeCubit.getInstance().selectedRoom == room
|
||||
? ColorsManager.textPrimaryColor
|
||||
: ColorsManager.textPrimaryColor.withOpacity(.2),
|
||||
: ColorsManager.textPrimaryColor
|
||||
.withOpacity(.2),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.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/bloc/scene_bloc/scene_bloc.dart';
|
||||
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
|
||||
import 'package:syncrow_app/features/scene/model/scenes_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
|
||||
class SceneListview extends StatelessWidget {
|
||||
final List<ScenesModel> scenes;
|
||||
@ -31,23 +28,28 @@ class SceneListview extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: DefaultContainer(
|
||||
onTap: () {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.sceneTasksRoute,
|
||||
arguments: SceneSettingsRouteArguments(
|
||||
sceneType: CreateSceneEnum.tabToRun.name,
|
||||
sceneId: scene.id,
|
||||
sceneName: scene.name,
|
||||
),
|
||||
);
|
||||
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
|
||||
context
|
||||
.read<SceneBloc>()
|
||||
.add(SceneTrigger(scene.id, scene.name));
|
||||
// Navigator.pushNamed(
|
||||
// context,
|
||||
// Routes.sceneTasksRoute,
|
||||
// arguments: SceneSettingsRouteArguments(
|
||||
// sceneType: CreateSceneEnum.tabToRun.name,
|
||||
// sceneId: scene.id,
|
||||
// sceneName: scene.name,
|
||||
// ),
|
||||
// );
|
||||
// context.read<SmartSceneSelectBloc>()
|
||||
// .add(const SmartSceneClearEvent());
|
||||
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
|
||||
// BlocProvider.of<CreateSceneBloc>(context).add(
|
||||
// FetchSceneTasksEvent(
|
||||
// sceneId: scene.id, isAutomation: false));
|
||||
|
||||
/// the state to set the scene type must be after the fetch
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
|
||||
// /// the state to set the scene type must be after the fetch
|
||||
// BlocProvider.of<CreateSceneBloc>(context)
|
||||
// .add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
@ -62,7 +64,8 @@ class SceneListview extends StatelessWidget {
|
||||
height: 32,
|
||||
width: 32,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (context, error, stackTrace) => Image.asset(
|
||||
errorBuilder: (context, error, stackTrace) =>
|
||||
Image.asset(
|
||||
Assets.assetsIconsLogo,
|
||||
height: 32,
|
||||
width: 32,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/smart_door/members_management_view.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/smart_door/smart_linkage_view.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/smart_door/temporary_password_page.dart';
|
||||
@ -14,6 +15,8 @@ class DoorLockGrid extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final buttons = doorLockButtons(val: uuid);
|
||||
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
@ -23,14 +26,15 @@ class DoorLockGrid extends StatelessWidget {
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 1.75 / 1,
|
||||
),
|
||||
itemCount: 4,
|
||||
itemCount: buttons.length,
|
||||
itemBuilder: (context, index) => DefaultContainer(
|
||||
onTap: () {
|
||||
//TODO: remove checking after adding the pages
|
||||
doorLockButtons()[index]['page'] != null
|
||||
? Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => doorLockButtons(val: uuid)[index]['page'] as Widget,
|
||||
builder: (context) =>
|
||||
doorLockButtons(val: uuid)[index]['page'] as Widget,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
@ -75,11 +79,12 @@ List<Map<String, dynamic>> doorLockButtons({val}) => [
|
||||
'image': Assets.assetsIconsDoorlockAssetsMembersManagement,
|
||||
'page': const MembersManagementView(),
|
||||
},
|
||||
{
|
||||
'title': 'Temporary Password',
|
||||
'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword,
|
||||
'page': TemporaryPasswordPage(deviceId: val),
|
||||
},
|
||||
if (HomeCubit.manageDeviceLocation)
|
||||
{
|
||||
'title': 'Temporary Password',
|
||||
'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword,
|
||||
'page': TemporaryPasswordPage(deviceId: val),
|
||||
},
|
||||
{
|
||||
'title': 'Smart Linkage',
|
||||
'image': Assets.assetsIconsDoorlockAssetsSmartLinkage,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/two_touch_bloc/two_touch_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/two_touch/two_touch_screen.dart';
|
||||
@ -26,6 +28,7 @@ class TwoTouchInterface extends StatelessWidget {
|
||||
extendBody: true,
|
||||
appBar: touchSwitch != null
|
||||
? DeviceAppbar(
|
||||
value: true,
|
||||
deviceName: touchSwitch!.name!,
|
||||
deviceUuid: touchSwitch!.uuid!,
|
||||
)
|
||||
|
||||
@ -215,8 +215,7 @@ class ParametersList extends StatelessWidget {
|
||||
{
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsEmpty,
|
||||
'title': 'Nobody Time',
|
||||
'code': null,
|
||||
//TODO: Implement the nobody time
|
||||
'code': 'no_one_time',
|
||||
},
|
||||
{
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsIndicator,
|
||||
@ -231,7 +230,7 @@ class ParametersList extends StatelessWidget {
|
||||
{
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsIlluminanceRecord,
|
||||
'title': 'Illuminance Record',
|
||||
'code': null
|
||||
'code': 'illuminance_value'
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -240,6 +239,7 @@ Widget listItem(Map<String, Object?> wallSensorButton, BuildContext context, Dev
|
||||
WallSensorModel wallSensorStatus) {
|
||||
String? unit;
|
||||
dynamic value;
|
||||
int noBodyTimeValue;
|
||||
if (wallSensorButton['code'] != null) {
|
||||
// if (wallSensor.status.any((element) => element.code == wallSensorButton['code'] as String)) {
|
||||
// unit = unitsMap[wallSensorButton['code'] as String];
|
||||
@ -256,12 +256,15 @@ Widget listItem(Map<String, Object?> wallSensorButton, BuildContext context, Dev
|
||||
} else if (wallSensorButton['code'] == 'illuminance_value') {
|
||||
unit = unitsMap[wallSensorButton['code'] as String];
|
||||
value = wallSensorStatus.illuminance;
|
||||
} else if (wallSensorButton['code'] == 'no_one_time') {
|
||||
unit = unitsMap[wallSensorButton['code'] as String];
|
||||
value = wallSensorStatus.noOneTime;
|
||||
}
|
||||
}
|
||||
return DefaultContainer(
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
|
||||
onTap: () {
|
||||
onTap: () async {
|
||||
if (wallSensorButton['page'] != null) {
|
||||
Navigator.push(
|
||||
context,
|
||||
@ -270,6 +273,42 @@ Widget listItem(Map<String, Object?> wallSensorButton, BuildContext context, Dev
|
||||
),
|
||||
);
|
||||
}
|
||||
if (wallSensorButton['title'] == 'Presence Record') {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PresenceRecords(
|
||||
deviceId: wallSensor.uuid!,
|
||||
code: 'presence_state',
|
||||
title: 'Presence Record',
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
if (wallSensorButton['title'] == 'Illuminance Record') {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PresenceRecords(
|
||||
deviceId: wallSensor.uuid!,
|
||||
code: 'illuminance_value',
|
||||
title: 'Illuminance Record',
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
if (wallSensorButton['title'] == 'Nobody Time') {
|
||||
int noBodyTimeValue = value as int;
|
||||
String controlCode = 'no_one_time';
|
||||
final result = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => ParameterControlDialog(
|
||||
title: 'Nobody Time', sensor: wallSensor, value: noBodyTimeValue, min: 0, max: 10000),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
BlocProvider.of<WallSensorBloc>(context)
|
||||
.add(ChangeValueEvent(value: result, code: controlCode));
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
@ -0,0 +1,120 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class PresenceRecords extends StatelessWidget {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final String title;
|
||||
const PresenceRecords(
|
||||
{super.key, required this.deviceId, required this.code, required this.title});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: title,
|
||||
child: BlocProvider(
|
||||
create: (context) => WallSensorBloc(deviceId: deviceId)
|
||||
..add(GetDeviceReportsEvent(deviceUuid: deviceId, code: code)),
|
||||
child: BlocBuilder<WallSensorBloc, WallSensorState>(builder: (context, state) {
|
||||
final Map<String, List<DeviceEvent>> groupedRecords = {};
|
||||
|
||||
if (state is LoadingInitialState) {
|
||||
return const Center(
|
||||
child: DefaultContainer(width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
);
|
||||
} else if (state is DeviceReportsState) {
|
||||
for (var record in state.deviceReport.data ?? []) {
|
||||
final DateTime eventDateTime = DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
|
||||
final String formattedDate = DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
|
||||
|
||||
// Group by formatted date
|
||||
if (groupedRecords.containsKey(formattedDate)) {
|
||||
groupedRecords[formattedDate]!.add(record);
|
||||
} else {
|
||||
groupedRecords[formattedDate] = [record];
|
||||
}
|
||||
}
|
||||
}
|
||||
return groupedRecords.isEmpty
|
||||
? const Center(
|
||||
child: Text('No records found'),
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: groupedRecords.length,
|
||||
itemBuilder: (context, index) {
|
||||
final String date = groupedRecords.keys.elementAt(index);
|
||||
final List<DeviceEvent> recordsForDate = groupedRecords[date]!;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5, top: 10),
|
||||
child: Text(
|
||||
date,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
DefaultContainer(
|
||||
child: Column(
|
||||
children: [
|
||||
...recordsForDate.asMap().entries.map((entry) {
|
||||
final int idx = entry.key;
|
||||
final DeviceEvent record = entry.value;
|
||||
final DateTime eventDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
|
||||
final String formattedTime =
|
||||
DateFormat('HH:mm:ss').format(eventDateTime);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
record.value == 'true'
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: record.value == 'true' ? Colors.blue : Colors.grey,
|
||||
),
|
||||
title: Text(
|
||||
record.value == 'true' ? "Opened" : "Closed",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
subtitle: Text('$formattedTime'),
|
||||
),
|
||||
),
|
||||
if (idx != recordsForDate.length - 1)
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -9,6 +11,8 @@ import 'package:syncrow_app/features/devices/bloc/wall_sensor_bloc/wall_sensor_e
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/wall_sensor_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/garage_door/garage_records_screen.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/persence_records.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';
|
||||
@ -41,7 +45,8 @@ class WallMountedInterface extends StatelessWidget {
|
||||
motionlessSensitivity: 0,
|
||||
currentDistance: 0,
|
||||
illuminance: 0,
|
||||
indicator: false);
|
||||
indicator: false,
|
||||
noOneTime: 0);
|
||||
|
||||
if (state is UpdateState) {
|
||||
wallSensorModel = state.wallSensorModel;
|
||||
|
||||
@ -1,12 +1,184 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/user_agreement.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/manage_home/manage_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/securty_view.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/services/api/profile_api.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
part 'menu_state.dart';
|
||||
|
||||
class MenuCubit extends Cubit<MenuState> {
|
||||
MenuCubit() : super(MenuInitial());
|
||||
|
||||
static MenuCubit of(context) => BlocProvider.of<MenuCubit>(context);
|
||||
|
||||
String name = '';
|
||||
String userAgreementHtml = "";
|
||||
String privacyPolicyHtml = "";
|
||||
|
||||
Future<void> fetchAgreement() async {
|
||||
try {
|
||||
emit(MenuLoading());
|
||||
final response = await ProfileApi().fetchUserAgreement();
|
||||
userAgreementHtml = response;
|
||||
emit(MenuLoaded(userAgreementHtml));
|
||||
} catch (error) {
|
||||
emit(MenuError(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchPrivacyPolicy() async {
|
||||
try {
|
||||
emit(MenuLoading());
|
||||
final response = await ProfileApi().fetchPrivacyPolicy();
|
||||
privacyPolicyHtml = response;
|
||||
emit(MenuLoaded(privacyPolicyHtml));
|
||||
} catch (error) {
|
||||
emit(MenuError(error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, Object>> menuSections = [
|
||||
//Home Management
|
||||
{
|
||||
'title': 'Home Management',
|
||||
'color': ColorsManager.primaryColor,
|
||||
'buttons': [
|
||||
// {
|
||||
// 'title': 'Create a Unit',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsCreateHome,
|
||||
// 'page': const CreateUnitView()
|
||||
// },
|
||||
{
|
||||
'title': 'Join a Unit',
|
||||
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsJoinAHome,
|
||||
'page': const JoinHomeView()
|
||||
},
|
||||
if (HomeCubit.manageSupSpace)
|
||||
{
|
||||
'title': 'Manage Your Units',
|
||||
'Icon':
|
||||
Assets.assetsIconsMenuIconsHomeManagementIconsManageYourHome,
|
||||
'page': const ManageHomeView()
|
||||
},
|
||||
],
|
||||
},
|
||||
//General Settings
|
||||
// {
|
||||
// 'title': 'General Settings',
|
||||
// 'color': const Color(0xFF023DFE),
|
||||
// 'buttons': [
|
||||
// {
|
||||
// 'title': 'Voice Assistant',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsVoiceAssistant,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Temperature unit',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTemperatureUnit,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Touch tone on panel',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTouchTone,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Language',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsLanguage,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Network Diagnosis',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsNetworkDiagnosis,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Clear Cache',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsClearCach,
|
||||
// 'page': null
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// //Messages Center
|
||||
// {
|
||||
// 'title': 'Messages Center',
|
||||
// 'color': const Color(0xFF0088FF),
|
||||
// 'buttons': [
|
||||
// {
|
||||
// 'title': 'Alerts',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsAlerts,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Messages',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'FAQs',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Help & Feedback',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback,
|
||||
// 'page': null
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
//Security And Privacy
|
||||
{
|
||||
'title': 'Security And Privacy',
|
||||
'color': const Color(0xFF8AB9FF),
|
||||
'buttons': [
|
||||
{
|
||||
'title': 'Security',
|
||||
'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsSecurty,
|
||||
'page': const SecurtyView()
|
||||
},
|
||||
// {
|
||||
// 'title': 'Privacy',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsPrivacy,
|
||||
// 'page': const PrivacyView()
|
||||
// },
|
||||
],
|
||||
},
|
||||
//Legal Information
|
||||
{
|
||||
'title': 'Legal Information',
|
||||
'color': const Color(0xFF001B72),
|
||||
'buttons': [
|
||||
{
|
||||
'title': 'About',
|
||||
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout,
|
||||
'page': null
|
||||
},
|
||||
{
|
||||
'title': 'Privacy Policy',
|
||||
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy,
|
||||
'page': const PrivacyPolicy()
|
||||
},
|
||||
{
|
||||
'title': 'User Agreement',
|
||||
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsUserAgreement,
|
||||
'page': const UserAgreement()
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
Future<void> fetchMenuSections() async {
|
||||
emit(MenuLoading());
|
||||
try {
|
||||
emit(MenuItemsLoaded(menuSections));
|
||||
} catch (e) {
|
||||
emit(MenuError(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,3 +3,27 @@ part of 'menu_cubit.dart';
|
||||
abstract class MenuState {}
|
||||
|
||||
class MenuInitial extends MenuState {}
|
||||
|
||||
class MenuLoading extends MenuState {}
|
||||
|
||||
class MenuLoaded extends MenuState {
|
||||
final String userAgreementHtml;
|
||||
MenuLoaded(this.userAgreementHtml);
|
||||
}
|
||||
|
||||
|
||||
class MenuItemsLoaded extends MenuState {
|
||||
final List<Map<String, Object>> menuSections;
|
||||
MenuItemsLoaded(this.menuSections);
|
||||
}
|
||||
class MenuError extends MenuState {
|
||||
final String message;
|
||||
|
||||
MenuError(this.message);
|
||||
}
|
||||
|
||||
class MenuNameUpdated extends MenuState {
|
||||
final String name;
|
||||
|
||||
MenuNameUpdated(this.name);
|
||||
}
|
||||
|
||||
57
lib/features/menu/bloc/privacy_policy.dart
Normal file
57
lib/features/menu/bloc/privacy_policy.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/menu_cubit.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class PrivacyPolicy extends StatefulWidget {
|
||||
const PrivacyPolicy({super.key});
|
||||
|
||||
@override
|
||||
_PrivacyPolicyState createState() => _PrivacyPolicyState();
|
||||
}
|
||||
|
||||
class _PrivacyPolicyState extends State<PrivacyPolicy> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
MenuCubit.of(context).fetchPrivacyPolicy();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Privacy Policy',
|
||||
child: BlocBuilder<MenuCubit, MenuState>(
|
||||
builder: (context, state) {
|
||||
if (state is MenuLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is MenuLoaded) {
|
||||
return ListView(
|
||||
children: [
|
||||
Html(
|
||||
data: state.userAgreementHtml.isNotEmpty
|
||||
? state.userAgreementHtml
|
||||
: '',
|
||||
onLinkTap: (url, attributes, element) async {
|
||||
final uri = Uri.parse(url!);
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (state is MenuError) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Error: ${state.message}',
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const Center(child: Text('Loading User Agreement...'));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
57
lib/features/menu/bloc/user_agreement.dart
Normal file
57
lib/features/menu/bloc/user_agreement.dart
Normal file
@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/menu_cubit.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class UserAgreement extends StatefulWidget {
|
||||
const UserAgreement({super.key});
|
||||
|
||||
@override
|
||||
_UserAgreementState createState() => _UserAgreementState();
|
||||
}
|
||||
|
||||
class _UserAgreementState extends State<UserAgreement> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
MenuCubit.of(context).fetchAgreement();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'User Agreement',
|
||||
child: BlocBuilder<MenuCubit, MenuState>(
|
||||
builder: (context, state) {
|
||||
if (state is MenuLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is MenuLoaded) {
|
||||
return ListView(
|
||||
children: [
|
||||
Html(
|
||||
data: state.userAgreementHtml.isNotEmpty
|
||||
? state.userAgreementHtml
|
||||
: '',
|
||||
onLinkTap: (url, attributes, element) async {
|
||||
final uri = Uri.parse(url!);
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (state is MenuError) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Error: ${state.message}',
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const Center(child: Text('Loading User Agreement...'));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,69 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/menu_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/menu_list.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/profile/profile_tab.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';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
|
||||
class MenuView extends StatelessWidget {
|
||||
const MenuView({super.key});
|
||||
const MenuView({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (BuildContext context) => MenuCubit(),
|
||||
create: (BuildContext context) => MenuCubit()..fetchMenuSections(),
|
||||
child: BlocBuilder<MenuCubit, MenuState>(
|
||||
builder: (context, state) {
|
||||
return BlocBuilder<AuthCubit, AuthState>(
|
||||
builder: (context, state) {
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
const ProfileTab(),
|
||||
for (var section in menuSections)
|
||||
MenuList(
|
||||
section: section,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
BodyMedium(text: dotenv.env['ENV_NAME'] ?? ''),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
AuthCubit.get(context).logout();
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultContainer(
|
||||
child: Center(
|
||||
child: BodyLarge(
|
||||
text: 'Logout',
|
||||
style: context.bodyLarge.copyWith(
|
||||
color: Colors.red,
|
||||
),
|
||||
builder: (context, menuState) {
|
||||
if (menuState is MenuLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (menuState is MenuError) {
|
||||
return Center(child: Text(menuState.message));
|
||||
} else if (menuState is MenuItemsLoaded) {
|
||||
final sections = menuState.menuSections;
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
const ProfileTab(),
|
||||
for (var section in sections) MenuList(section: section),
|
||||
const SizedBox(height: 15),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
AuthCubit.get(context).logout();
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultContainer(
|
||||
child: Center(
|
||||
child: BodyLarge(
|
||||
text: 'Logout',
|
||||
style: context.bodyLarge.copyWith(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// Fallback in case no states match
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@ -3,10 +3,12 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.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';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class JoinHomeView extends StatelessWidget {
|
||||
const JoinHomeView({super.key});
|
||||
@ -57,14 +59,93 @@ class JoinHomeView extends StatelessWidget {
|
||||
'Please enter the invitation code');
|
||||
return;
|
||||
}
|
||||
if (await HomeCubit.getInstance().activationCode(textEditingController.text)) {
|
||||
if (await HomeCubit.getInstance()
|
||||
.activationCode(textEditingController.text)) {
|
||||
await HomeCubit.getInstance().fetchUnitsByUserId();
|
||||
|
||||
CustomSnackBar.displaySnackBar('Done successfully');
|
||||
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
CustomSnackBar.displaySnackBar('Wrong code!');
|
||||
// CustomSnackBar.displaySnackBar('Wrong code!');
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.2,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
const BodyLarge(
|
||||
text: "Warning",
|
||||
fontWeight: FontWeight.w700,
|
||||
fontColor: ColorsManager.red,
|
||||
fontSize: 16,
|
||||
),
|
||||
const Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 15, right: 15),
|
||||
child: Divider(
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15,
|
||||
right: 20,
|
||||
top: 15,
|
||||
bottom: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
HomeCubit.getInstance().errorMsg,
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: ColorsManager.textGray,
|
||||
width: 1.0,
|
||||
),
|
||||
)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 5, bottom: 5),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Ok',
|
||||
style: TextStyle(
|
||||
color: ColorsManager
|
||||
.switchButton
|
||||
.withOpacity(0.6),
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.w400),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
|
||||
@ -16,10 +16,12 @@ class SceneRoomsTabBarDevicesView extends StatefulWidget {
|
||||
const SceneRoomsTabBarDevicesView({super.key});
|
||||
|
||||
@override
|
||||
State<SceneRoomsTabBarDevicesView> createState() => _SceneRoomsTabBarDevicesViewState();
|
||||
State<SceneRoomsTabBarDevicesView> createState() =>
|
||||
_SceneRoomsTabBarDevicesViewState();
|
||||
}
|
||||
|
||||
class _SceneRoomsTabBarDevicesViewState extends State<SceneRoomsTabBarDevicesView>
|
||||
class _SceneRoomsTabBarDevicesViewState
|
||||
extends State<SceneRoomsTabBarDevicesView>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final TabController _tabController;
|
||||
List<SubSpaceModel>? rooms = [];
|
||||
@ -35,14 +37,15 @@ class _SceneRoomsTabBarDevicesViewState extends State<SceneRoomsTabBarDevicesVie
|
||||
0,
|
||||
SubSpaceModel(
|
||||
name: 'All Devices',
|
||||
devices: DevicesCubit.getInstance().allDevices,
|
||||
devices: context.read<DevicesCubit>().allDevices,
|
||||
id: '-1',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_tabController = TabController(length: rooms!.length, vsync: this, initialIndex: 0);
|
||||
_tabController =
|
||||
TabController(length: rooms!.length, vsync: this, initialIndex: 0);
|
||||
_tabController.addListener(_handleTabSwitched);
|
||||
super.initState();
|
||||
}
|
||||
@ -52,8 +55,10 @@ class _SceneRoomsTabBarDevicesViewState extends State<SceneRoomsTabBarDevicesVie
|
||||
final value = _tabController.index;
|
||||
|
||||
/// select tab
|
||||
context.read<TabBarBloc>().add(
|
||||
TabChanged(selectedIndex: value, roomId: rooms?[value].id ?? '', unit: selectedSpace));
|
||||
context.read<TabBarBloc>().add(TabChanged(
|
||||
selectedIndex: value,
|
||||
roomId: rooms?[value].id ?? '',
|
||||
unit: selectedSpace));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,34 +36,38 @@ class SceneItem extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultContainer(
|
||||
onTap: () {
|
||||
context.read<SmartSceneSelectBloc>().add(const SmartSceneClearEvent());
|
||||
if (disablePlayButton == false) {
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
|
||||
if (HomeCubit.manageScene) {
|
||||
context
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(const SmartSceneClearEvent());
|
||||
if (disablePlayButton == false) {
|
||||
BlocProvider.of<CreateSceneBloc>(context).add(
|
||||
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: false));
|
||||
|
||||
/// the state to set the scene type must be after the fetch
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
|
||||
} else {
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true));
|
||||
/// the state to set the scene type must be after the fetch
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.tabToRun));
|
||||
} else {
|
||||
BlocProvider.of<CreateSceneBloc>(context).add(
|
||||
FetchSceneTasksEvent(sceneId: scene.id, isAutomation: true));
|
||||
|
||||
/// the state to set the scene type must be after the fetch
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.deviceStatusChanges));
|
||||
/// the state to set the scene type must be after the fetch
|
||||
BlocProvider.of<CreateSceneBloc>(context)
|
||||
.add(const SceneTypeEvent(CreateSceneEnum.deviceStatusChanges));
|
||||
}
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.sceneTasksRoute,
|
||||
arguments: SceneSettingsRouteArguments(
|
||||
sceneType: disablePlayButton == false
|
||||
? CreateSceneEnum.tabToRun.name
|
||||
: CreateSceneEnum.deviceStatusChanges.name,
|
||||
sceneId: scene.id,
|
||||
sceneName: scene.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
Routes.sceneTasksRoute,
|
||||
arguments: SceneSettingsRouteArguments(
|
||||
sceneType: disablePlayButton == false
|
||||
? CreateSceneEnum.tabToRun.name
|
||||
: CreateSceneEnum.deviceStatusChanges.name,
|
||||
sceneId: scene.id,
|
||||
sceneName: scene.name,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -79,8 +83,11 @@ class SceneItem extends StatelessWidget {
|
||||
height: 32,
|
||||
width: 32,
|
||||
fit: BoxFit.fill,
|
||||
errorBuilder: (context, error, stackTrace) =>
|
||||
Image.asset(Assets.assetsIconsLogo, height: 32, width: 32, fit: BoxFit.fill),
|
||||
errorBuilder: (context, error, stackTrace) => Image.asset(
|
||||
Assets.assetsIconsLogo,
|
||||
height: 32,
|
||||
width: 32,
|
||||
fit: BoxFit.fill),
|
||||
),
|
||||
if (disablePlayButton || scene.iconInBytes.isEmpty)
|
||||
SvgPicture.asset(
|
||||
@ -93,7 +100,9 @@ class SceneItem extends StatelessWidget {
|
||||
? IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onPressed: () {
|
||||
context.read<SceneBloc>().add(SceneTrigger(scene.id, scene.name));
|
||||
context
|
||||
.read<SceneBloc>()
|
||||
.add(SceneTrigger(scene.id, scene.name));
|
||||
},
|
||||
icon: isLoading
|
||||
? const Center(
|
||||
@ -115,11 +124,15 @@ class SceneItem extends StatelessWidget {
|
||||
activeColor: ColorsManager.primaryColor,
|
||||
value: scene.status == 'enable' ? true : false,
|
||||
onChanged: (value) {
|
||||
context.read<SceneBloc>().add(UpdateAutomationStatus(
|
||||
automationStatusUpdate: AutomationStatusUpdate(
|
||||
isEnable: value,
|
||||
spaceUuid: HomeCubit.getInstance().selectedSpace!.id),
|
||||
automationId: scene.id));
|
||||
context.read<SceneBloc>().add(
|
||||
UpdateAutomationStatus(
|
||||
automationStatusUpdate:
|
||||
AutomationStatusUpdate(
|
||||
isEnable: value,
|
||||
spaceUuid: HomeCubit.getInstance()
|
||||
.selectedSpace!
|
||||
.id),
|
||||
automationId: scene.id));
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@ -16,7 +16,8 @@ class CreateUnitWidget extends StatelessWidget {
|
||||
TextEditingController textEditingController = TextEditingController();
|
||||
return BlocConsumer<HomeCubit, HomeState>(
|
||||
listener: (context, state) {
|
||||
if (state is ActivationError) {}
|
||||
// if (state is ActivationError) {
|
||||
// }
|
||||
},
|
||||
builder: (context, state) {
|
||||
return SingleChildScrollView(
|
||||
@ -96,11 +97,12 @@ class CreateUnitWidget extends StatelessWidget {
|
||||
textEditingController.text)) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Done successfully');
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Wrong code!');
|
||||
}
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 500),
|
||||
() {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_right_alt,
|
||||
@ -136,42 +138,42 @@ class CreateUnitWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// return SizedBox(
|
||||
// width: MediaQuery.sizeOf(context).width,
|
||||
// height: MediaQuery.sizeOf(context).height,
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.noUnitsIconDashboard,
|
||||
// width: 100,
|
||||
// height: 100,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 50,
|
||||
// ),
|
||||
// Flexible(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// Navigator.pushNamed(context, Routes.createUnit);
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 34, vertical: 14),
|
||||
// decoration: ShapeDecoration(
|
||||
// color: const Color(0x99023DFE),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(20),
|
||||
// ),
|
||||
// ),
|
||||
// child: const TitleMedium(
|
||||
// text: 'Create a unit',
|
||||
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.white),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// return SizedBox(
|
||||
// width: MediaQuery.sizeOf(context).width,
|
||||
// height: MediaQuery.sizeOf(context).height,
|
||||
// child: Column(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.noUnitsIconDashboard,
|
||||
// width: 100,
|
||||
// height: 100,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 50,
|
||||
// ),
|
||||
// Flexible(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// Navigator.pushNamed(context, Routes.createUnit);
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 34, vertical: 14),
|
||||
// decoration: ShapeDecoration(
|
||||
// color: const Color(0x99023DFE),
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(20),
|
||||
// ),
|
||||
// ),
|
||||
// child: const TitleMedium(
|
||||
// text: 'Create a unit',
|
||||
// style: TextStyle(fontSize: 16, fontWeight: FontWeight.w400, color: Colors.white),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -1,81 +1,146 @@
|
||||
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/devices_cubit.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/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
class CustomSwitch extends StatelessWidget {
|
||||
const CustomSwitch({super.key, required this.device});
|
||||
class CustomSwitch extends StatefulWidget {
|
||||
const CustomSwitch({Key? key, required this.device}) : super(key: key);
|
||||
|
||||
final DeviceModel device;
|
||||
|
||||
@override
|
||||
_CustomSwitchState createState() => _CustomSwitchState();
|
||||
}
|
||||
|
||||
bool isCurtainOpen(DeviceModel device) {
|
||||
// Find the status with code == 'percent_control'
|
||||
final curtainStatus = device.status.firstWhere(
|
||||
(s) => s.code == 'percent_control',
|
||||
// orElse: () => null,
|
||||
);
|
||||
|
||||
// We consider it "on/open" if percent_control == 100
|
||||
return curtainStatus.value >= 20;
|
||||
}
|
||||
|
||||
class _CustomSwitchState extends State<CustomSwitch> {
|
||||
bool _isLoading = false;
|
||||
|
||||
bool isDeviceOn(DeviceModel device) {
|
||||
// If it's a curtain, check percent_control
|
||||
if (device.type == "CUR") {
|
||||
return isCurtainOpen(device);
|
||||
}
|
||||
|
||||
// Otherwise, default to your existing switch logic
|
||||
final switchStatuses = device.status.where(
|
||||
(s) => (s.code?.startsWith('switch') ?? false),
|
||||
);
|
||||
final anySwitchFalse = switchStatuses.any((s) => s.value == false);
|
||||
return !anySwitchFalse;
|
||||
}
|
||||
|
||||
Widget _buildLoadingIndicator() {
|
||||
return const SizedBox(
|
||||
width: 45,
|
||||
height: 28,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 22,
|
||||
height: 22,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<DevicesCubit, DevicesState>(
|
||||
builder: (context, state) {
|
||||
bool? status;
|
||||
if (device.status.isNotEmpty) {
|
||||
status = device.status
|
||||
.firstWhereOrNull((status) => status.code == "switch")
|
||||
?.value;
|
||||
final isSwitch = widget.device.categoryName == 'Switch';
|
||||
if (!isSwitch &&
|
||||
widget.device.type != "AC" &&
|
||||
widget.device.type != "GD" &&
|
||||
widget.device.type != "CUR") {
|
||||
return const SizedBox();
|
||||
}
|
||||
final isOn = isDeviceOn(widget.device);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
final newValue = !isOn;
|
||||
String code;
|
||||
dynamic controlValue;
|
||||
|
||||
if (widget.device.type == "AC") {
|
||||
code = "switch";
|
||||
controlValue = newValue;
|
||||
} else if (widget.device.type == "CUR") {
|
||||
code = "percent_control";
|
||||
controlValue = newValue ? "close" : "open";
|
||||
} else {
|
||||
code = "switch_1";
|
||||
controlValue = newValue;
|
||||
}
|
||||
|
||||
final control = DeviceControlModel(
|
||||
code: code,
|
||||
value: controlValue,
|
||||
deviceId: widget.device.uuid!,
|
||||
);
|
||||
try {
|
||||
if (widget.device.type == "CUR") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.changeCurtainSwitch(control, widget.device.uuid!);
|
||||
} else if (widget.device.type == "1GT") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.oneGTGangToggle(control, widget.device.uuid!);
|
||||
} else if (widget.device.type == "2GT") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.towGTGangToggle(control, widget.device.uuid!);
|
||||
} else if (widget.device.type == "3G") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.threeGangToggle(control, widget.device.uuid!);
|
||||
} else if (widget.device.type == "2G") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.towGangToggle(control, widget.device.uuid!);
|
||||
} else if (widget.device.type == "1G") {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.oneGangToggle(control, widget.device.uuid!);
|
||||
} else {
|
||||
await context
|
||||
.read<DevicesCubit>()
|
||||
.deviceControl(control, widget.device.uuid!);
|
||||
}
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
return status == null
|
||||
? const SizedBox()
|
||||
: GestureDetector(
|
||||
onTap: () {
|
||||
DevicesCubit.getInstance().deviceControl(
|
||||
DeviceControlModel(
|
||||
deviceId: device.uuid,
|
||||
code: device.status
|
||||
.firstWhere((status) => status.code == "switch")
|
||||
.code,
|
||||
value: !device.status
|
||||
.firstWhere((status) => status.code == "switch")
|
||||
.value!,
|
||||
),
|
||||
device.uuid!,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 45.0,
|
||||
height: 28.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
color: status
|
||||
? ColorsManager.primaryColor
|
||||
: const Color(0xFFD9D9D9)),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 40.0,
|
||||
height: 23.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
alignment: status
|
||||
? Alignment.centerRight
|
||||
: Alignment.centerLeft,
|
||||
child: Container(
|
||||
width: 20.0,
|
||||
height: 20.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: status
|
||||
? ColorsManager.primaryColor
|
||||
: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _isLoading
|
||||
? _buildLoadingIndicator()
|
||||
: SizedBox(
|
||||
child: SvgPicture.asset(
|
||||
isOn ? Assets.toggleSwitchSmall : Assets.offToggleSwitchSmall,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1139,4 +1139,8 @@ class Assets {
|
||||
static const String office = 'assets/icons/office.svg';
|
||||
static const String parlour = 'assets/icons/parlour.svg';
|
||||
static const String grid = 'assets/images/grid.svg';
|
||||
|
||||
static const String toggleSwitchSmall = 'assets/icons/toggleSwitchSmall.svg';
|
||||
static const String offToggleSwitchSmall = 'assets/icons/offToggleSwitchSmall.svg';
|
||||
|
||||
}
|
||||
|
||||
@ -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/devices/bloc/devices_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/menu_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/profile_bloc/profile_bloc.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';
|
||||
@ -26,6 +28,9 @@ class MyApp extends StatelessWidget {
|
||||
Constants.bottomNavBarHeightPercentage;
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<DevicesCubit>(
|
||||
create: (context) => DevicesCubit.getInstance(),
|
||||
),
|
||||
BlocProvider(create: (context) => AuthCubit()),
|
||||
BlocProvider(
|
||||
create: (context) => EffectPeriodBloc(),
|
||||
@ -34,6 +39,7 @@ class MyApp extends StatelessWidget {
|
||||
BlocProvider(create: (context) => CreateSceneBloc()),
|
||||
BlocProvider(create: (context) => SceneBloc()),
|
||||
BlocProvider(create: (context) => ProfileBloc()),
|
||||
BlocProvider(create: (context) => MenuCubit()), //DevicesCubit
|
||||
],
|
||||
child: MaterialApp(
|
||||
navigatorKey: NavigationService.navigatorKey,
|
||||
|
||||
@ -53,7 +53,8 @@ abstract class ApiEndpoints {
|
||||
//POST
|
||||
static const String addUnit = '/unit';
|
||||
static const String addUnitToUser = '/unit/user';
|
||||
static const String verifyInvitationCode = '/user/{userUuid}/spaces/verify-code';
|
||||
static const String verifyInvitationCode =
|
||||
'/user/{userUuid}/spaces/verify-code';
|
||||
|
||||
//GET
|
||||
static const String unitByUuid = '/unit/';
|
||||
@ -63,8 +64,7 @@ abstract class ApiEndpoints {
|
||||
static const String unitUser = '/unit/user/';
|
||||
static const String invitationCode =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{unitUuid}/invitation-code';
|
||||
static const String activationCode =
|
||||
'/invite-user/activation';
|
||||
static const String activationCode = '/invite-user/activation';
|
||||
|
||||
//PUT
|
||||
static const String renameUnit = '/unit/{unitUuid}';
|
||||
@ -97,7 +97,8 @@ abstract class ApiEndpoints {
|
||||
static const String controlGroup = '/group/control';
|
||||
//GET
|
||||
static const String groupBySpace = '/group/{unitUuid}';
|
||||
static const String devicesByGroupName = '/group/{unitUuid}/devices/{groupName}';
|
||||
static const String devicesByGroupName =
|
||||
'/group/{unitUuid}/devices/{groupName}';
|
||||
|
||||
static const String groupByUuid = '/group/{groupUuid}';
|
||||
//DELETE
|
||||
@ -109,7 +110,8 @@ abstract class ApiEndpoints {
|
||||
static const String addDeviceToRoom = '/device/room';
|
||||
static const String addDeviceToGroup = '/device/group';
|
||||
static const String controlDevice = '/device/{deviceUuid}/control';
|
||||
static const String firmwareDevice = '/device/{deviceUuid}/firmware/{firmwareVersion}';
|
||||
static const String firmwareDevice =
|
||||
'/device/{deviceUuid}/firmware/{firmwareVersion}';
|
||||
static const String getDevicesByUserId = '/device/user/{userId}';
|
||||
static const String getDevicesByUnitId = '/device/unit/{unitUuid}';
|
||||
static const String openDoorLock = '/door-lock/open/{doorLockUuid}';
|
||||
@ -120,8 +122,10 @@ abstract class ApiEndpoints {
|
||||
static const String deviceByUuid = '/device/{deviceUuid}';
|
||||
static const String deviceFunctions = '/device/{deviceUuid}/functions';
|
||||
static const String gatewayApi = '/device/gateway/{gatewayUuid}/devices';
|
||||
static const String deviceFunctionsStatus = '/device/{deviceUuid}/functions/status';
|
||||
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status';
|
||||
static const String deviceFunctionsStatus =
|
||||
'/device/{deviceUuid}/functions/status';
|
||||
static const String powerClamp =
|
||||
'/device/{powerClampUuid}/power-clamp/status';
|
||||
|
||||
///Device Permission Module
|
||||
//POST
|
||||
@ -149,14 +153,16 @@ abstract class ApiEndpoints {
|
||||
|
||||
static const String getUnitAutomation = '/automation/{unitUuid}';
|
||||
|
||||
static const String getAutomationDetails = '/automation/details/{automationId}';
|
||||
static const String getAutomationDetails =
|
||||
'/automation/details/{automationId}';
|
||||
|
||||
/// PUT
|
||||
static const String updateScene = '/scene/tap-to-run/{sceneId}';
|
||||
|
||||
static const String updateAutomation = '/automation/{automationId}';
|
||||
|
||||
static const String updateAutomationStatus = '/automation/status/{automationId}';
|
||||
static const String updateAutomationStatus =
|
||||
'/automation/status/{automationId}';
|
||||
|
||||
/// DELETE
|
||||
static const String deleteScene = '/scene/tap-to-run/{sceneId}';
|
||||
@ -165,8 +171,10 @@ abstract class ApiEndpoints {
|
||||
|
||||
//////////////////////Door Lock //////////////////////
|
||||
//online
|
||||
static const String addTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}';
|
||||
static const String getTemporaryPassword = '/door-lock/temporary-password/online/{doorLockUuid}';
|
||||
static const String addTemporaryPassword =
|
||||
'/door-lock/temporary-password/online/{doorLockUuid}';
|
||||
static const String getTemporaryPassword =
|
||||
'/door-lock/temporary-password/online/{doorLockUuid}';
|
||||
|
||||
//one-time offline
|
||||
static const String addOneTimeTemporaryPassword =
|
||||
@ -198,7 +206,8 @@ abstract class ApiEndpoints {
|
||||
'/door-lock/temporary-password/online/{doorLockUuid}/{passwordId}';
|
||||
|
||||
static const String saveSchedule = '/schedule/{deviceUuid}';
|
||||
static const String getSchedule = '/schedule/{deviceUuid}?category={category}';
|
||||
static const String getSchedule =
|
||||
'/schedule/{deviceUuid}?category={category}';
|
||||
static const String changeSchedule = '/schedule/enable/{deviceUuid}';
|
||||
static const String deleteSchedule = '/schedule/{deviceUuid}/{scheduleId}';
|
||||
static const String reportLogs =
|
||||
@ -207,8 +216,16 @@ abstract class ApiEndpoints {
|
||||
static const String statusBatch = '/device/status/batch';
|
||||
static const String deviceScene = '/device/{deviceUuid}/scenes';
|
||||
|
||||
static const String fourSceneByName = '/device/{deviceUuid}/scenes?switchName={switchName}';
|
||||
static const String fourSceneByName =
|
||||
'/device/{deviceUuid}/scenes?switchName={switchName}';
|
||||
|
||||
static const String resetDevice = '/factory/reset/{deviceUuid}';
|
||||
static const String unAssignScenesDevice = '/device/{deviceUuid}/scenes?switchName={switchName}';
|
||||
static const String unAssignScenesDevice =
|
||||
'/device/{deviceUuid}/scenes?switchName={switchName}';
|
||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||
static const String terms = '/terms';
|
||||
static const String policy = '/policy';
|
||||
static const String getPermission = '/permission/{roleUuid}';
|
||||
static const String getAllDevices =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
|
||||
}
|
||||
|
||||
@ -51,11 +51,14 @@ class DevicesAPI {
|
||||
static Future<Map<String, dynamic>> controlDevice(
|
||||
DeviceControlModel controlModel, String deviceId) async {
|
||||
try {
|
||||
print('object-*/-*/-*/${controlModel.toJson()}');
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.controlDevice.replaceAll('{deviceUuid}', deviceId),
|
||||
body: controlModel.toJson(),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
print('object-*/-*/-*/${json}');
|
||||
|
||||
return json;
|
||||
},
|
||||
);
|
||||
@ -545,4 +548,61 @@ class DevicesAPI {
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future<DeviceReport> getDeviceReports(
|
||||
String uuid,
|
||||
String code,
|
||||
) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getDeviceLogs
|
||||
.replaceAll('{uuid}', uuid)
|
||||
.replaceAll('{code}', code),
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
return DeviceReport.fromJson(json);
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future<List<DeviceModel>> getAllDevices({
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
}) async {
|
||||
print('communityUuid=$communityUuid');
|
||||
print('spaceUuid=$spaceUuid');
|
||||
print('projectUuid=${TempConst.projectId}');
|
||||
|
||||
try {
|
||||
final String path = ApiEndpoints.getAllDevices
|
||||
.replaceAll('{communityUuid}', communityUuid)
|
||||
.replaceAll('{spaceUuid}', spaceUuid)
|
||||
.replaceAll('{projectUuid}', TempConst.projectId);
|
||||
|
||||
final response = await _httpService.get(
|
||||
path: path,
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
print('response-*/-*/$json');
|
||||
final data = json['data'];
|
||||
|
||||
if (data == null || data.isEmpty) {
|
||||
return <DeviceModel>[];
|
||||
}
|
||||
if (json == null || json.isEmpty || json == []) {
|
||||
return <DeviceModel>[];
|
||||
}
|
||||
return data
|
||||
.map<DeviceModel>((device) => DeviceModel.fromJson(device))
|
||||
.toList();
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
// Log the error if needed
|
||||
// Return an empty list in case of error
|
||||
return <DeviceModel>[];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/auth/model/user_model.dart';
|
||||
import 'package:syncrow_app/features/menu/model/region_model.dart';
|
||||
@ -95,6 +94,16 @@ class ProfileApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future fetchPermissions(roleId) async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getPermission.replaceAll('{roleUuid}', roleId!),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future<List<RegionModel>> fetchRegion() async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getRegion,
|
||||
@ -116,4 +125,24 @@ class ProfileApi {
|
||||
});
|
||||
return response as List<TimeZone>;
|
||||
}
|
||||
|
||||
Future fetchUserAgreement() async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.terms,
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'];
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
Future fetchPrivacyPolicy() async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.policy,
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['data'];
|
||||
});
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
//ignore_for_file: constant_identifier_names
|
||||
import 'dart:ui';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/devices/model/function_model.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/privacy_policy.dart';
|
||||
import 'package:syncrow_app/features/menu/bloc/user_agreement.dart';
|
||||
// import 'package:syncrow_app/features/menu/view/widgets/create_home/create_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/manage_home/manage_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/privacy/privacy_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/securty_view.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
@ -99,7 +101,9 @@ Map<String, DeviceType> devicesTypesMap = {
|
||||
Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
DeviceType.AC: [
|
||||
FunctionModel(
|
||||
code: 'switch', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'mode',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -122,7 +126,9 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
// "range": ["low", "middle", "high", "auto"]
|
||||
})),
|
||||
FunctionModel(
|
||||
code: 'child_lock', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'child_lock',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
],
|
||||
DeviceType.Gateway: [
|
||||
FunctionModel(
|
||||
@ -136,7 +142,9 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
"range": ["normal", "alarm"]
|
||||
})),
|
||||
FunctionModel(
|
||||
code: 'factory_reset', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'factory_reset',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'alarm_active',
|
||||
type: functionTypesMap['String'],
|
||||
@ -146,7 +154,8 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
FunctionModel(
|
||||
code: 'sensitivity',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "", "min": 1, "max": 10, "scale": 0, "step": 1})),
|
||||
],
|
||||
DeviceType.DoorLock: [
|
||||
FunctionModel(
|
||||
@ -154,7 +163,9 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'remote_no_dp_key', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})),
|
||||
code: 'remote_no_dp_key',
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'normal_open_switch',
|
||||
type: functionTypesMap['Boolean'],
|
||||
@ -164,64 +175,87 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
FunctionModel(
|
||||
code: 'far_detection',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "cm", "min": 75, "max": 600, "scale": 0, "step": 75})),
|
||||
FunctionModel(
|
||||
code: 'presence_time',
|
||||
type: functionTypesMap['Integer'],
|
||||
values:
|
||||
ValueModel.fromJson({"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "Min", "min": 0, "max": 65535, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'motion_sensitivity_value',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'motionless_sensitivity',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "", "min": 1, "max": 5, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'indicator', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'indicator',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
],
|
||||
DeviceType.OneGang: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
],
|
||||
DeviceType.TwoGang: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_2',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_2',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
],
|
||||
DeviceType.ThreeGang: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_2',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_3', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_3',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_2',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_3',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
],
|
||||
DeviceType.Curtain: [
|
||||
FunctionModel(
|
||||
@ -233,15 +267,19 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
FunctionModel(
|
||||
code: 'percent_control',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "%", "min": 0, "max": 100, "scale": 0, "step": 1})),
|
||||
],
|
||||
DeviceType.WH: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'relay_status',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -265,7 +303,9 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
],
|
||||
DeviceType.DS: [
|
||||
FunctionModel(
|
||||
code: 'doorcontact_state', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})),
|
||||
code: 'doorcontact_state',
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'battery_percentage',
|
||||
type: functionTypesMap['Integer'],
|
||||
@ -273,11 +313,14 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
],
|
||||
DeviceType.OneTouch: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'relay_status',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -299,17 +342,23 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
],
|
||||
DeviceType.TowTouch: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_2',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_2',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'relay_status',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -337,23 +386,32 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
],
|
||||
DeviceType.ThreeTouch: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_2', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_2',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_3', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_3',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_2',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_3',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 43200, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'relay_status',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -387,19 +445,24 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
],
|
||||
DeviceType.GarageDoor: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'tr_timecon',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_alarm',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'door_control_1',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -420,19 +483,24 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
DeviceType.WaterLeak: [],
|
||||
DeviceType.PC: [
|
||||
FunctionModel(
|
||||
code: 'switch_1', type: functionTypesMap['Boolean'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_1',
|
||||
type: functionTypesMap['Boolean'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'countdown_1',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'tr_timecon',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 120, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'countdown_alarm',
|
||||
type: functionTypesMap['Integer'],
|
||||
values: ValueModel.fromJson({"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
values: ValueModel.fromJson(
|
||||
{"unit": "s", "min": 0, "max": 86400, "scale": 0, "step": 1})),
|
||||
FunctionModel(
|
||||
code: 'door_control_1',
|
||||
type: functionTypesMap['Enum'],
|
||||
@ -476,9 +544,13 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
"range": ["scene"]
|
||||
})),
|
||||
FunctionModel(
|
||||
code: 'scene_id_group_id', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})),
|
||||
code: 'scene_id_group_id',
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_backlight', type: functionTypesMap['Enum'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_backlight',
|
||||
type: functionTypesMap['Enum'],
|
||||
values: ValueModel.fromJson({})),
|
||||
],
|
||||
DeviceType.SixScene: [
|
||||
FunctionModel(
|
||||
@ -518,13 +590,19 @@ Map<DeviceType, List<FunctionModel>> devicesFunctionsMap = {
|
||||
"range": ["scene"]
|
||||
})),
|
||||
FunctionModel(
|
||||
code: 'scene_id_group_id', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})),
|
||||
code: 'scene_id_group_id',
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'switch_backlight', type: functionTypesMap['Enum'], values: ValueModel.fromJson({})),
|
||||
code: 'switch_backlight',
|
||||
type: functionTypesMap['Enum'],
|
||||
values: ValueModel.fromJson({})),
|
||||
],
|
||||
DeviceType.SOS: [
|
||||
FunctionModel(
|
||||
code: 'contact_state', type: functionTypesMap['Raw'], values: ValueModel.fromJson({})),
|
||||
code: 'contact_state',
|
||||
type: functionTypesMap['Raw'],
|
||||
values: ValueModel.fromJson({})),
|
||||
FunctionModel(
|
||||
code: 'battery_percentage',
|
||||
type: functionTypesMap['Integer'],
|
||||
@ -600,130 +678,6 @@ K? getNextItem<K, V>(Map<K, V> map, V value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> menuSections = [
|
||||
//Home Management
|
||||
{
|
||||
'title': 'Home Management',
|
||||
'color': ColorsManager.primaryColor,
|
||||
'buttons': [
|
||||
// {
|
||||
// 'title': 'Create a Unit',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsCreateHome,
|
||||
// 'page': const CreateUnitView()
|
||||
// },
|
||||
{
|
||||
'title': 'Join a Unit',
|
||||
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsJoinAHome,
|
||||
'page': const JoinHomeView()
|
||||
},
|
||||
{
|
||||
'title': 'Manage Your Units',
|
||||
'Icon': Assets.assetsIconsMenuIconsHomeManagementIconsManageYourHome,
|
||||
'page': const ManageHomeView()
|
||||
},
|
||||
],
|
||||
},
|
||||
//General Settings
|
||||
// {
|
||||
// 'title': 'General Settings',
|
||||
// 'color': const Color(0xFF023DFE),
|
||||
// 'buttons': [
|
||||
// {
|
||||
// 'title': 'Voice Assistant',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsVoiceAssistant,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Temperature unit',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTemperatureUnit,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Touch tone on panel',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsTouchTone,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Language',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsLanguage,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Network Diagnosis',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsNetworkDiagnosis,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Clear Cache',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsGeneralSettingsIconsClearCach,
|
||||
// 'page': null
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// //Messages Center
|
||||
// {
|
||||
// 'title': 'Messages Center',
|
||||
// 'color': const Color(0xFF0088FF),
|
||||
// 'buttons': [
|
||||
// {
|
||||
// 'title': 'Alerts',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsAlerts,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Messages',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsMessages,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'FAQs',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsFAQs,
|
||||
// 'page': null
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Help & Feedback',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsMessagesCenterIconsHelpAndFeedback,
|
||||
// 'page': null
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
//Security And Privacy
|
||||
{
|
||||
'title': 'Security And Privacy',
|
||||
'color': const Color(0xFF8AB9FF),
|
||||
'buttons': [
|
||||
{
|
||||
'title': 'Security',
|
||||
'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsSecurty,
|
||||
'page': const SecurtyView()
|
||||
},
|
||||
// {
|
||||
// 'title': 'Privacy',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsSecurityAndPrivacyIconsPrivacy,
|
||||
// 'page': const PrivacyView()
|
||||
// },
|
||||
],
|
||||
},
|
||||
//Legal Information
|
||||
{
|
||||
'title': 'Legal Information',
|
||||
'color': const Color(0xFF001B72),
|
||||
'buttons': [
|
||||
{'title': 'About', 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsAbout, 'page': null},
|
||||
// {
|
||||
// 'title': 'Privacy Policy',
|
||||
// 'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsPrivacyPolicy,
|
||||
// 'page': null
|
||||
// },
|
||||
{
|
||||
'title': 'User Agreement',
|
||||
'Icon': Assets.assetsIconsMenuIconsLeagalInfoIconsUserAgreement,
|
||||
'page': null
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
enum MemberRole {
|
||||
FamilyMember,
|
||||
OtherMember,
|
||||
|
||||
224
pubspec.lock
224
pubspec.lock
@ -25,6 +25,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
audio_session:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audio_session
|
||||
sha256: b2a26ba8b7efa1790d6460e82971fde3e398cfbe2295df9dea22f3499d2c12a7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.23"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -73,6 +81,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
chewie:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: chewie
|
||||
sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.7.5"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -129,6 +145,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.10"
|
||||
device_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -440,6 +464,70 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_widget_from_html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_widget_from_html
|
||||
sha256: f3967a5b42896662efdd420b5adaf8a7d3692b0f44462a07c80e3b4c173b1a02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.3"
|
||||
flutter_widget_from_html_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_widget_from_html_core
|
||||
sha256: b1048fd119a14762e2361bd057da608148a895477846d6149109b2151d2f7abf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.2"
|
||||
fwfh_cached_network_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_cached_network_image
|
||||
sha256: "8e44226801bfba27930673953afce8af44da7e92573be93f60385d9865a089dd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.3"
|
||||
fwfh_chewie:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_chewie
|
||||
sha256: "37bde9cedfb6dc5546176f7f0c56af1e814966cb33ec58f16c9565ed93ccb704"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.8"
|
||||
fwfh_just_audio:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_just_audio
|
||||
sha256: "38dc2c55803bd3cef33042c473e0c40b891ad4548078424641a32032f6a1245f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.2"
|
||||
fwfh_svg:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_svg
|
||||
sha256: "550b1014d12b5528d8bdb6e3b44b58721f3fb1f65d7a852d1623a817008bdfc4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.3"
|
||||
fwfh_url_launcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_url_launcher
|
||||
sha256: b9f5d55a5ae2c2c07243ba33f7ba49ac9544bdb2f4c16d8139df9ccbebe3449c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.1"
|
||||
fwfh_webview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fwfh_webview
|
||||
sha256: c0a8b664b642f40f4c252a0ab4e72c22dcd97c7fb3a7e50a6b4bdb6f63afca19
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.3"
|
||||
get_it:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -552,6 +640,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
just_audio:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio
|
||||
sha256: "1a1eb86e7d81e69a1d36943f2b3efd62dece3dad2cafd9ec2e62e6db7c04d9b7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.43"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.13"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -584,6 +696,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -640,6 +760,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: cb44f49b6e690fa766f023d5b22cac6b9affe741dd792b6ac7ad4fabe0d7b097
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1101,6 +1237,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
video_player:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player
|
||||
sha256: "4a8c3492d734f7c39c2588a3206707a05ee80cef52e8c7f3b2078d430c84bc17"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.2"
|
||||
video_player_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_android
|
||||
sha256: e343701aa890b74a863fa460f5c0e628127ed06a975d7d9af6b697133fb25bdf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.1"
|
||||
video_player_avfoundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_avfoundation
|
||||
sha256: "8a4e73a3faf2b13512978a43cf1cdda66feeeb900a0527f1fbfd7b19cf3458d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.7"
|
||||
video_player_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_platform_interface
|
||||
sha256: "229d7642ccd9f3dc4aba169609dd6b5f3f443bb4cc15b82f7785fcada5af9bbb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.3"
|
||||
video_player_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: video_player_web
|
||||
sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1109,6 +1285,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
wakelock_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: "104d94837bb28c735894dcd592877e990149c380e6358b00c04398ca1426eed4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus_platform_interface
|
||||
sha256: "70e780bc99796e1db82fe764b1e7dcb89a86f1e5b3afb1db354de50f2e41eb7a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1117,6 +1309,38 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
webview_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: ed021f27ae621bc97a6019fb601ab16331a3db4bf8afa305e9f6689bdb3edced
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.16.8"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "9c62cc46fa4f2d41e10ab81014c1de470a6c6f26051a2de32111b2ee55287feb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.14.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
version: 1.0.9+45
|
||||
version: 1.0.18+55
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.6 <4.0.0"
|
||||
@ -47,6 +47,7 @@ dependencies:
|
||||
fl_chart: ^0.69.0
|
||||
firebase_database: ^10.5.7
|
||||
percent_indicator: ^4.2.3
|
||||
flutter_html: ^3.0.0-beta.2
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
Reference in New Issue
Block a user